<jupyter_start><jupyter_text>Biolink Metamodel Test Notebook<jupyter_code>!pip install yamlmagic
%reload_ext yamlmagic
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<jupyter_output><empty_output><jupyter_text>Basic model structureA biolink model consists of:* a name* a uri* type definitions* slot definitions* class definitions* subset definitionsAs an example, the model below defines:<jupyter_code>%%yaml --loader DupCheckYamlLoader yaml
id: http://example.org/sample/example1
name: synopsis2
prefixes:
    foaf: http://xmlns.com/foaf/0.1/
    samp: http://example.org/model/
    xsd: http://www.w3.org/2001/XMLSchema#
    
default_prefix: samp

default_curi_maps:
    - semweb_context
    
default_range: string

types:
    string:
        base: str
        uri: xsd:string
    int:
        base: int
        uri: xsd:integer
    boolean:
        base: Bool
        uri: xsd:boolean
        

classes:
    person:
        description: A person, living or dead
        slots:
            - id
            - first name
            - last name
            - age
            - living
            - knows
                  
    friendly_person:
        description: Any person that knows someone
        is_a: person
        slot_usage:
            knows:
                required: True

slots:
    id:
        description: Unique identifier of a person
        identifier: true

    first name:
        description: The first name of a person
        slot_uri: foaf:firstName
        multivalued: true
        
    last name:
        description: The last name of a person
        slot_uri: foaf:lastName
        required: true
        
    living:
        description: Whether the person is alive
        range: boolean
        comments:
            - unspecified means unknown
        
    age:
        description: The age of a person if living or age of death if not
        range: int
        slot_uri: foaf:age
        
    knows:
        description: A person known by this person (indicating some level of reciprocated interaction between the parties).
        range: person
        slot_uri: foaf:knows
        multivalued: true<jupyter_output><empty_output><jupyter_text>We can emit this model as a Python class<jupyter_code>print(PythonGenerator(yaml).serialize())<jupyter_output># Auto generated from None by pythongen.py version: 0.2.0
# Generation date: 2019-04-09 15:50
# Schema: synopsis2
#
# id: http://example.org/sample/example1
# description:
# license:

from typing import Optional, List, Union, Dict, ClassVar
from dataclasses import dataclass
from biolinkml.utils.metamodelcore import empty_list, empty_dict
from biolinkml.utils.yamlutils import YAMLRoot
from biolinkml.utils.metamodelcore import Bool

metamodel_version = "1.3.2"

# Types
class String(str):
    pass


class Int(int):
    pass


class Boolean(Bool):
    pass


# Class references
class PersonId(str):
    pass


class FriendlyPersonId(PersonId):
    pass


@dataclass
class Person(YAMLRoot):
    """
    A person, living or dead
    """
    _inherited_slots: ClassVar[List[str]] = []

    # === person ===
    id: Union[str, PersonId]
    last_name: str
    first_name: List[str] = empty_list()
    age: Optional[int] = None
    living: Optional[Bool] = None
    knows: List[Union[str, PersonId]] = e[...]<jupyter_text>Compile the python into a module<jupyter_code>spec = compile(PythonGenerator(yaml).serialize(), 'test', 'exec')
module = ModuleType('test')
exec(spec, module.__dict__)<jupyter_output><empty_output><jupyter_text>We can emit a UML rendering of  this model<jupyter_code>display(HTML(f'<img src="{YumlGenerator(yaml).serialize()}"/>'))<jupyter_output><empty_output><jupyter_text>We can emit a JSON-LD context for the model:<jupyter_code>cntxt = loads(ContextGenerator(yaml).serialize(base="http://example.org/people/"))
print(dumps(cntxt, indent="  "))<jupyter_output>{
  "_comments": "Auto generated from None by jsonldcontextgen.py version: 0.0.2\nGeneration date: 2019-04-09 15:50\nSchema: synopsis2\n\nid: http://example.org/sample/example1\ndescription: \nlicense: \n",
  "@context": {
    "_comments": null,
    "type": "@type",
    "foaf": "http://xmlns.com/foaf/0.1/",
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "@vocab": "http://example.org/model/",
    "age": {
      "@type": "xsd:integer",
      "@id": "foaf:age"
    },
    "first_name": {
      "@id": "foaf:firstName"
    },
    "id": "@id",
    "knows": {
      "@type": "@id",
      "@id": "foaf:knows"
    },
    "last_name": {
      "@id": "foaf:lastName"
    },
    "living": {
      "@type": "xsd:boolean"
    },
    "@base": "http://example.org/people/"
  }
}<jupyter_text>The python model can be used to create classes<jupyter_code># Generate a person
joe_smith = module.Person(id="42", last_name="smith", first_name=['Joe', 'Bob'], age=43)
print(joe_smith)<jupyter_output>Person(id='42', last_name='smith', first_name=['Joe', 'Bob'], age=43, living=None, knows=[])<jupyter_text>and can be combined w/ the JSON-LD Context to generate RDF<jupyter_code># 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())<jupyter_output>{
   "id": "42",
   "last_name": "smith",
   "first_name": [
      "Joe",
      "Bob"
   ],
   "age": 43,
   "living": null,
   "knows": [],
   "type": "Person",
   "_comments": "Auto generated from None by jsonldcontextgen.py version: 0.0.2\nGeneration date: 2019-04-09 15:50\nSchema: synopsis2\n\nid: http://example.org/sample/example1\ndescription: \nlicense: \n",
   "@context": {
      "_comments": null,
      "type": "@type",
      "foaf": "http://xmlns.com/foaf/0.1/",
      "xsd": "http://www.w3.org/2001/XMLSchema#",
      "@vocab": "http://example.org/model/",
      "age": {
         "@type": "xsd:integer",
         "@id": "foaf:age"
      },
      "first_name": {
         "@id": "foaf:firstName"
      },
      "id": "@id",
      "knows": {
         "@type": "@id",
         "@id": "foaf:knows"
      },
      "last_name": {
         "@id": "foaf:lastName"
      },
      "living": {
         "@type": "xsd:boolean"
      },
      "@base": "http://example.org/people/"
   }
}
@prefix :[...]<jupyter_text>The model can be turned into ShEx<jupyter_code>shex = ShExGenerator(yaml).serialize(collections=False)
print(shex)<jupyter_output>BASE <http://example.org/model/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>


<String> xsd:string

<Int> xsd:integer

<Boolean> xsd:boolean

<FriendlyPerson> EXTRA rdf:type CLOSED {
    (  $<FriendlyPerson_t> (  <knows> @<Person> + ;
          &<Person_t>
       ) ;
       rdf:type [ <FriendlyPerson> ]
    )
}

<Person> EXTRA rdf:type CLOSED {
    (  $<Person_t> (  foaf:firstName @<String> * ;
          foaf:lastName @<String> ;
          foaf:age @<Int> ? ;
          <living> @<Boolean> ? ;
          foaf:knows @<Person> *
       ) ;
       rdf:type [ <Person> ]
    )
}<jupyter_text>The ShEx can then be used to validate RDF<jupyter_code>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])
r = evaluate(g, shex, 
             start="http://example.org/model/FriendlyPerson", 
             focus="http://example.org/people/42")
print("Conforms" if r[0] else r[1])<jupyter_output>Testing <http://example.org/people/42> against shape http://example.org/model/FriendlyPerson
    Node: :Person not in value set:
	 {"values": ["http://example.org/model/FriendlyPerson"], "typ...
  Testing <http://example.org/people/42> against shape http://example.org/model/FriendlyPerson
    Node: :Person not in value set:
	 {"values": ["http://example.org/model/FriendlyPerson"], "typ...
  Testing <http://example.org/people/42> against shape http://example.org/model/FriendlyPerson
       No matching triples found for predicate rdf:type
{'fraction_non_alphanumeric': 0.13913948646773075, 'fraction_numerical': 0.013763590099467963, 'mean_word_length': 3.1176190476190477, 'pattern_counts': {'":': 51, '<': 61, '<?xml version=': 0, '>': 61, 'https://': 0, 'lorem ipsum': 0, 'www.': 5, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '376715', 'license': 'CC0-1.0', 'n_tokens_mistral': 3023, 'n_tokens_neox': 2920, 'n_words': 666, 'path': 'notebooks/examples.ipynb', 'repo_name': 'STARInformatics/biolinkml', 'size': '8646'}