diff --git a/.readthedocs.yml b/.readthedocs.yml index 7c0b59382..f6e293031 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,3 +13,7 @@ python: extra_requirements: - docs - ode + - dkg-client + - dkg-construct + - metaregistry + - web diff --git a/README.md b/README.md index e398244b0..62a0cb358 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ MIRA is a framework for representing systems using ontology-grounded **meta-mode * Generating an executable model from MIRA Templates and running simulation: [Notebook 2](https://github.com/indralab/mira/blob/main/notebooks/simulation.ipynb) * Stratifying and visualizing MIRA models, and exporting as Petri nets: [Notebook 3](https://github.com/indralab/mira/blob/main/notebooks/viz_strat_petri.ipynb) * Using the MIRA Domain Knowledge Graph REST API: [Notebook 4](https://github.com/indralab/mira/blob/main/notebooks/dkg_api.ipynb) +* Using the Model REST API to perform various model operations: [Notebook 5](https://github.com/indralab/mira/blob/main/notebooks/model_api.ipynb) +* Using the web client in python that connects to the REST API: [Notebook 6](https://github.com/indralab/mira/blob/main/notebooks/web_client.ipynb) + +[//]: # (Gromet Export fixme: uncomment when gromet works again) ## Related work @@ -30,13 +34,13 @@ MIRA builds on and generalizes prior work including: The most recent code and data can be installed directly from GitHub with: -```python +```shell python -m pip install git+https://github.com/indralab/mira.git ``` To install in development mode, use the following: -```python +```shell git clone git+https://github.com/indralab/mira.git cd mira python -m pip install -e . diff --git a/docs/source/conf.py b/docs/source/conf.py index 2bd6317bd..41bbe9ff3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -16,6 +16,7 @@ import os import re import sys +import mock from datetime import date sys.path.insert(0, os.path.abspath("../..")) @@ -67,6 +68,7 @@ "sphinx_autodoc_typehints", "sphinx_automodapi.automodapi", "sphinx_automodapi.smart_resolver", + "sphinxcontrib.autodoc_pydantic", "m2r2", ] @@ -235,3 +237,16 @@ # Don't sort alphabetically, explained at: # https://stackoverflow.com/questions/37209921/python-how-not-to-sort-sphinx-output-in-alphabetical-order autodoc_member_order = "bysource" + +MOCK_MODULES = [ + "neo4j", + "neo4j.graph", + #"bioontologies", + #"bioontologies.obograph", + #"bioregistry", + #"bioregistry.app", + #"bioregistry.app.impl", +] + +for mod_name in MOCK_MODULES: + sys.modules[mod_name] = mock.Mock() diff --git a/docs/source/dkg.rst b/docs/source/dkg.rst new file mode 100644 index 000000000..bbc1308ee --- /dev/null +++ b/docs/source/dkg.rst @@ -0,0 +1,41 @@ +Domain Knowledge Graph +====================== +.. automodule:: mira.dkg + :members: + :show-inheritance: + +Client (:py:mod:`mira.dkg.client`) +---------------------------------- +.. automodule:: mira.dkg.client + :members: + :show-inheritance: + +Construct (:py:mod:`mira.dkg.construct`) +---------------------------------------- +.. automodule:: mira.dkg.construct + :members: + :show-inheritance: + +Constructing Registry (:py:mod:`mira.dkg.construct_registry`) +------------------------------------------------------------- +.. automodule:: mira.dkg.construct_registry + :members: + :show-inheritance: + +Configuration Models (:py:mod:`mira.dkg.models`) +------------------------------------------------ +.. automodule:: mira.dkg.models + :members: + :show-inheritance: + +App Utilities (:py:mod:`mira.dkg.utils`) +---------------------------------------- +.. automodule:: mira.dkg.utils + :members: + :show-inheritance: + +Web Client (:py:mod:`mira.dkg.web_client`) +------------------------------------------ +.. automodule:: mira.dkg.web_client + :members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index c12d42c4b..edb7dc708 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -8,6 +8,8 @@ Table of Contents overview metamodel modeling + dkg + metaregistry Indices and Tables ------------------ diff --git a/docs/source/metamodel.rst b/docs/source/metamodel.rst index 125a2dd73..7fc9008ac 100644 --- a/docs/source/metamodel.rst +++ b/docs/source/metamodel.rst @@ -1,4 +1,12 @@ Meta-model ========== +.. automodule:: mira.metamodel + :members: + :show-inheritance: -.. automodapi:: mira.metamodel.templates +Templates (:py:mod:`mira.metamodel.templates`) +---------------------------------------------- +.. automodule:: mira.metamodel.templates + :members: + :exclude-members: Concept, ControlledConversion, NaturalConversion, Provenance, Template + :show-inheritance: diff --git a/docs/source/metaregistry.rst b/docs/source/metaregistry.rst new file mode 100644 index 000000000..0d00a6fe6 --- /dev/null +++ b/docs/source/metaregistry.rst @@ -0,0 +1,11 @@ +Metaregistry +============ +.. automodule:: mira.dkg.metaregistry + :members: + :show-inheritance: + +Metaregistry Utils (:py:mod:`mira.dkg.metaregistry.utils`) +---------------------------------------------------------- +.. automodule:: mira.dkg.metaregistry.utils + :members: + :show-inheritance: diff --git a/mira/dkg/construct.py b/mira/dkg/construct.py index 66840a01e..6cb3f17c2 100644 --- a/mira/dkg/construct.py +++ b/mira/dkg/construct.py @@ -39,7 +39,7 @@ EDGES_PATH = DEMO_MODULE.join(name="edges.tsv.gz") METAREGISTRY_PATH = DEMO_MODULE.join(name="metaregistry.json") OBSOLETE = {"oboinowl:ObsoleteClass", "oboinowl:ObsoleteProperty"} -EDGES_PATHS: dict[str, Path] = { +EDGES_PATHS: Dict[str, Path] = { prefix: DEMO_MODULE.join("sources", name=f"edges_{prefix}.tsv") for prefix in PREFIXES } EDGE_HEADER = ( diff --git a/mira/dkg/utils.py b/mira/dkg/utils.py index bcec4f214..3b14b6e3a 100644 --- a/mira/dkg/utils.py +++ b/mira/dkg/utils.py @@ -21,6 +21,7 @@ class MiraState: grounder: Grounder +#: A list of all prefixes used in MIRA PREFIXES = [ # meta "oboinowl", diff --git a/mira/dkg/web_client.py b/mira/dkg/web_client.py index 1d913a551..b78851ab8 100644 --- a/mira/dkg/web_client.py +++ b/mira/dkg/web_client.py @@ -8,6 +8,7 @@ from mira.dkg.utils import DKG_REFINER_RELS __all__ = [ + "web_client", "get_relations_web", "get_entity_web", "get_lexical_web", diff --git a/mira/metamodel/schema.json b/mira/metamodel/schema.json index 31ef6e235..b99452263 100644 --- a/mira/metamodel/schema.json +++ b/mira/metamodel/schema.json @@ -6,6 +6,7 @@ "definitions": { "Concept": { "title": "Concept", + "description": "A concept is specified by its identifier(s), name, and - optionally -\nits context.", "type": "object", "properties": { "name": { @@ -36,6 +37,7 @@ }, "Template": { "title": "Template", + "description": "The Template is a parent class for model processes", "type": "object", "properties": {} }, @@ -46,6 +48,7 @@ }, "NaturalConversion": { "title": "NaturalConversion", + "description": "Specifies a process of natural conversion from subject to outcome", "type": "object", "properties": { "type": { @@ -78,6 +81,7 @@ }, "ControlledConversion": { "title": "ControlledConversion", + "description": "Specifies a process of controlled conversion from subject to outcome,\ncontrolled by the controller", "type": "object", "properties": { "type": { diff --git a/mira/metamodel/templates.py b/mira/metamodel/templates.py index 49f4a6672..cacf8a2d2 100644 --- a/mira/metamodel/templates.py +++ b/mira/metamodel/templates.py @@ -3,7 +3,16 @@ Regenerate the JSON schema by running ``python -m mira.metamodel.templates``. """ -__all__ = ["Concept", "Template", "Provenance", "ControlledConversion", "NaturalConversion"] +__all__ = [ + "Concept", + "Template", + "Provenance", + "ControlledConversion", + "NaturalConversion", + "get_json_schema", + "templates_equal", + "assert_concept_context_refinement", +] import json import logging @@ -22,6 +31,8 @@ class Config(BaseModel): + """Config determining how keys are generated""" + prefix_priority: List[str] @@ -33,6 +44,10 @@ class Config(BaseModel): class Concept(BaseModel): + """A concept is specified by its identifier(s), name, and - optionally - + its context. + """ + name: str = Field(..., description="The name of the concept.") identifiers: Mapping[str, str] = Field( default_factory=dict, description="A mapping of namespaces to identifiers." @@ -126,8 +141,7 @@ def refinement_of( refinement_func : A function that given a source/more detailed entity and a target/less detailed entity checks if they are in a child-parent and - returns a boolean. If not provided, the default - ``mira.dkg.web_client.is_ontological_child`` is used. + returns a boolean. Returns ------- @@ -162,6 +176,8 @@ def refinement_of( class Template(BaseModel): + """The Template is a parent class for model processes""" + @classmethod def from_json(cls, data) -> "Template": template_type = data.pop("type") @@ -169,6 +185,21 @@ def from_json(cls, data) -> "Template": return stmt_cls(**data) def is_equal_to(self, other: "Template", with_context: bool = False) -> bool: + """Check if this template is equal to another template + + Parameters + ---------- + other : + The other template to check for equality with this one with + with_context : + If True, the contexts are taken into account when checking for + equality. Default: False. + + Returns + ------- + : + True if the other Template is equal to this Template + """ if not isinstance(other, Template): return False return templates_equal(self, other, with_context) @@ -179,7 +210,26 @@ def refinement_of( refinement_func: Callable[[str, str], bool], with_context: bool = False, ) -> bool: - """Check if this template is a more detailed version of another""" + """Check if this template is a more detailed version of another + + Parameters + ---------- + other : + The other template to compare with. Is assumed to be less + detailed than this template. + with_context : + If True, also consider the context of Templates' Concepts for the + refinement. + refinement_func : + A function that given a source/more detailed entity and a + target/less detailed entity checks if they are in a child-parent + relationship and returns a boolean. + + Returns + ------- + : + True if this Template is a refinement of the other Template. + """ if not isinstance(other, Template): return False @@ -219,6 +269,9 @@ class Provenance(BaseModel): class ControlledConversion(Template): + """Specifies a process of controlled conversion from subject to outcome, + controlled by the controller""" + type: Literal["ControlledConversion"] = Field("ControlledConversion", const=True) controller: Concept subject: Concept @@ -226,6 +279,7 @@ class ControlledConversion(Template): provenance: List[Provenance] = Field(default_factory=list) def with_context(self, **context) -> "ControlledConversion": + """Return a copy of this template with context added""" return self.__class__( type=self.type, subject=self.subject.with_context(**context), @@ -244,12 +298,15 @@ def get_key(self, config: Optional[Config] = None): class NaturalConversion(Template): + """Specifies a process of natural conversion from subject to outcome""" + type: Literal["NaturalConversion"] = Field("NaturalConversion", const=True) subject: Concept outcome: Concept provenance: List[Provenance] = Field(default_factory=list) def with_context(self, **context) -> "NaturalConversion": + """Return a copy of this template with context added""" return self.__class__( type=self.type, subject=self.subject.with_context(**context), @@ -282,6 +339,23 @@ def get_json_schema(): def templates_equal(templ: Template, other_templ: Template, with_context: bool) -> bool: + """Check if two Template objects are equal + + Parameters + ---------- + templ : + A template to compare. + other_templ : + The other template to compare. + with_context : + If True, also check the contexts of the contained Concepts of the + Template. + + Returns + ------- + : + True if the two Template objects are equal. + """ if templ.type != other_templ.type: return False diff --git a/setup.cfg b/setup.cfg index ad369d30f..9244a95c5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,6 +65,8 @@ uvicorn = gunicorn = gunicorn docs = + bioregistry + bioontologies sphinx sphinx-rtd-theme sphinx-autodoc-typehints @@ -72,6 +74,8 @@ docs = autodoc-pydantic m2r2 pygraphviz + mock + wget [mypy] plugins = pydantic.mypy diff --git a/tox.ini b/tox.ini index 93aebef8c..16b201a78 100644 --- a/tox.ini +++ b/tox.ini @@ -23,6 +23,10 @@ commands = extras = docs ode + dkg-client + dkg-construct + metaregistry + web commands = python -m sphinx -b {posargs:html} -d docs/build/doctrees docs/source docs/build/{posargs:html}