From 1c2718ab28f70d12796384cee2ca0ae300800436 Mon Sep 17 00:00:00 2001 From: napakalas Date: Mon, 16 Feb 2026 11:29:28 +1300 Subject: [PATCH 1/5] Update neurondm to 0.1.10. --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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]] From af7343a424f492aa1bcce5a3d07c1c50fb275b47 Mon Sep 17 00:00:00 2001 From: napakalas Date: Wed, 18 Feb 2026 09:57:38 +1300 Subject: [PATCH 2/5] Reorganise TTL loading by knowledge type and keep legacy fallbacks (#65). --- mapknowledge/npo.py | 90 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/mapknowledge/npo.py b/mapknowledge/npo.py index 5bcb701..68c570a 100644 --- a/mapknowledge/npo.py +++ b/mapknowledge/npo.py @@ -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' @@ -387,26 +411,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) From 71dcbc10443ea6c313c52f55df177700477984d9 Mon Sep 17 00:00:00 2001 From: napakalas Date: Wed, 18 Feb 2026 10:05:32 +1300 Subject: [PATCH 3/5] Prioritise long-label value from skos.prefLabel (#65). --- mapknowledge/npo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mapknowledge/npo.py b/mapknowledge/npo.py index 68c570a..68dff4a 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) @@ -211,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 @@ -569,7 +570,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 From bd846d32e1e7e413fbf9977110e234504f34dc70 Mon Sep 17 00:00:00 2001 From: napakalas Date: Wed, 18 Feb 2026 10:07:53 +1300 Subject: [PATCH 4/5] Expose expert-consultants, member-of-circuits, and curator-notes attributes (#65). --- mapknowledge/npo.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mapknowledge/npo.py b/mapknowledge/npo.py index 68dff4a..cb837c0 100644 --- a/mapknowledge/npo.py +++ b/mapknowledge/npo.py @@ -244,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 @@ -584,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} From 5533469a05e81773af96834c99f02d1ca04e52d5 Mon Sep 17 00:00:00 2001 From: napakalas Date: Wed, 18 Feb 2026 13:30:36 +1300 Subject: [PATCH 5/5] Tidy up up for consistency (#65). --- mapknowledge/npo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapknowledge/npo.py b/mapknowledge/npo.py index cb837c0..d0d4b55 100644 --- a/mapknowledge/npo.py +++ b/mapknowledge/npo.py @@ -75,7 +75,7 @@ NPO_APINATOMY_TTLS = [ 'apinat-complex', - 'apinat-manual' + 'apinat-manual', ] NPO_APINATOMY_LEGACY_TTLS = [