@@ -3232,59 +3232,85 @@ def get_neuron_neuron_connectivity(short_form: str, return_dataframe=True, limit
32323232
32333233@with_solr_cache ('neuron_region_connectivity_query' )
32343234def get_neuron_region_connectivity (short_form : str , return_dataframe = True , limit : int = - 1 ):
3235- """
3236- Retrieves brain regions where the specified neuron has synaptic terminals.
3237-
3238- This implements the neuron_region_connectivity_query from the VFB XMI specification .
3239- Query chain (from XMI): Neo4j compound query → process
3240- Matching criteria: Individual + has_region_connectivity
3241-
3242- Uses has_presynaptic_terminals_in and has_postsynaptic_terminal_in relationships
3243- to find brain regions where the neuron makes connections .
3244-
3235+ """Retrieves brain regions where the specified neuron has synaptic terminals.
3236+
3237+ v1.14.12: add Type / Template / Imaging Technique / Thumbnail columns
3238+ matching the AnatomyExpressedIn / NeuronNeuronConnectivityQuery pattern .
3239+ CALL subquery walks (target)<-[:depicts]-(channel)-[in_register_with]->
3240+ (template)-[:depicts]->(template_anat) and (channel)-[:is_specified_output_of]
3241+ ->(technique). LIMIT applied before the CALL fires so the multi-hop walk
3242+ only runs on returned rows. region wrapped as `[label](short_form)`
3243+ markdown so the Brain Region column is clickable .
3244+
32453245 :param short_form: short form of the neuron (Individual)
32463246 :param return_dataframe: Returns pandas dataframe if true, otherwise returns formatted dict
32473247 :param limit: maximum number of results to return (default -1, returns all results)
3248- :return: Brain regions with presynaptic and postsynaptic terminal counts
3248+ :return: Brain regions with presynaptic and postsynaptic terminal counts + image cols
32493249 """
3250- # Build Cypher query based on XMI spec pattern
3250+ limit_clause = f"LIMIT { limit } " if limit != - 1 else ""
32513251 cypher = f"""
3252- MATCH (primary:Individual {{short_form: '{ short_form } '}})
3253- MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_postsynaptic_terminal_in]-(primary)
3254- WITH DISTINCT collect(properties(r)) + {{}} as props, target, primary
3255- WITH apoc.map.removeKeys(apoc.map.merge(props[0], props[1]), ['iri', 'short_form', 'Related', 'label', 'type']) as synapse_counts,
3256- target,
3257- primary
3258- RETURN
3259- target.short_form AS id,
3260- target.label AS region,
3261- synapse_counts.`pre` AS presynaptic_terminals,
3262- synapse_counts.`post` AS postsynaptic_terminals,
3263- target.uniqueFacets AS tags
3252+ MATCH (primary:Individual {{short_form: '{ short_form } '}})
3253+ MATCH (target:Individual)<-[r:has_presynaptic_terminals_in|has_postsynaptic_terminal_in]-(primary)
3254+ WITH DISTINCT collect(properties(r)) + [{{}}] AS props, target, primary
3255+ WITH apoc.map.removeKeys(
3256+ apoc.map.merge(props[0], coalesce(props[1], {{}})),
3257+ ['iri', 'short_form', 'Related', 'label', 'type']
3258+ ) AS synapse_counts,
3259+ target, primary
3260+ OPTIONAL MATCH (target)-[:INSTANCEOF]->(typ:Class)
3261+ WITH target, synapse_counts,
3262+ apoc.text.join(
3263+ collect(DISTINCT coalesce(typ.label, '')),
3264+ '; '
3265+ ) AS type
3266+ ORDER BY target.label
3267+ { limit_clause }
3268+ CALL {{
3269+ WITH target
3270+ OPTIONAL MATCH (target)<-[:depicts]-(channel:Individual)-[irw:in_register_with]->(template:Individual)-[:depicts]->(template_anat:Individual)
3271+ OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class)
3272+ WITH channel, template, template_anat, technique, irw
3273+ LIMIT 1
3274+ RETURN channel, template, template_anat, technique, irw
3275+ }}
3276+ RETURN
3277+ target.short_form AS id,
3278+ apoc.text.format("[%s](%s)", [target.label, target.short_form]) AS region,
3279+ type,
3280+ synapse_counts.`pre` AS presynaptic_terminals,
3281+ synapse_counts.`post` AS postsynaptic_terminals,
3282+ apoc.text.join(coalesce(target.uniqueFacets, []), '|') AS tags,
3283+ REPLACE(apoc.text.format("[%s](%s)", [COALESCE(template_anat.symbol[0], template_anat.label), template_anat.short_form]), '[null](null)', '') AS template,
3284+ coalesce(technique.label, '') AS technique,
3285+ REPLACE(apoc.text.format("[](%s)", [COALESCE(target.symbol[0], coalesce(target.label, 'image')) + " aligned to " + COALESCE(template_anat.symbol[0], template_anat.label), REPLACE(COALESCE(irw.thumbnail[0], ''), 'thumbnailT.png', 'thumbnail.png'), COALESCE(target.symbol[0], coalesce(target.label, 'image')) + " aligned to " + COALESCE(template_anat.symbol[0], template_anat.label), template_anat.short_form + "," + target.short_form]), "[](null)", "") AS thumbnail
32643286 """
3265- if limit != - 1 :
3266- cypher += f" LIMIT { limit } "
32673287
3268- # Run query using Neo4j client
32693288 results = vc .nc .commit_list ([cypher ])
32703289 rows = get_dict_cursor ()(results )
3271-
3272- # Format output
3290+
32733291 if return_dataframe :
32743292 df = pd .DataFrame (rows )
3293+ if not df .empty :
3294+ df = encode_markdown_links (df , ['region' , 'template' , 'thumbnail' ])
32753295 return df
3276-
3277- headers = {
3278- 'id' : {'title' : 'Region ID' , 'type' : 'selection_id' , 'order' : - 1 },
3279- 'region' : {'title' : 'Brain Region' , 'type' : 'markdown' , 'order' : 0 },
3280- 'presynaptic_terminals' : {'title' : 'Presynaptic Terminals' , 'type' : 'number' , 'order' : 1 },
3281- 'postsynaptic_terminals' : {'title' : 'Postsynaptic Terminals' , 'type' : 'number' , 'order' : 2 },
3282- 'tags' : {'title' : 'Region Types' , 'type' : 'list' , 'order' : 3 },
3283- }
3296+
32843297 return {
3285- 'headers' : headers ,
3286- 'rows' : rows ,
3287- 'count' : len (rows )
3298+ 'headers' : {
3299+ 'id' : {'title' : 'Region ID' , 'type' : 'selection_id' , 'order' : - 1 },
3300+ 'region' : {'title' : 'Brain Region' , 'type' : 'markdown' , 'order' : 0 },
3301+ 'type' : {'title' : 'Type' , 'type' : 'text' , 'order' : 1 },
3302+ 'presynaptic_terminals' : {'title' : 'Presynaptic Terminals' , 'type' : 'number' , 'order' : 2 },
3303+ 'postsynaptic_terminals' : {'title' : 'Postsynaptic Terminals' ,'type' : 'number' , 'order' : 3 },
3304+ 'template' : {'title' : 'Template' , 'type' : 'markdown' , 'order' : 4 },
3305+ 'technique' : {'title' : 'Imaging Technique' , 'type' : 'text' , 'order' : 5 },
3306+ 'tags' : {'title' : 'Tags' , 'type' : 'tags' , 'order' : 6 },
3307+ 'thumbnail' : {'title' : 'Thumbnail' , 'type' : 'markdown' , 'order' : 9 },
3308+ },
3309+ 'rows' : [
3310+ {k : row .get (k ) for k in ['id' , 'region' , 'type' , 'presynaptic_terminals' , 'postsynaptic_terminals' , 'template' , 'technique' , 'tags' , 'thumbnail' ]}
3311+ for row in rows
3312+ ],
3313+ 'count' : len (rows ),
32883314 }
32893315
32903316
@@ -4301,7 +4327,7 @@ def get_anatomy_scrnaseq(anatomy_short_form: str, return_dataframe=True, limit:
43014327 MATCH (sub:Class)
43024328 WHERE sub.short_form IN { anat_short_forms !r}
43034329 MATCH (sub)<-[:composed_primarily_of]-(c:Cluster)-[:has_source]->(ds:scRNAseq_DataSet)
4304- WITH DISTINCT primary, c, ds
4330+ WITH DISTINCT primary, c, ds, sub
43054331 OPTIONAL MATCH (ds)-[:has_reference]->(p:pub)
43064332 WITH {{
43074333 short_form: c.short_form,
@@ -4332,11 +4358,12 @@ def get_anatomy_scrnaseq(anatomy_short_form: str, return_dataframe=True, limit:
43324358 FlyBase: coalesce(([]+p.FlyBase)[0], ''),
43334359 DOI: coalesce(([]+p.DOI)[0], '')
43344360 }}) AS pubs,
4335- primary
4361+ primary, sub
43364362 RETURN
43374363 cluster.short_form AS id,
43384364 apoc.text.format("[%s](%s)", [cluster.label, cluster.short_form]) AS name,
43394365 apoc.text.join(cluster.unique_facets, '|') AS tags,
4366+ apoc.text.format("[%s](%s)", [sub.label, sub.short_form]) AS cell_type,
43404367 apoc.text.format("[%s](%s)", [dataset.label, dataset.short_form]) AS dataset,
43414368 pubs
43424369 ORDER BY cluster.label
@@ -4351,22 +4378,23 @@ def get_anatomy_scrnaseq(anatomy_short_form: str, return_dataframe=True, limit:
43514378
43524379 # Encode markdown links
43534380 if not df .empty :
4354- columns_to_encode = ['name' , 'dataset' ]
4381+ columns_to_encode = ['name' , 'cell_type' , ' dataset' ]
43554382 df = encode_markdown_links (df , columns_to_encode )
4356-
4383+
43574384 if return_dataframe :
43584385 return df
43594386 else :
43604387 formatted_results = {
43614388 "headers" : {
4362- "id" : {"title" : "ID" , "type" : "selection_id" , "order" : - 1 },
4363- "name" : {"title" : "Cluster" , "type" : "markdown" , "order" : 0 },
4364- "tags" : {"title" : "Tags" , "type" : "tags" , "order" : 1 },
4365- "dataset" : {"title" : "Dataset" , "type" : "markdown" , "order" : 2 },
4366- "pubs" : {"title" : "Publications" , "type" : "metadata" , "order" : 3 }
4389+ "id" : {"title" : "ID" , "type" : "selection_id" , "order" : - 1 },
4390+ "name" : {"title" : "Cluster" , "type" : "markdown" , "order" : 0 },
4391+ "cell_type" : {"title" : "Cell type" , "type" : "markdown" , "order" : 1 },
4392+ "dataset" : {"title" : "Dataset" , "type" : "markdown" , "order" : 2 },
4393+ "pubs" : {"title" : "Publications" , "type" : "metadata" , "order" : 3 },
4394+ "tags" : {"title" : "Tags" , "type" : "tags" , "order" : 4 }
43674395 },
43684396 "rows" : [
4369- {key : row [key ] for key in ["id" , "name" , "tags " , "dataset" , "pubs" ]}
4397+ {key : row [key ] for key in ["id" , "name" , "cell_type " , "dataset" , "pubs" , "tags " ]}
43704398 for row in safe_to_dict (df , sort_by_id = False )
43714399 ],
43724400 "count" : total_count
@@ -4436,7 +4464,7 @@ def get_cluster_expression(cluster_short_form: str, return_dataframe=True, limit
44364464 apoc.text.join(gene.unique_facets, '|') AS tags,
44374465 expression_level,
44384466 expression_extent,
4439- anatomy
4467+ apoc.text.format("[%s](%s)", [anatomy.label, anatomy.short_form]) AS anatomy
44404468 ORDER BY expression_level DESC, gene.symbol
44414469 """
44424470
@@ -4449,23 +4477,23 @@ def get_cluster_expression(cluster_short_form: str, return_dataframe=True, limit
44494477
44504478 # Encode markdown links
44514479 if not df .empty :
4452- columns_to_encode = ['name' ]
4480+ columns_to_encode = ['name' , 'anatomy' ]
44534481 df = encode_markdown_links (df , columns_to_encode )
4454-
4482+
44554483 if return_dataframe :
44564484 return df
44574485 else :
44584486 formatted_results = {
44594487 "headers" : {
44604488 "id" : {"title" : "ID" , "type" : "selection_id" , "order" : - 1 },
44614489 "name" : {"title" : "Gene" , "type" : "markdown" , "order" : 0 },
4462- "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 1 },
4490+ "anatomy " : {"title" : "Cell type " , "type" : "markdown " , "order" : 1 },
44634491 "expression_level" : {"title" : "Expression Level" , "type" : "numeric" , "order" : 2 },
44644492 "expression_extent" : {"title" : "Expression Extent" , "type" : "numeric" , "order" : 3 },
4465- "anatomy " : {"title" : "Anatomy " , "type" : "metadata " , "order" : 4 }
4493+ "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 4 }
44664494 },
44674495 "rows" : [
4468- {key : row [key ] for key in ["id" , "name" , "tags " , "expression_level" , "expression_extent" , "anatomy " ]}
4496+ {key : row [key ] for key in ["id" , "name" , "anatomy " , "expression_level" , "expression_extent" , "tags " ]}
44694497 for row in safe_to_dict (df , sort_by_id = False )
44704498 ],
44714499 "count" : total_count
@@ -4531,7 +4559,7 @@ def get_expression_cluster(gene_short_form: str, return_dataframe=True, limit: i
45314559 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
45324560 expression_level,
45334561 expression_extent,
4534- anatomy
4562+ apoc.text.format("[%s](%s)", [anatomy.label, anatomy.short_form]) AS anatomy
45354563 ORDER BY expression_level DESC, primary.label
45364564 """
45374565
@@ -4544,23 +4572,23 @@ def get_expression_cluster(gene_short_form: str, return_dataframe=True, limit: i
45444572
45454573 # Encode markdown links
45464574 if not df .empty :
4547- columns_to_encode = ['name' ]
4575+ columns_to_encode = ['name' , 'anatomy' ]
45484576 df = encode_markdown_links (df , columns_to_encode )
4549-
4577+
45504578 if return_dataframe :
45514579 return df
45524580 else :
45534581 formatted_results = {
45544582 "headers" : {
45554583 "id" : {"title" : "ID" , "type" : "selection_id" , "order" : - 1 },
45564584 "name" : {"title" : "Cluster" , "type" : "markdown" , "order" : 0 },
4557- "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 1 },
4585+ "anatomy " : {"title" : "Cell type " , "type" : "markdown " , "order" : 1 },
45584586 "expression_level" : {"title" : "Expression Level" , "type" : "numeric" , "order" : 2 },
45594587 "expression_extent" : {"title" : "Expression Extent" , "type" : "numeric" , "order" : 3 },
4560- "anatomy " : {"title" : "Anatomy " , "type" : "metadata " , "order" : 4 }
4588+ "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 4 }
45614589 },
45624590 "rows" : [
4563- {key : row [key ] for key in ["id" , "name" , "tags " , "expression_level" , "expression_extent" , "anatomy " ]}
4591+ {key : row [key ] for key in ["id" , "name" , "anatomy " , "expression_level" , "expression_extent" , "tags " ]}
45644592 for row in safe_to_dict (df , sort_by_id = False )
45654593 ],
45664594 "count" : total_count
@@ -4627,7 +4655,7 @@ def get_scrnaseq_dataset_data(dataset_short_form: str, return_dataframe=True, li
46274655 c.short_form AS id,
46284656 apoc.text.format("[%s](%s)", [c.label, c.short_form]) AS name,
46294657 apoc.text.join(coalesce(c.uniqueFacets, []), '|') AS tags,
4630- anatomy,
4658+ apoc.text.format("[%s](%s)", [anatomy.label, anatomy.short_form]) AS anatomy,
46314659 pubs
46324660 ORDER BY c.label
46334661 """
@@ -4641,22 +4669,22 @@ def get_scrnaseq_dataset_data(dataset_short_form: str, return_dataframe=True, li
46414669
46424670 # Encode markdown links
46434671 if not df .empty :
4644- columns_to_encode = ['name' ]
4672+ columns_to_encode = ['name' , 'anatomy' ]
46454673 df = encode_markdown_links (df , columns_to_encode )
4646-
4674+
46474675 if return_dataframe :
46484676 return df
46494677 else :
46504678 formatted_results = {
46514679 "headers" : {
46524680 "id" : {"title" : "ID" , "type" : "selection_id" , "order" : - 1 },
46534681 "name" : {"title" : "Cluster" , "type" : "markdown" , "order" : 0 },
4654- "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 1 },
4655- "anatomy " : {"title" : "Anatomy " , "type" : "metadata " , "order" : 2 },
4682+ "anatomy " : {"title" : "Cell type " , "type" : "markdown " , "order" : 1 },
4683+ "tags " : {"title" : "Tags " , "type" : "tags " , "order" : 2 },
46564684 "pubs" : {"title" : "Publications" , "type" : "metadata" , "order" : 3 }
46574685 },
46584686 "rows" : [
4659- {key : row [key ] for key in ["id" , "name" , "tags " , "anatomy " , "pubs" ]}
4687+ {key : row [key ] for key in ["id" , "name" , "anatomy " , "tags " , "pubs" ]}
46604688 for row in safe_to_dict (df , sort_by_id = False )
46614689 ],
46624690 "count" : total_count
0 commit comments