diff --git a/mapknowledge/npo.py b/mapknowledge/npo.py index 5bcb701..d0d4b55 100644 --- a/mapknowledge/npo.py +++ b/mapknowledge/npo.py @@ -45,7 +45,7 @@ from neurondm.core import IntersectionOf from pyontutils.core import OntGraph, OntResIri -from pyontutils.namespaces import rdfs, ilxtr +from pyontutils.namespaces import rdfs, ilxtr, skos # Renable general logging logging.disable(logging.NOTSET) @@ -73,11 +73,35 @@ NPO_RAW = f'https://raw.githubusercontent.com/{NPO_OWNER}/{NPO_REPO}' NPO_GIT = f'https://github.com/{NPO_OWNER}/{NPO_REPO}' -NPO_TTLS = ('apinat-partial-orders', - 'apinat-pops-more', - 'apinat-simple-sheet', - 'sparc-nlp', - 'apinat-complex') +NPO_APINATOMY_TTLS = [ + 'apinat-complex', + 'apinat-manual', +] + +NPO_APINATOMY_LEGACY_TTLS = [ + 'apinat-partial-orders', + 'apinat-pops-more', + 'apinat-simple-sheet', + 'apinatomy-neuron-populations', + '../../npo-annotations', +] + +NPO_NLP_TTLS = [ + 'composer-nlp', +] + +NPO_NLP_LEGACY_TTLS = [ + 'sparc-nlp', +] + +NPO_COMPOSER_TTLS = [ + 'composer', +] + +LABEL_TTLS = [ + '../../npo', + '../../sparc-community-terms', +] GEN_NEURONS_PATH = 'ttl/generated/neurons/' TURTLE_SUFFIX = '.ttl' @@ -187,6 +211,7 @@ def for_composer(n, cull=False) -> dict[str, Any]: fc = dict( id = NAMESPACES.curie(str(n.id_)), label = str(n.origLabel), + long_label = str(pref_labels[0]) if (pref_labels:=lrdf(n, skos.prefLabel)) else str(n.origLabel), origin = lpes(n, ilxtr.hasSomaLocatedIn), dest = ( # XXX looking at this there seems to be a fault assumption that @@ -219,11 +244,14 @@ def for_composer(n, cull=False) -> dict[str, Any]: + [l[0] for l in lpes(n, ilxtr.hasProjectionPhenotype)], forward_connections = [fc[0] for fc in lpes(n, ilxtr.hasForwardConnectionPhenotype)], node_phenotypes = {NAMESPACES.curie(str(pn)): lpes(n, pn) for pn in NODE_PHENOTYPES}, + member_of_circuits = [l[0] for l in lpes(n, ilxtr.isMemberOfCircuit)], # direct references from individual individual neurons provenance = lrdf(n, ilxtr.literatureCitation), sentence_number = lrdf(n, ilxtr.sentenceNumber), note_alert = lrdf(n, ilxtr.alertNote), + expert_consultants = lrdf(n, ilxtr.expertConsultant), + curator_note = lrdf(n, ilxtr.curatorNote), # XXX provenance from ApiNATOMY models as a whole is not ingested # right now because composer lacks support for 1:n from neuron to # prov, (or rather lacks prov collections) and because it attaches @@ -387,26 +415,52 @@ def __load_knowledge_from_ttl(self): graphBase._sgv = None # type: ignore del graphBase._sgv + def load_path_knowledge_ttls(ttls): + g = OntGraph() + for f in ttls: + ori = OntResIri(f'{NPO_RAW}/{self.__npo_release}/{GEN_NEURONS_PATH}{f}{TURTLE_SUFFIX}') + try: + if ori.graph is not None: + if f == 'apinat-manual': + for s, p, o in ori.graph: + if not ( + p == rdfs.subClassOf + and (o, rdfs.subClassOf, ilxtr.NeuronApinatComplex) in ori.graph + ): + g.add((s, p, o)) + elif f == 'apinatomy-neuron-populations': + [self.__rdf_graph.add((s, rdfs.label, o)) + for s, o in ori.graph.subject_objects(rdfs.label)] + else: + [g.add(t) for t in ori.graph] + except: + log.warning(f'Could not fetch {ori.iri} from {self.__npo_release}.') + return None + return g + OntTerm.query._services = (RDFL(self.__rdf_graph, OntId),) - for f in NPO_TTLS: - ori = OntResIri(f'{NPO_RAW}/{self.__npo_release}/{GEN_NEURONS_PATH}{f}{TURTLE_SUFFIX}') - try: - if ori.graph is not None: - [self.__rdf_graph.add(t) for t in ori.graph] - except: - log.warning(f'Could not fetch {ori.iri} from {self.__npo_release}.') - - for f in ('apinatomy-neuron-populations', '../../npo', '../../sparc-community-terms'): + # Load ApiNATOMY + if (apinatomy_graph := load_path_knowledge_ttls(NPO_APINATOMY_TTLS)) is None: + apinatomy_graph = load_path_knowledge_ttls(NPO_APINATOMY_LEGACY_TTLS) + self.__rdf_graph += apinatomy_graph if apinatomy_graph is not None else OntGraph() + # Load SPARC_NLP + if (nlp_graph := load_path_knowledge_ttls(NPO_NLP_TTLS)) is None: + nlp_graph = load_path_knowledge_ttls(NPO_NLP_LEGACY_TTLS) + self.__rdf_graph += nlp_graph if nlp_graph is not None else OntGraph() + # Load pain, portal, and gastInt + if (composer_graph := load_path_knowledge_ttls(NPO_COMPOSER_TTLS)) is not None: + self.__rdf_graph += composer_graph + + for f in LABEL_TTLS: p = urllib.parse.quote(GEN_NEURONS_PATH + f) ori = OntResIri(f'{NPO_RAW}/{self.__npo_release}/{p}{TURTLE_SUFFIX}') if ori.graph is not None: [self.__rdf_graph.add((s, rdfs.label, o)) - for s, o in ori.graph[:rdfs.label:]] # type: ignore - if f != 'apinatomy-neuron-populations': - [self.__rdf_graph.add((s, rdfs.subClassOf, o)) - for s, o in ori.graph[:rdfs.subClassOf:]] # type: ignore - [self.__rdf_graph.add((s, ilxtr.hasExistingId, o)) - for s, o in ori.graph[:ilxtr.hasExistingId:]] # type: ignore + for s, o in ori.graph.subject_objects(rdfs.label)] + [self.__rdf_graph.add((s, rdfs.subClassOf, o)) + for s, o in ori.graph.subject_objects(rdfs.subClassOf)] + [self.__rdf_graph.add((s, ilxtr.hasExistingId, o)) + for s, o in ori.graph.subject_objects(ilxtr.hasExistingId)] config = Config('npo-connectivity') config.load_existing(self.__rdf_graph) @@ -519,7 +573,7 @@ def get_nodes(target_nodes, nodes): if (path_kn:=self.__get_neuron_knowledge(entity)) is not None: if 'label' not in knowledge: knowledge['label'] = path_kn['label'] - knowledge['long-label'] = path_kn['label'] + knowledge['long-label'] = path_kn['long_label'] knowledge['connectivity'] = path_kn['connectivity'] if len(phenotype:=path_kn['phenotype']+path_kn['circuit_type']) > 0: knowledge['phenotypes'] = phenotype @@ -533,6 +587,12 @@ def get_nodes(target_nodes, nodes): knowledge['alert'] = alert if len(references:=path_kn['provenance']) > 0: knowledge['references'] = references + if len(expert_consultants:=path_kn['expert_consultants']) > 0: + knowledge['expert-consultants'] = expert_consultants + if len(curator_note:=path_kn['curator_note']) > 0: + knowledge['curator-note'] = curator_note + if len(member_of_circuits:=path_kn['member_of_circuits']) > 0: + knowledge['member-of-circuits'] = member_of_circuits knowledge['pathDisconnected'] = not path_kn.get('connected', False) knowledge['forward-connections'] = path_kn['forward_connections'] all_nodes = {n for edge in path_kn['connectivity'] for n in edge} diff --git a/pyproject.toml b/pyproject.toml index 63ce17d..4f8faf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "requests>=2.28.1", "rdflib>=7.1.4", "sparqlwrapper>=2.0.0", - "neurondm (==0.1.9)", + "neurondm==0.1.10", "pyontutils (==0.1.39)", "structlog>=24.4.0", "psycopg[binary,pool]>=3.2.9", diff --git a/uv.lock b/uv.lock index 92bd787..525c908 100644 --- a/uv.lock +++ b/uv.lock @@ -479,7 +479,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "networkx", specifier = ">=3.3" }, - { name = "neurondm", specifier = "==0.1.9" }, + { name = "neurondm", specifier = "==0.1.10" }, { name = "psycopg", extras = ["binary", "pool"], specifier = ">=3.2.9" }, { name = "pyontutils", specifier = "==0.1.39" }, { name = "rdflib", specifier = ">=7.1.4" }, @@ -517,15 +517,15 @@ wheels = [ [[package]] name = "neurondm" -version = "0.1.9" +version = "0.1.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "hyputils" }, { name = "pyontutils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/1d/c79fd768fdb18dde24a84876bebe9623d96680692c300aea8b9c3a1e7653/neurondm-0.1.9.tar.gz", hash = "sha256:960af602c26c5238670b6a938fb4d08e629d9c070d7f50bf48ea1ae109c26222", size = 201964 } +sdist = { url = "https://files.pythonhosted.org/packages/0b/6c/62ee0a7f933851835da58300bbfb9dec70e0f903d0eaa0ad0a0500a0dfd0/neurondm-0.1.10.tar.gz", hash = "sha256:59e543074e62ef3d48e1efe3691701650420fde865869c454e4acaef321a83a9", size = 207289 } wheels = [ - { url = "https://files.pythonhosted.org/packages/21/6f/fd5ad2c6677c518e55332ad7bca599ff5f6f0ebebdf823de5859cf3ca4fd/neurondm-0.1.9-py2.py3-none-any.whl", hash = "sha256:d76bd0600939b59cdd9d992b49ad3c1e9f2c0a992d0d5964f9025c72fc55a016", size = 177576 }, + { url = "https://files.pythonhosted.org/packages/9d/3b/46c0b2bbb4cb10ec0fb1d9f55dd99b181bfb699293a25bd7a23c4814cf08/neurondm-0.1.10-py2.py3-none-any.whl", hash = "sha256:d2a7c5d04f01b22b87f44846f0e4ba8e8cc9c2c2c8bcf1d7785bc4e1f13e46d8", size = 183057 }, ] [[package]]