Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/test/test_publication_transgene_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ def test_get_terms_for_pub_formatted(self):
self.assertIn('rows', result)
self.assertTrue(result['rows'], f"{self.pub_term} should have referenced terms")

def test_terms_for_pub_includes_classes(self):
"""Ontology classes citing a pub (definition/synonym refs) are listed.

Li et al. 2020 (FBrf0248215, mushroom body connectome) is cited only
by FBbt classes; matching just Individuals returned 0 rows.
"""
result = get_terms_for_pub('FBrf0248215', return_dataframe=False)
rows = {r['id']: r for r in result['rows']}
self.assertIn('FBbt_00005801', rows, "mushroom body cites FBrf0248215 for its definition")
mb = rows['FBbt_00005801']
self.assertIn('Definition', mb['reference_type'])
self.assertIn('FBbt_00041000', mb['type'], "Class rows show their SUBCLASSOF parent(s) as type")
self.assertNotIn('[null]', mb['type'])

def test_terms_for_pub_keeps_individuals(self):
"""Individual (DataSet) references survive alongside Class ones."""
result = get_terms_for_pub(self.pub_term, return_dataframe=False)
ids = {r['id'] for r in result['rows']}
self.assertIn('Aso2014', ids, "the Aso2014 DataSet references FBrf0227179")
self.assertTrue(any(i.startswith('FBbt_') for i in ids), "FBbt classes also cite FBrf0227179")

def test_get_transgene_expression_here(self):
"""Test get_transgene_expression_here query"""
result = get_transgene_expression_here(self.anatomy_term, return_dataframe=True, limit=10)
Expand Down
48 changes: 36 additions & 12 deletions src/vfbquery/vfb_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -7331,9 +7331,10 @@ def get_terms_for_pub(pub_short_form: str, return_dataframe=True, limit: int = -
"""
# A publication is cited two different ways in the graph, and the legacy
# TermsForPub only saw the first:
# 1. Reference — a term has a direct (:term)-[:has_reference]->(:pub)
# edge (datasets, images, anatomy the paper is the
# source/citation for).
# 1. Reference — a term (Individual or Class) has a direct
# (:term)-[:has_reference]->(:pub) edge: datasets and
# images the paper is the source for, and ontology
# classes citing it for a definition or synonym.
# 2. Expression — the pub is recorded as a `pub` array PROPERTY on an
# overlaps/part_of relationship of an expression-pattern
# individual (the same model AnatomyExpressedIn /
Expand All @@ -7346,26 +7347,49 @@ def get_terms_for_pub(pub_short_form: str, return_dataframe=True, limit: int = -
# the relationship `pub` property (no node path exists — the pub node has
# no edges), so it is the expensive leg; the whole query is cached.

# Source 1: direct has_reference terms, with image enrichment for
# channel-image primaries (one representative image via the CALL).
# Source 1: direct has_reference terms. Both Individuals (datasets,
# images, neurons cited for a synonym) AND Classes carry these edges —
# FBbt classes cite papers for their definitions and synonyms, and for
# anatomy papers (e.g. Li et al. 2020, FBrf0248215, the mushroom body
# connectome) every citing term is a Class. Matching only
# `primary:Individual` returned 0 for such papers.
#
# Each edge's `typ` says why the term cites the pub ('def', 'syn', 'nlp';
# null on DataSet references) and feeds the "Reference type" column.
# Type is the INSTANCEOF class for an Individual, the direct SUBCLASSOF
# parent(s) for a Class. The image enrichment only ever matches
# channel-image Individuals; Classes leave those cells blank.
# Obsolete (Deprecated) terms are excluded.
ref_query = f"""
MATCH (:pub:Individual {{short_form:'{pub_short_form}'}})<-[:has_reference]-(primary:Individual)
OPTIONAL MATCH (primary)-[:INSTANCEOF]->(typ:Class)
WITH DISTINCT primary, typ
MATCH (:pub:Individual {{short_form:'{pub_short_form}'}})<-[ref:has_reference]-(primary)
WHERE (primary:Individual OR primary:Class) AND NOT primary:Deprecated
WITH primary, collect(DISTINCT coalesce(ref.typ, '')) AS ref_typs
CALL {{
WITH primary
OPTIONAL MATCH (primary)-[:INSTANCEOF|SUBCLASSOF]->(typ:Class)
WITH typ ORDER BY typ.label
RETURN collect(CASE WHEN typ IS NULL THEN null ELSE apoc.text.format("[%s](%s)", [typ.label, typ.short_form]) END) AS types
}}
CALL {{
WITH primary
OPTIONAL MATCH (primary)-[irw:in_register_with]->(template:Individual)-[:depicts]->(template_anat:Individual)
OPTIONAL MATCH (primary)-[:is_specified_output_of]->(technique:Class)
WITH primary, template, template_anat, technique, irw
WITH template, template_anat, technique, irw
LIMIT 1
RETURN template, template_anat, technique, irw
}}
RETURN
primary.short_form AS id,
apoc.text.format("[%s](%s)", [primary.label, primary.short_form]) AS name,
apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
REPLACE(apoc.text.format("[%s](%s)", [typ.label, typ.short_form]), '[null](null)', '') AS type,
'Reference' AS reference_type,
apoc.text.join(types, '; ') AS type,
apoc.text.join(
apoc.coll.sort(apoc.coll.toSet(
[t IN ref_typs | CASE t WHEN 'def' THEN 'Definition'
WHEN 'syn' THEN 'Synonym'
WHEN 'nlp' THEN 'Text-mined'
ELSE 'Reference' END]
)), '; ') AS reference_type,
REPLACE(apoc.text.format("[%s](%s)", [CASE WHEN template_anat.symbol[0] <> '' THEN template_anat.symbol[0] ELSE template_anat.label END, template_anat.short_form]), '[null](null)', '') AS template,
coalesce(technique.label, '') AS technique,
REPLACE(apoc.text.format("[![%s](%s '%s')](%s)", [coalesce(primary.label, 'image') + " aligned to " + CASE WHEN template_anat.symbol[0] <> '' THEN template_anat.symbol[0] ELSE template_anat.label END, REPLACE(REPLACE(COALESCE(irw.thumbnail[0], ''), 'thumbnailT.png', 'thumbnail.png'),'http://','https://'), coalesce(primary.label, 'image') + " aligned to " + CASE WHEN template_anat.symbol[0] <> '' THEN template_anat.symbol[0] ELSE template_anat.label END, template_anat.short_form + "," + primary.short_form]), "[![null]( 'null')](null)", "") AS thumbnail
Expand Down Expand Up @@ -7399,7 +7423,7 @@ def get_terms_for_pub(pub_short_form: str, return_dataframe=True, limit: int = -
'name': 'first',
'tags': 'first',
'type': 'first',
'reference_type': lambda s: '; '.join(sorted({x for x in s if x})),
'reference_type': lambda s: '; '.join(sorted({p for x in s if x for p in x.split('; ')})),
'template': 'first',
'technique': 'first',
'thumbnail': 'first',
Expand Down
Loading