#!/usr/bin/env python # coding: utf-8 # # Biolink Metamodel Test Notebook # In[20]: get_ipython().system('pip install yamlmagic') get_ipython().run_line_magic('reload_ext', 'yamlmagic') # In[21]: from IPython.core.display import display, HTML from types import ModuleType from json import loads, dumps from jsonasobj import JsonObj, as_json_object from rdflib import Graph from biolinkml.meta import SchemaDefinition from biolinkml.utils.schemaloader import SchemaLoader from biolinkml.utils.yamlutils import DupCheckYamlLoader, as_json_object as yaml_to_json from biolinkml.generators.shexgen import ShExGenerator from biolinkml.generators.pythongen import PythonGenerator from biolinkml.generators.yumlgen import YumlGenerator from biolinkml.generators.jsonldcontextgen import ContextGenerator # ## Basic model structure # A biolink model consists of: # * a name # * a uri # * type definitions # * slot definitions # * class definitions # * subset definitions # # As an example, the model below defines: # In[22]: get_ipython().run_cell_magic('yaml', '--loader DupCheckYamlLoader yaml', 'id: http://example.org/sample/example1\nname: synopsis2\nprefixes:\n foaf: http://xmlns.com/foaf/0.1/\n samp: http://example.org/model/\n xsd: http://www.w3.org/2001/XMLSchema#\n \ndefault_prefix: samp\n\ndefault_curi_maps:\n - semweb_context\n \ndefault_range: string\n\ntypes:\n string:\n base: str\n uri: xsd:string\n int:\n base: int\n uri: xsd:integer\n boolean:\n base: Bool\n uri: xsd:boolean\n \n\nclasses:\n person:\n description: A person, living or dead\n slots:\n - id\n - first name\n - last name\n - age\n - living\n - knows\n \n friendly_person:\n description: Any person that knows someone\n is_a: person\n slot_usage:\n knows:\n required: True\n\nslots:\n id:\n description: Unique identifier of a person\n identifier: true\n\n first name:\n description: The first name of a person\n slot_uri: foaf:firstName\n multivalued: true\n \n last name:\n description: The last name of a person\n slot_uri: foaf:lastName\n required: true\n \n living:\n description: Whether the person is alive\n range: boolean\n comments:\n - unspecified means unknown\n \n age:\n description: The age of a person if living or age of death if not\n range: int\n slot_uri: foaf:age\n \n knows:\n description: A person known by this person (indicating some level of reciprocated interaction between the parties).\n range: person\n slot_uri: foaf:knows\n multivalued: true\n') # ### We can emit this model as a Python class # In[23]: print(PythonGenerator(yaml).serialize()) # ### Compile the python into a module # In[24]: spec = compile(PythonGenerator(yaml).serialize(), 'test', 'exec') module = ModuleType('test') exec(spec, module.__dict__) # ### We can emit a UML rendering of this model # In[25]: display(HTML(f'')) # ### We can emit a JSON-LD context for the model: # In[26]: cntxt = loads(ContextGenerator(yaml).serialize(base="http://example.org/people/")) print(dumps(cntxt, indent=" ")) # ### The python model can be used to create classes # In[27]: # Generate a person joe_smith = module.Person(id="42", last_name="smith", first_name=['Joe', 'Bob'], age=43) print(joe_smith) # ### and can be combined w/ the JSON-LD Context to generate RDF # In[28]: # Add the context and turn it into RDF jsonld = as_json_object(yaml_to_json(joe_smith, cntxt)) print(jsonld) g = Graph() g.parse(data=jsonld, format="json-ld") print(g.serialize(format="turtle").decode()) # ### The model can be turned into ShEx # In[29]: shex = ShExGenerator(yaml).serialize(collections=False) print(shex) # ### The ShEx can then be used to validate RDF # In[30]: from pyshex.evaluate import evaluate r = evaluate(g, shex, start="http://example.org/model/Person", focus="http://example.org/people/42") print("Conforms" if r[0] else r[1]) # In[31]: r = evaluate(g, shex, start="http://example.org/model/FriendlyPerson", focus="http://example.org/people/42") print("Conforms" if r[0] else r[1]) # In[ ]: