{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Biolink Metamodel Test Notebook"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:19.348629Z",
     "iopub.status.busy": "2021-04-02T22:12:19.347893Z",
     "iopub.status.idle": "2021-04-02T22:12:21.068424Z",
     "shell.execute_reply": "2021-04-02T22:12:21.068833Z"
    }
   },
   "outputs": [],
   "source": [
    "!pip -q --disable-pip-version-check install yamlmagic\n",
    "%reload_ext yamlmagic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.074327Z",
     "iopub.status.busy": "2021-04-02T22:12:21.073079Z",
     "iopub.status.idle": "2021-04-02T22:12:21.668809Z",
     "shell.execute_reply": "2021-04-02T22:12:21.669211Z"
    }
   },
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'linkml.dumpers'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-1-3ba7d22fbdf6>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mlinkml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgenerators\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0myumlgen\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mYumlGenerator\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mlinkml_runtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0myamlutils\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mDupCheckYamlLoader\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mlinkml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumpers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson_dumper\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mdumps\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'linkml.dumpers'"
     ]
    }
   ],
   "source": [
    "from IPython.core.display import display, HTML\n",
    "from types import ModuleType\n",
    "\n",
    "from jsonasobj import as_json, loads\n",
    "from rdflib import Graph\n",
    "\n",
    "from linkml.generators.jsonldcontextgen import ContextGenerator\n",
    "from linkml.generators.pythongen import PythonGenerator\n",
    "from linkml.generators.shexgen import ShExGenerator\n",
    "from linkml.generators.yumlgen import YumlGenerator\n",
    "from linkml_runtime.utils.yamlutils import DupCheckYamlLoader\n",
    "from linkml.dumpers.json_dumper import dumps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Basic model structure\n",
    "A biolink model consists of:\n",
    "* a name\n",
    "* a uri\n",
    "* type definitions\n",
    "* slot definitions\n",
    "* class definitions\n",
    "* subset definitions\n",
    "\n",
    "As an example, the model below defines:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.678874Z",
     "iopub.status.busy": "2021-04-02T22:12:21.678289Z",
     "iopub.status.idle": "2021-04-02T22:12:21.689131Z",
     "shell.execute_reply": "2021-04-02T22:12:21.690011Z"
    }
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "            require(\n",
       "                [\n",
       "                    \"notebook/js/codecell\",\n",
       "                    \"codemirror/mode/yaml/yaml\"\n",
       "                ],\n",
       "                function(cc){\n",
       "                    cc.CodeCell.options_default.highlight_modes.magic_yaml = {\n",
       "                        reg: [\"^%%yaml\"]\n",
       "                    }\n",
       "                }\n",
       "            );\n",
       "            "
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%yaml --loader DupCheckYamlLoader yaml\n",
    "id: http://example.org/sample/example1\n",
    "name: synopsis2\n",
    "prefixes:\n",
    "    foaf: http://xmlns.com/foaf/0.1/\n",
    "    samp: http://example.org/model/\n",
    "    xsd: http://www.w3.org/2001/XMLSchema#\n",
    "\n",
    "default_prefix: samp\n",
    "\n",
    "default_curi_maps:\n",
    "    - semweb_context\n",
    "\n",
    "default_range: string\n",
    "\n",
    "types:\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",
    "\n",
    "classes:\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",
    "\n",
    "slots:\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"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### We can emit this model as a Python class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.697717Z",
     "iopub.status.busy": "2021-04-02T22:12:21.696871Z",
     "iopub.status.idle": "2021-04-02T22:12:21.761009Z",
     "shell.execute_reply": "2021-04-02T22:12:21.760443Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# Auto generated from None by pythongen.py version: 0.9.0\n",
      "# Generation date: 2021-04-02 17:12\n",
      "# Schema: synopsis2\n",
      "#\n",
      "# id: http://example.org/sample/example1\n",
      "# description:\n",
      "# license:\n",
      "\n",
      "import dataclasses\n",
      "import sys\n",
      "import re\n",
      "from typing import Optional, List, Union, Dict, ClassVar, Any\n",
      "from dataclasses import dataclass\n",
      "from linkml_model.meta import EnumDefinition, PermissibleValue, PvFormulaOptions\n",
      "\n",
      "from linkml_runtime.utils.slot import Slot\n",
      "from linkml_runtime.utils.metamodelcore import empty_list, empty_dict, bnode\n",
      "from linkml_runtime.utils.yamlutils import YAMLRoot, extended_str, extended_float, extended_int\n",
      "from linkml_runtime.utils.dataclass_extensions_376 import dataclasses_init_fn_with_kwargs\n",
      "from linkml_runtime.utils.formatutils import camelcase, underscore, sfx\n",
      "from linkml_runtime.utils.enumerations import EnumDefinitionImpl\n",
      "from rdflib import Namespace, URIRef\n",
      "from linkml_runtime.utils.curienamespace import CurieNamespace\n",
      "from linkml_runtime.utils.metamodelcore import Bool\n",
      "\n",
      "metamodel_version = \"1.7.0\"\n",
      "\n",
      "# Overwrite dataclasses _init_fn to add **kwargs in __init__\n",
      "dataclasses._init_fn = dataclasses_init_fn_with_kwargs\n",
      "\n",
      "# Namespaces\n",
      "FOAF = CurieNamespace('foaf', 'http://xmlns.com/foaf/0.1/')\n",
      "SAMP = CurieNamespace('samp', 'http://example.org/model/')\n",
      "XSD = CurieNamespace('xsd', 'http://www.w3.org/2001/XMLSchema#')\n",
      "DEFAULT_ = SAMP\n",
      "\n",
      "\n",
      "# Types\n",
      "class String(str):\n",
      "    type_class_uri = XSD.string\n",
      "    type_class_curie = \"xsd:string\"\n",
      "    type_name = \"string\"\n",
      "    type_model_uri = SAMP.String\n",
      "\n",
      "\n",
      "class Int(int):\n",
      "    type_class_uri = XSD.integer\n",
      "    type_class_curie = \"xsd:integer\"\n",
      "    type_name = \"int\"\n",
      "    type_model_uri = SAMP.Int\n",
      "\n",
      "\n",
      "class Boolean(Bool):\n",
      "    type_class_uri = XSD.boolean\n",
      "    type_class_curie = \"xsd:boolean\"\n",
      "    type_name = \"boolean\"\n",
      "    type_model_uri = SAMP.Boolean\n",
      "\n",
      "\n",
      "# Class references\n",
      "class PersonId(extended_str):\n",
      "    pass\n",
      "\n",
      "\n",
      "class FriendlyPersonId(PersonId):\n",
      "    pass\n",
      "\n",
      "\n",
      "@dataclass\n",
      "class Person(YAMLRoot):\n",
      "    \"\"\"\n",
      "    A person, living or dead\n",
      "    \"\"\"\n",
      "    _inherited_slots: ClassVar[List[str]] = []\n",
      "\n",
      "    class_class_uri: ClassVar[URIRef] = SAMP.Person\n",
      "    class_class_curie: ClassVar[str] = \"samp:Person\"\n",
      "    class_name: ClassVar[str] = \"person\"\n",
      "    class_model_uri: ClassVar[URIRef] = SAMP.Person\n",
      "\n",
      "    id: Union[str, PersonId] = None\n",
      "    last_name: str = None\n",
      "    first_name: Optional[Union[str, List[str]]] = empty_list()\n",
      "    age: Optional[int] = None\n",
      "    living: Optional[Bool] = None\n",
      "    knows: Optional[Union[Union[str, PersonId], List[Union[str, PersonId]]]] = empty_list()\n",
      "\n",
      "    def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):\n",
      "        if self.id is None:\n",
      "            raise ValueError(\"id must be supplied\")\n",
      "        if not isinstance(self.id, PersonId):\n",
      "            self.id = PersonId(self.id)\n",
      "\n",
      "        if self.last_name is None:\n",
      "            raise ValueError(\"last_name must be supplied\")\n",
      "        if not isinstance(self.last_name, str):\n",
      "            self.last_name = str(self.last_name)\n",
      "\n",
      "        if self.first_name is None:\n",
      "            self.first_name = []\n",
      "        if not isinstance(self.first_name, list):\n",
      "            self.first_name = [self.first_name]\n",
      "        self.first_name = [v if isinstance(v, str) else str(v) for v in self.first_name]\n",
      "\n",
      "        if self.age is not None and not isinstance(self.age, int):\n",
      "            self.age = int(self.age)\n",
      "\n",
      "        if self.living is not None and not isinstance(self.living, Bool):\n",
      "            self.living = Bool(self.living)\n",
      "\n",
      "        if self.knows is None:\n",
      "            self.knows = []\n",
      "        if not isinstance(self.knows, list):\n",
      "            self.knows = [self.knows]\n",
      "        self.knows = [v if isinstance(v, PersonId) else PersonId(v) for v in self.knows]\n",
      "\n",
      "        super().__post_init__(**kwargs)\n",
      "\n",
      "\n",
      "@dataclass\n",
      "class FriendlyPerson(Person):\n",
      "    \"\"\"\n",
      "    Any person that knows someone\n",
      "    \"\"\"\n",
      "    _inherited_slots: ClassVar[List[str]] = []\n",
      "\n",
      "    class_class_uri: ClassVar[URIRef] = SAMP.FriendlyPerson\n",
      "    class_class_curie: ClassVar[str] = \"samp:FriendlyPerson\"\n",
      "    class_name: ClassVar[str] = \"friendly_person\"\n",
      "    class_model_uri: ClassVar[URIRef] = SAMP.FriendlyPerson\n",
      "\n",
      "    id: Union[str, FriendlyPersonId] = None\n",
      "    last_name: str = None\n",
      "    knows: Union[Union[str, PersonId], List[Union[str, PersonId]]] = None\n",
      "\n",
      "    def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):\n",
      "        if self.id is None:\n",
      "            raise ValueError(\"id must be supplied\")\n",
      "        if not isinstance(self.id, FriendlyPersonId):\n",
      "            self.id = FriendlyPersonId(self.id)\n",
      "\n",
      "        if self.knows is None:\n",
      "            raise ValueError(\"knows must be supplied\")\n",
      "        elif not isinstance(self.knows, list):\n",
      "            self.knows = [self.knows]\n",
      "        elif len(self.knows) == 0:\n",
      "            raise ValueError(f\"knows must be a non-empty list\")\n",
      "        self.knows = [v if isinstance(v, PersonId) else PersonId(v) for v in self.knows]\n",
      "\n",
      "        super().__post_init__(**kwargs)\n",
      "\n",
      "\n",
      "# Enumerations\n",
      "\n",
      "\n",
      "# Slots\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(PythonGenerator(yaml, gen_slots=False).serialize())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Compile the python into a module"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.768642Z",
     "iopub.status.busy": "2021-04-02T22:12:21.767661Z",
     "iopub.status.idle": "2021-04-02T22:12:21.824510Z",
     "shell.execute_reply": "2021-04-02T22:12:21.824921Z"
    }
   },
   "outputs": [],
   "source": [
    "spec = compile(PythonGenerator(yaml).serialize(), 'test', 'exec')\n",
    "module = ModuleType('test')\n",
    "exec(spec, module.__dict__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### We can emit a UML rendering of  this model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.832651Z",
     "iopub.status.busy": "2021-04-02T22:12:21.831978Z",
     "iopub.status.idle": "2021-04-02T22:12:21.890688Z",
     "shell.execute_reply": "2021-04-02T22:12:21.891283Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"http://yuml.me/diagram/nofunky;dir:TB/class/[Person]<knows 0..*- [Person|id:string;first_name:string *;last_name:string;age:int %3F;living:boolean %3F],[FriendlyPerson|id(i):string;first_name(i):string *;last_name(i):string;age(i):int %3F;living(i):boolean %3F]- knows 1..*>[Person],[Person]^-[FriendlyPerson],[FriendlyPerson]\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(HTML(f'<img src=\"{YumlGenerator(yaml).serialize()}\"/>'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### We can emit a JSON-LD context for the model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.898783Z",
     "iopub.status.busy": "2021-04-02T22:12:21.898082Z",
     "iopub.status.idle": "2021-04-02T22:12:21.957854Z",
     "shell.execute_reply": "2021-04-02T22:12:21.958634Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "   \"_comments\": \"Auto generated from None by jsonldcontextgen.py version: 0.1.1\\n    Generation date: 2021-04-02 17:12\\n    Schema: synopsis2\\n    \\n    id: http://example.org/sample/example1\\n    description: \\n    license: \\n    \",\n",
      "   \"@context\": {\n",
      "      \"foaf\": \"http://xmlns.com/foaf/0.1/\",\n",
      "      \"samp\": \"http://example.org/model/\",\n",
      "      \"xsd\": \"http://www.w3.org/2001/XMLSchema#\",\n",
      "      \"@vocab\": \"http://example.org/model/\",\n",
      "      \"age\": {\n",
      "         \"@type\": \"xsd:integer\",\n",
      "         \"@id\": \"foaf:age\"\n",
      "      },\n",
      "      \"first_name\": {\n",
      "         \"@id\": \"foaf:firstName\"\n",
      "      },\n",
      "      \"id\": \"@id\",\n",
      "      \"knows\": {\n",
      "         \"@type\": \"@id\",\n",
      "         \"@id\": \"foaf:knows\"\n",
      "      },\n",
      "      \"last_name\": {\n",
      "         \"@id\": \"foaf:lastName\"\n",
      "      },\n",
      "      \"living\": {\n",
      "         \"@type\": \"xsd:boolean\"\n",
      "      },\n",
      "      \"@base\": \"http://example.org/people/\"\n",
      "   }\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "cntxt = ContextGenerator(yaml).serialize(base=\"http://example.org/people/\")\n",
    "print(cntxt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The python model can be used to create classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.964934Z",
     "iopub.status.busy": "2021-04-02T22:12:21.964154Z",
     "iopub.status.idle": "2021-04-02T22:12:21.967512Z",
     "shell.execute_reply": "2021-04-02T22:12:21.968318Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Person(id='42', last_name='smith', first_name=['Joe', 'Bob'], age=43, living=None, knows=[])\n"
     ]
    }
   ],
   "source": [
    "# Generate a person\n",
    "joe_smith = module.Person(id=\"42\", last_name=\"smith\", first_name=['Joe', 'Bob'], age=43)\n",
    "print(joe_smith)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### and can be combined w/ the JSON-LD Context to generate RDF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.974031Z",
     "iopub.status.busy": "2021-04-02T22:12:21.973340Z",
     "iopub.status.idle": "2021-04-02T22:12:21.990949Z",
     "shell.execute_reply": "2021-04-02T22:12:21.991340Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"id\": \"42\",\n",
      "  \"last_name\": \"smith\",\n",
      "  \"first_name\": [\n",
      "    \"Joe\",\n",
      "    \"Bob\"\n",
      "  ],\n",
      "  \"age\": 43,\n",
      "  \"@type\": \"Person\",\n",
      "  \"@context\": {\n",
      "    \"foaf\": \"http://xmlns.com/foaf/0.1/\",\n",
      "    \"samp\": \"http://example.org/model/\",\n",
      "    \"xsd\": \"http://www.w3.org/2001/XMLSchema#\",\n",
      "    \"@vocab\": \"http://example.org/model/\",\n",
      "    \"age\": {\n",
      "      \"@type\": \"xsd:integer\",\n",
      "      \"@id\": \"foaf:age\"\n",
      "    },\n",
      "    \"first_name\": {\n",
      "      \"@id\": \"foaf:firstName\"\n",
      "    },\n",
      "    \"id\": \"@id\",\n",
      "    \"knows\": {\n",
      "      \"@type\": \"@id\",\n",
      "      \"@id\": \"foaf:knows\"\n",
      "    },\n",
      "    \"last_name\": {\n",
      "      \"@id\": \"foaf:lastName\"\n",
      "    },\n",
      "    \"living\": {\n",
      "      \"@type\": \"xsd:boolean\"\n",
      "    },\n",
      "    \"@base\": \"http://example.org/people/\"\n",
      "  }\n",
      "}\n",
      "@prefix : <http://example.org/model/> .\n",
      "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n",
      "@prefix samp: <http://example.org/model/> .\n",
      "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
      "\n",
      "<http://example.org/people/42> a samp:Person ;\n",
      "    foaf:age 43 ;\n",
      "    foaf:firstName \"Bob\",\n",
      "        \"Joe\" ;\n",
      "    foaf:lastName \"smith\" .\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Add the context and turn it into RDF\n",
    "jsonld = dumps(joe_smith, cntxt)\n",
    "print(jsonld)\n",
    "g = Graph()\n",
    "g.parse(data=jsonld, format=\"json-ld\")\n",
    "print(g.serialize(format=\"turtle\").decode())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The model can be turned into ShEx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:21.997764Z",
     "iopub.status.busy": "2021-04-02T22:12:21.997122Z",
     "iopub.status.idle": "2021-04-02T22:12:22.067571Z",
     "shell.execute_reply": "2021-04-02T22:12:22.067966Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BASE <http://example.org/model/>\n",
      "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n",
      "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n",
      "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n",
      "\n",
      "\n",
      "<String> xsd:string\n",
      "\n",
      "<Int> xsd:integer\n",
      "\n",
      "<Boolean> xsd:boolean\n",
      "\n",
      "<FriendlyPerson> CLOSED {\n",
      "    (  $<FriendlyPerson_tes> (  &<Person_tes> ;\n",
      "          rdf:type [ <Person> ] ? ;\n",
      "          <knows> @<Person> +\n",
      "       ) ;\n",
      "       rdf:type [ <FriendlyPerson> ]\n",
      "    )\n",
      "}\n",
      "\n",
      "<Person>  (\n",
      "    CLOSED {\n",
      "       (  $<Person_tes> (  foaf:firstName @<String> * ;\n",
      "             foaf:lastName @<String> ;\n",
      "             foaf:age @<Int> ? ;\n",
      "             <living> @<Boolean> ? ;\n",
      "             foaf:knows @<Person> *\n",
      "          ) ;\n",
      "          rdf:type [ <Person> ]\n",
      "       )\n",
      "    } OR @<FriendlyPerson>\n",
      ")\n",
      "\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "shex = ShExGenerator(yaml).serialize(collections=False)\n",
    "print(shex)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The ShEx can then be used to validate RDF"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:22.072610Z",
     "iopub.status.busy": "2021-04-02T22:12:22.071675Z",
     "iopub.status.idle": "2021-04-02T22:12:22.224039Z",
     "shell.execute_reply": "2021-04-02T22:12:22.224849Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Conforms\n"
     ]
    }
   ],
   "source": [
    "from pyshex.evaluate import evaluate\n",
    "r = evaluate(g, shex,\n",
    "             start=\"http://example.org/model/Person\",\n",
    "             focus=\"http://example.org/people/42\")\n",
    "print(\"Conforms\" if r[0] else r[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-04-02T22:12:22.250603Z",
     "iopub.status.busy": "2021-04-02T22:12:22.250047Z",
     "iopub.status.idle": "2021-04-02T22:12:22.252819Z",
     "shell.execute_reply": "2021-04-02T22:12:22.253779Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  Testing <http://example.org/people/42> against shape http://example.org/model/FriendlyPerson\n",
      "       No matching triples found for predicate samp:knows\n"
     ]
    }
   ],
   "source": [
    "r = evaluate(g, shex,\n",
    "             start=\"http://example.org/model/FriendlyPerson\",\n",
    "             focus=\"http://example.org/people/42\")\n",
    "print(\"Conforms\" if r[0] else r[1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
