@@ -64,6 +64,29 @@ def get_dict_cursor():
6464# Replace VfbConnect with SimpleVFBConnect
6565vc = SimpleVFBConnect ()
6666
67+ # ---------------------------------------------------------------------------
68+ # Canonical VFB term link
69+ # ---------------------------------------------------------------------------
70+ # Public, environment-independent permalink base for a VFB term. This resolves
71+ # to the term's report page outside the app and is recognised as an internal
72+ # link by the v2 query-results table (MarkdownLinkComponent parses the
73+ # short_form out of the `/reports/<id>` path). Never hard-code an app URL such
74+ # as https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id=<id> into
75+ # query output -- that pins results to one deployment. Build every VFB term
76+ # link from here (or via the helpers below) so the form is defined in one place.
77+ VFB_REPORT_BASE = "https://virtualflybrain.org/reports/"
78+
79+
80+ def vfb_report_url (short_form : str ) -> str :
81+ """Public permalink for a VFB term id, e.g. .../reports/VFB_00101567."""
82+ return f"{ VFB_REPORT_BASE } { short_form } "
83+
84+
85+ def vfb_term_link (label : str , short_form : str ) -> str :
86+ """Markdown link for a VFB term: [label](https://virtualflybrain.org/reports/<id>)."""
87+ return f"[{ label } ]({ vfb_report_url (short_form )} )"
88+
89+
6790def initialize_vfb_connect ():
6891 """
6992 Initialize VFB_connect by triggering the lazy load of the vfb and nc properties.
@@ -4777,7 +4800,7 @@ def get_similar_morphology(neuron_short_form: str, return_dataframe=True, limit:
47774800 }}) END AS types, primary, channel_image, nblast
47784801 RETURN
47794802 primary.short_form AS id,
4780- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
4803+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
47814804 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
47824805 nblast.NBLAST_score[0] AS score,
47834806 types,
@@ -4835,7 +4858,7 @@ def get_similar_morphology_part_of(neuron_short_form: str, return_dataframe=True
48354858 WITH primary, nblast, channel, ri, templ, technique
48364859 OPTIONAL MATCH (primary)-[:INSTANCEOF]->(typ:Class) WITH CASE WHEN typ IS NULL THEN [] ELSE collect({{short_form: typ.short_form, label: coalesce(typ.label, ''), iri: typ.iri, types: labels(typ), symbol: coalesce(typ.symbol[0], '')}}) END AS types, primary, nblast, channel, ri, templ, technique
48374860 RETURN primary.short_form AS id,
4838- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
4861+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
48394862 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
48404863 nblast.NBLAST_score[0] AS score,
48414864 types,
@@ -4866,7 +4889,7 @@ def get_similar_morphology_part_of_exp(expression_short_form: str, return_datafr
48664889 WITH primary, nblast, channel, ri, templ, technique
48674890 OPTIONAL MATCH (primary)-[:INSTANCEOF]->(typ:Class) WITH CASE WHEN typ IS NULL THEN [] ELSE collect({{short_form: typ.short_form, label: coalesce(typ.label, ''), iri: typ.iri, types: labels(typ), symbol: coalesce(typ.symbol[0], '')}}) END AS types, primary, nblast, channel, ri, templ, technique
48684891 RETURN primary.short_form AS id,
4869- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
4892+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
48704893 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
48714894 nblast.NBLAST_score[0] AS score,
48724895 types,
@@ -4897,7 +4920,7 @@ def get_similar_morphology_nb(neuron_short_form: str, return_dataframe=True, lim
48974920 WITH primary, nblast, channel, ri, templ, technique
48984921 OPTIONAL MATCH (primary)-[:INSTANCEOF]->(typ:Class) WITH CASE WHEN typ IS NULL THEN [] ELSE collect({{short_form: typ.short_form, label: coalesce(typ.label, ''), iri: typ.iri, types: labels(typ), symbol: coalesce(typ.symbol[0], '')}}) END AS types, primary, nblast, channel, ri, templ, technique
48994922 RETURN primary.short_form AS id,
4900- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
4923+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
49014924 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
49024925 nblast.neuronbridge_score[0] AS score,
49034926 types,
@@ -4943,7 +4966,7 @@ def get_similar_morphology_nb_exp(expression_short_form: str, return_dataframe=T
49434966 RETURN ri, templ, technique
49444967 }}
49454968 RETURN primary.short_form AS id,
4946- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
4969+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
49474970 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
49484971 nblast.neuronbridge_score[0] AS score,
49494972 type,
@@ -4989,7 +5012,7 @@ def get_painted_domains(template_short_form: str, return_dataframe=True, limit:
49895012 total_count = get_dict_cursor ()(count_results )[0 ]['count' ] if count_results else 0
49905013
49915014 main_query = f"""MATCH (n:Template {{short_form:'{ template_short_form } '}})<-[:depicts]-(:Template)<-[r:in_register_with]-(dc:Individual)-[:depicts]->(di:Individual)-[:INSTANCEOF]->(d:Class) WHERE EXISTS(r.index)
4992- RETURN DISTINCT di.short_form AS id, '[' + di.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + di.short_form + ')' AS name, coalesce(di.description[0], d.description[0]) AS description, COLLECT(DISTINCT d.label) AS type, replace(r.folder[0],'http:','https:') + '/thumbnailT.png' AS thumbnail"""
5015+ RETURN DISTINCT di.short_form AS id, '[' + di.label + ']({ VFB_REPORT_BASE } ' + di.short_form + ')' AS name, coalesce(di.description[0], d.description[0]) AS description, COLLECT(DISTINCT d.label) AS type, replace(r.folder[0],'http:','https:') + '/thumbnailT.png' AS thumbnail"""
49935016 if limit != - 1 : main_query += f" LIMIT { limit } "
49945017
49955018 results = vc .nc .commit_list ([main_query ])
@@ -5143,16 +5166,15 @@ def get_template_roi_tree(template_short_form: str, return_dataframe=False):
51435166 def _node_summary_md (class_id , class_label , painted_list , parent_label ):
51445167 parts = [
51455168 f"**{ class_label or class_id } ** "
5146- f"([ { class_id } ](http://virtualflybrain.org/reports/ { class_id } ) )" ,
5169+ f"({ vfb_term_link ( class_id , class_id ) } )" ,
51475170 "" ,
51485171 ]
51495172 if parent_label :
51505173 parts .append (f"Part of: *{ parent_label } *." )
51515174 parts .append ("" )
51525175 if painted_list :
51535176 ind_lines = [
5154- f"[{ p .get ('individual_label' ) or p .get ('individual_id' )} ]"
5155- f"(http://virtualflybrain.org/reports/{ p .get ('individual_id' )} )"
5177+ vfb_term_link (p .get ('individual_label' ) or p .get ('individual_id' ), p .get ('individual_id' ))
51565178 for p in painted_list if p .get ('individual_id' )
51575179 ]
51585180 if ind_lines :
@@ -5190,7 +5212,7 @@ def _build(node_id, ancestors, parent_label):
51905212 summary_md = (
51915213 f"## ROI tree for **{ template_label } **\n \n "
51925214 f"Anatomy root: "
5193- f"[ { root_label or root_id } ](http://virtualflybrain.org/reports/ { root_id } ) "
5215+ f"{ vfb_term_link ( root_label or root_id , root_id ) } "
51945216 f"({ root_id } ).\n \n "
51955217 f"{ painted_class_count } painted region"
51965218 f"{ 's' if painted_class_count != 1 else '' } "
@@ -5260,7 +5282,7 @@ def get_dataset_images(dataset_short_form: str, return_dataframe=True, limit: in
52605282 OPTIONAL MATCH (primary)-[:INSTANCEOF]->(typ:Class)
52615283 OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class)
52625284 RETURN primary.short_form AS id,
5263- '[' + primary.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + primary.short_form + ')' AS name,
5285+ '[' + primary.label + ']({ VFB_REPORT_BASE } ' + primary.short_form + ')' AS name,
52645286 apoc.text.join(coalesce(primary.uniqueFacets, []), '|') AS tags,
52655287 typ.label AS type,
52665288 REPLACE(apoc.text.format("[%s](%s)",[COALESCE(template.symbol[0],template.label),template.short_form]), '[null](null)', '') AS template,
@@ -5286,7 +5308,7 @@ def get_all_aligned_images(template_short_form: str, return_dataframe=True, limi
52865308 OPTIONAL MATCH (di)-[:INSTANCEOF]->(typ:Class)
52875309 OPTIONAL MATCH (channel)-[:is_specified_output_of]->(technique:Class)
52885310 RETURN DISTINCT di.short_form AS id,
5289- '[' + di.label + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + di.short_form + ')' AS name,
5311+ '[' + di.label + ']({ VFB_REPORT_BASE } ' + di.short_form + ')' AS name,
52905312 apoc.text.join(coalesce(di.uniqueFacets, []), '|') AS tags,
52915313 typ.label AS type,
52925314 REPLACE(apoc.text.format("[%s](%s)",[COALESCE(templ.symbol[0],templ.label),templ.short_form]), '[null](null)', '') AS template,
@@ -5358,7 +5380,7 @@ def _dataset_return_clause(ds_var: str = "ds") -> str:
53585380 return f"""
53595381 RETURN
53605382 { ds_var } .short_form AS id,
5361- '[' + coalesce({ ds_var } .label, { ds_var } .short_form) + '](https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id= ' + { ds_var } .short_form + ')' AS name,
5383+ '[' + coalesce({ ds_var } .label, { ds_var } .short_form) + ']({ VFB_REPORT_BASE } ' + { ds_var } .short_form + ')' AS name,
53625384 pubs,
53635385 apoc.text.join(coalesce({ ds_var } .uniqueFacets, []), '|') AS tags,
53645386 license,
@@ -6288,7 +6310,7 @@ def build(node_id, depth):
62886310 # Render display text and HTML
62896311 # ------------------------------------------------------------------
62906312
6291- VFB_BASE = 'https://v2.virtualflybrain.org/org.geppetto.frontend/geppetto?id='
6313+ VFB_BASE = VFB_REPORT_BASE
62926314 DEFAULT_MAX_SIBLINGS = 10 # truncate large sibling groups in text display
62936315
62946316 def _text_tree (node , prefix = '' , is_last = True , is_root = True , max_siblings = DEFAULT_MAX_SIBLINGS ):
0 commit comments