diff --git a/src/vfbquery/term_info_queries.py b/src/vfbquery/term_info_queries.py index 9502f24..04f1cf8 100644 --- a/src/vfbquery/term_info_queries.py +++ b/src/vfbquery/term_info_queries.py @@ -87,6 +87,11 @@ def get_types_str(self, show_types: bool) -> str: def return_type(self, type_list: List[str]) -> str: return " ".join([typ.replace("_", " ") for typ in type_list]) + def is_deprecated(self) -> bool: + """True if this entity carries the 'Deprecated' tag (mirrors the + Neo4j :Deprecated label) in its types or unique_facets.""" + return "Deprecated" in (self.types or []) or "Deprecated" in (self.unique_facets or []) + @dataclass_json @dataclass @@ -290,6 +295,11 @@ def link(self): return self.homepage return self.site.iri + def site_deprecated(self) -> bool: + """True if the cross-referenced site is deprecated; such xrefs must not + be turned into external links.""" + return bool(self.site) and self.site.is_deprecated() + @dataclass_json @dataclass @@ -621,6 +631,9 @@ def get_xref_list(self): results = list() if self.xrefs: for xref in self.xrefs: + # never build a link to a deprecated site + if xref.site_deprecated(): + continue results.append(xref.get_ext_link()) # sort xrefs alphabetically (by site) results = sorted(results, key=lambda d: d['label']) @@ -694,6 +707,9 @@ def get_thumbnail(self): def get_cluster_image(self): image_array = list() try: + # don't build an image URL from a deprecated site + if self.xrefs[0].site_deprecated(): + return None image_array.append(self.get_image(self.xrefs[0].link_base + self.xrefs[0].accession + "/snapshot.png", self.term.core.label, self.term.core.short_form)) except Exception as e: diff --git a/src/vfbquery/vfb_connectivity.py b/src/vfbquery/vfb_connectivity.py index 2efc8a0..9ae74a9 100644 --- a/src/vfbquery/vfb_connectivity.py +++ b/src/vfbquery/vfb_connectivity.py @@ -242,6 +242,8 @@ def _build_connectivity_cypher(upstream_id, downstream_id, weight, "-[r:synapsed_to]->" "(n2:Individual:Neuron:has_neuron_connectivity)-[:INSTANCEOF]->(c2)" f"\nWHERE r.weight[0] >= {weight}" + # deprecated neurons are obsolete and must not appear in connectivity + "\nAND NOT n1:Deprecated AND NOT n2:Deprecated" ) # Database filtering @@ -256,6 +258,10 @@ def _build_connectivity_cypher(upstream_id, downstream_id, weight, if not group_by_class: # Per-neuron results + # Show the source site + accession as plain text even when the site is + # deprecated (these columns are passed through to the results table and + # are not rendered as links downstream); deprecated *neurons* are still + # excluded above. clauses.append( "OPTIONAL MATCH (n1)-[r1:database_cross_reference]->" "(s1:Individual:Site {is_data_source:[True]})" @@ -291,6 +297,8 @@ def _build_connectivity_cypher(upstream_id, downstream_id, weight, # Count total upstream neurons (with optional db filtering) total_match = ( "MATCH (c1)<-[:INSTANCEOF]-(all_n1:Individual:has_neuron_connectivity)" + # keep the percent_connected denominator consistent: exclude deprecated neurons + "\nWHERE NOT all_n1:Deprecated" ) if exclude_dbs: db_list = str(exclude_dbs) diff --git a/src/vfbquery/vfb_queries.py b/src/vfbquery/vfb_queries.py index 783980f..1b7011a 100644 --- a/src/vfbquery/vfb_queries.py +++ b/src/vfbquery/vfb_queries.py @@ -1345,6 +1345,9 @@ def term_info_parse_object(results, short_form): xrefs_out = [] for x in vfbTerm.xrefs: site = getattr(x, 'site', None) + # never build a link to a deprecated site + if site is not None and getattr(site, 'is_deprecated', None) and site.is_deprecated(): + continue label = getattr(site, 'label', '') if site else '' acc = x.accession if getattr(x, 'accession', None) and x.accession != "None" else '' if acc: @@ -2268,8 +2271,9 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1): apoc.text.format("[%s](%s)",[i.label,i.short_form]) AS label, apoc.text.join(i.uniqueFacets, '|') AS tags, apoc.text.format("[%s](%s)",[p.label,p.short_form]) AS parent, - REPLACE(apoc.text.format("[%s](%s)",[site.label,site.short_form]), '[null](null)', '') AS source, - REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0],site.link_base[0] + rx.accession[0]]), '[null](null)', '') AS source_id, + // Deprecated sites: show the source/accession text but no link. + CASE WHEN site:Deprecated THEN COALESCE(site.label,'') ELSE REPLACE(apoc.text.format("[%s](%s)",[site.label,site.short_form]), '[null](null)', '') END AS source, + CASE WHEN site:Deprecated THEN COALESCE(rx.accession[0],'') ELSE REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0],site.link_base[0] + rx.accession[0]]), '[null](null)', '') END AS source_id, apoc.text.format("[%s](%s)",[CASE WHEN templ.symbol[0] <> '' THEN templ.symbol[0] ELSE templ.label END,templ.short_form]) AS template, apoc.text.format("[%s](%s)",[ds.label,ds.short_form]) AS dataset, REPLACE(apoc.text.format("[%s](%s)",[lic.label,lic.short_form]), '[null](null)', '') AS license, @@ -2727,8 +2731,8 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram r.{similarity_score}[0] AS score, apoc.text.join(coalesce(n2.uniqueFacets, []), '|') AS tags, type, - REPLACE(apoc.text.format("[%s](%s)",[site.label,site.short_form]), '[null](null)', '') AS source, - REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0], (site.link_base[0] + rx.accession[0])]), '[null](null)', '') AS source_id, + CASE WHEN site:Deprecated THEN COALESCE(site.label,'') ELSE REPLACE(apoc.text.format("[%s](%s)",[site.label,site.short_form]), '[null](null)', '') END AS source, + CASE WHEN site:Deprecated THEN COALESCE(rx.accession[0],'') ELSE REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0], (site.link_base[0] + rx.accession[0])]), '[null](null)', '') END AS source_id, REPLACE(apoc.text.format("[%s](%s)",[CASE WHEN templ.symbol[0] <> '' THEN templ.symbol[0] ELSE templ.label END,templ.short_form]), '[null](null)', '') AS template, coalesce(technique.label, '') AS technique, REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[n2.label + " aligned to " + CASE WHEN templ.symbol[0] <> '' THEN templ.symbol[0] ELSE templ.label END, REPLACE(COALESCE(ri.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), n2.label + " aligned to " + CASE WHEN templ.symbol[0] <> '' THEN templ.symbol[0] ELSE templ.label END, templ.short_form + "," + n2.short_form]), "[![null]( 'null')](null)", "") as thumbnail @@ -4560,15 +4564,19 @@ def _owlery_query_to_results(owl_query_string: str, short_form: str, return_data for xref in xrefs: if xref.get('is_data_source', False): site_info = xref.get('site', {}) + # Deprecated sites: show the source/accession + # text but never build a link to them. + site_tags = (site_info.get('types') or []) + (site_info.get('unique_facets') or []) + is_deprecated = 'Deprecated' in site_tags site_label = site_info.get('symbol') or site_info.get('label', '') site_short_form = site_info.get('short_form', '') - if site_label and site_short_form: - source = f"[{site_label}]({site_short_form})" - + if site_label: + source = site_label if (is_deprecated or not site_short_form) else f"[{site_label}]({site_short_form})" + accession = xref.get('accession', '') link_base = xref.get('link_base', '') - if accession and link_base: - source_id = f"[{accession}]({link_base}{accession})" + if accession: + source_id = accession if (is_deprecated or not link_base) else f"[{accession}]({link_base}{accession})" break row['source'] = source row['source_id'] = source_id