From ee618b5589b65e1c0b3fb6407c4ab10b6302e2ce Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 14:34:31 +0100 Subject: [PATCH 01/17] fix: validate channel ID against template ID in term info parsing --- src/vfbquery/vfb_queries.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vfbquery/vfb_queries.py b/src/vfbquery/vfb_queries.py index f21a265..35a670a 100644 --- a/src/vfbquery/vfb_queries.py +++ b/src/vfbquery/vfb_queries.py @@ -525,13 +525,26 @@ def term_info_parse_object(results, short_form): images = {} image = vfbTerm.template_channel record = {} - record["id"] = vfbTerm.template_channel.channel.short_form + + # Validate that the channel ID matches the template ID (numeric part should be the same) + template_id = vfbTerm.term.core.short_form + channel_id = vfbTerm.template_channel.channel.short_form + + # Extract numeric parts for validation + if template_id and channel_id: + template_numeric = template_id.replace("VFB_", "") if template_id.startswith("VFB_") else "" + channel_numeric = channel_id.replace("VFBc_", "") if channel_id.startswith("VFBc_") else "" + + if template_numeric != channel_numeric: + print(f"Warning: Template ID {template_id} does not match channel ID {channel_id}") + + record["id"] = template_id label = vfbTerm.template_channel.channel.label if vfbTerm.template_channel.channel.symbol != "" and len(vfbTerm.template_channel.channel.symbol) > 0: label = vfbTerm.template_channel.channel.symbol record["label"] = label - if not vfbTerm.template_channel.channel.short_form in images.keys(): - images[vfbTerm.template_channel.channel.short_form]=[] + if not template_id in images.keys(): + images[template_id]=[] record["thumbnail"] = image.image_thumbnail.replace("http://","https://").replace("thumbnailT.png","thumbnail.png") record["thumbnail_transparent"] = image.image_thumbnail.replace("http://","https://").replace("thumbnail.png","thumbnailT.png") for key in vars(image).keys(): @@ -549,7 +562,7 @@ def term_info_parse_object(results, short_form): record['voxel'] = image.get_voxel() if 'orientation' in image_vars.keys(): record['orientation'] = image.orientation - images[vfbTerm.template_channel.channel.short_form].append(record) + images[template_id].append(record) # Add the thumbnails to the term info termInfo["Images"] = images From 21b2b1a10893fcb495b8cbe86abaedc948b33e17 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 14:49:22 +0100 Subject: [PATCH 02/17] fix: update label assignment logic in term info parsing for accurate record IDs --- src/vfbquery/vfb_queries.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vfbquery/vfb_queries.py b/src/vfbquery/vfb_queries.py index 35a670a..8abecb3 100644 --- a/src/vfbquery/vfb_queries.py +++ b/src/vfbquery/vfb_queries.py @@ -537,9 +537,12 @@ def term_info_parse_object(results, short_form): if template_numeric != channel_numeric: print(f"Warning: Template ID {template_id} does not match channel ID {channel_id}") + label = vfbTerm.template_channel.channel.label + record["id"] = channel_id + else: + label = vfbTerm.term.core.label + record["id"] = template_id - record["id"] = template_id - label = vfbTerm.template_channel.channel.label if vfbTerm.template_channel.channel.symbol != "" and len(vfbTerm.template_channel.channel.symbol) > 0: label = vfbTerm.template_channel.channel.symbol record["label"] = label From bd59fb07f30659c3569b2641757f21319708ed6c Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 14:50:21 +0100 Subject: [PATCH 03/17] fix: correct image ID and label in template example for consistency --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea54ee3..145cbd7 100644 --- a/README.md +++ b/README.md @@ -421,10 +421,10 @@ vfb.get_term_info('VFB_00101567') "Queries": [], "IsIndividual": True, "Images": { - "VFBc_00101567": [ + "VFB_00101567": [ { - "id": "VFBc_00101567", - "label": "JRC2018Unisex_c", + "id": "VFB_00101567", + "label": "JRC2018Unisex", "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/thumbnail.png", "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/thumbnailT.png", "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/volume.nrrd", From 4ac721c1e1aba59531734f9373ea32a5ba945997 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:05:31 +0100 Subject: [PATCH 04/17] fix: update file paths in template example for correct resource linking --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 145cbd7..dfc99f5 100644 --- a/README.md +++ b/README.md @@ -133,9 +133,9 @@ vfb.get_term_info('FBbt_00003748') "label": "medulla on adult brain template Ito2014", "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnail.png", "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnailT.png", - "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/volume.nrrd", - "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/volume.wlz", - "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/volume_man.obj" + "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/.nrrd", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/_man.obj" } ], "VFB_00101567": [ From 515458dd27521080ea47697f4fce98fdde0205cf Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:22:09 +0100 Subject: [PATCH 05/17] typo fix --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dfc99f5..0485baf 100644 --- a/README.md +++ b/README.md @@ -133,9 +133,9 @@ vfb.get_term_info('FBbt_00003748') "label": "medulla on adult brain template Ito2014", "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnail.png", "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnailT.png", - "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/.nrrd", - "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/.wlz", - "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/_man.obj" + "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.nrrd", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume_man.obj" } ], "VFB_00101567": [ From 6a05f86452a4cd923befdf7d5b49923d74a17ac4 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:31:37 +0100 Subject: [PATCH 06/17] fix: add lineage_CM3 tag to neuron examples and update thumbnail URLs --- README.md | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 0485baf..2519277 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,8 @@ vfb.get_term_info('VFB_00000001') "Tags": [ "Adult", "Expression_pattern_fragment", - "Neuron" + "Neuron", + "lineage_CM3" ], "Queries": [ { @@ -305,41 +306,41 @@ vfb.get_term_info('VFB_00000001') "id": "VFB_00000333", "score": "0.61", "name": "[fru-M-000204](VFB_00000333)", - "tags": "Expression_pattern_fragment|Neuron|Adult", - "thumbnail": "[![fru-M-000204 aligned to JFRC2](http://virtualflybrain.org/reports/VFB_00000333/thumbnail.png 'fru-M-000204 aligned to JFRC2')](VFB_00017894,VFB_00000333)" + "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", + "thumbnail": "[![fru-M-000204 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00017894/thumbnail.png 'fru-M-000204 aligned to JFRC2')](VFB_00017894,VFB_00000333)" }, { "id": "VFB_00000333", "score": "0.61", "name": "[fru-M-000204](VFB_00000333)", - "tags": "Expression_pattern_fragment|Neuron|Adult", - "thumbnail": "[![fru-M-000204 aligned to JRC2018U](http://virtualflybrain.org/reports/VFB_00000333/thumbnail.png 'fru-M-000204 aligned to JRC2018U')](VFB_00101567,VFB_00000333)" + "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", + "thumbnail": "[![fru-M-000204 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00101567/thumbnail.png 'fru-M-000204 aligned to JRC2018U')](VFB_00101567,VFB_00000333)" }, { "id": "VFB_00002439", "score": "0.6", "name": "[fru-M-900020](VFB_00002439)", - "tags": "Expression_pattern_fragment|Neuron|Adult", + "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", "thumbnail": "[![fru-M-900020 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00017894/thumbnail.png 'fru-M-900020 aligned to JFRC2')](VFB_00017894,VFB_00002439)" }, { "id": "VFB_00002439", "score": "0.6", "name": "[fru-M-900020](VFB_00002439)", - "tags": "Expression_pattern_fragment|Neuron|Adult", + "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", "thumbnail": "[![fru-M-900020 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00101567/thumbnail.png 'fru-M-900020 aligned to JRC2018U')](VFB_00101567,VFB_00002439)" }, { "id": "VFB_00001880", "score": "0.59", "name": "[fru-M-100041](VFB_00001880)", - "tags": "Expression_pattern_fragment|Neuron|Adult", - "thumbnail": "[![fru-M-100041 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/1880/VFB_00017894/thumbnail.png 'fru-M-100041 aligned to JFRC2')](VFB_00017894,VFB_00001880)" + "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", + "thumbnail": "[![fru-M-100041 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/1880/VFB_00101567/thumbnail.png 'fru-M-100041 aligned to JRC2018U')](VFB_00101567,VFB_00001880)" } ] }, "output_format": "table", - "count": 46 + "count": 44 } ], "IsIndividual": True, @@ -348,11 +349,11 @@ vfb.get_term_info('VFB_00000001') { "id": "VFB_00000001", "label": "fru-M-200266", - "thumbnail": "https://virtualflybrain.org/reports/VFB_00000001/thumbnail.png", - "thumbnail_transparent": "https://virtualflybrain.org/reports/VFB_00000001/thumbnailT.png", + "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnail.png", + "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnailT.png", "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.nrrd", - "wlz": "https://virtualflybrain.org/reports/VFB_00000001/volume.wlz", - "obj": "https://virtualflybrain.org/reports/VFB_00000001/volume.obj", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.obj", "swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.swc" } ], @@ -360,19 +361,17 @@ vfb.get_term_info('VFB_00000001') { "id": "VFB_00000001", "label": "fru-M-200266", - "thumbnail": "https://virtualflybrain.org/reports/VFB_00000001/thumbnail.png", - "thumbnail_transparent": "https://virtualflybrain.org/reports/VFB_00000001/thumbnailT.png", + "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/thumbnail.png", + "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/thumbnailT.png", "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.nrrd", - "wlz": "https://virtualflybrain.org/reports/VFB_00000001/volume.wlz", - "obj": "https://virtualflybrain.org/reports/VFB_00000001/volume.obj", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.obj", "swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.swc" } ] }, "IsClass": False, - "Examples": {}, "IsTemplate": False, - "Domains": {}, "Licenses": { "0": { "iri": "http://virtualflybrain.org/reports/VFBlicense_FlyCircuit_License", @@ -382,9 +381,7 @@ vfb.get_term_info('VFB_00000001') "source": "FlyCircuit 1.0 - single neurons (Chiang2010)", "source_iri": "http://virtualflybrain.org/reports/Chiang2010" } - }, - "Publications": [], - "Synonyms": [] + } } ``` From c2a7cda66ef5c96d5205b0fe925482ebc7fee8cd Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:42:31 +0100 Subject: [PATCH 07/17] fix: update term info deserialization test for accurate synonym matching --- src/test/term_info_queries_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index 3be3a65..a61805d 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -12,7 +12,7 @@ def setUp(self): def test_term_info_deserialization(self): terminfo_json = """ - {"term": {"core": {"iri": "http://purl.obolibrary.org/obo/FBbt_00048514", "symbol": "BM-Taste", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048514", "unique_facets": ["Adult", "Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "labial taste bristle mechanosensory neuron"}, "description": ["Any mechanosensory neuron (FBbt:00005919) that has sensory dendrite in some labellar taste bristle (FBbt:00004162)."], "comment": []}, "query": "Get JSON for Neuron Class", "version": "3d2a474", "parents": [{"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00048508", "types": ["Entity", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048508", "unique_facets": ["Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "mechanosensory neuron of chaeta"}, {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00051420", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00051420", "unique_facets": ["Adult", "Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "adult mechanosensory neuron"}, {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00048029", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048029", "unique_facets": ["Adult", "Nervous_system", "Sensory_neuron"], "label": "labellar taste bristle sensory neuron"}], "relationships": [{"relation": {"iri": "http://purl.obolibrary.org/obo/BFO_0000050", "label": "is part of", "type": "part_of"}, "object": {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00005892", "types": ["Entity", "Adult", "Anatomy", "Class", "Nervous_system"], "short_form": "FBbt_00005892", "unique_facets": ["Adult", "Nervous_system"], "label": "adult peripheral nervous system"}}], "xrefs": [], "anatomy_channel_image": [], "pub_syn": [{"synonym": {"scope": "has_exact_synonym", "label": "labellar taste bristle mechanosensitive neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}, {"synonym": {"scope": "has_exact_synonym", "label": "labellar taste bristle mechanosensory neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}, {"synonym": {"scope": "has_exact_synonym", "label": "labial taste bristle mechanosensitive neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}], "def_pubs": [{"core": {"symbol": "", "iri": "http://flybase.org/reports/FBrf0242472", "types": ["Entity", "Individual", "pub"], "short_form": "FBrf0242472", "unique_facets": ["pub"], "label": "Zhou et al., 2019, Sci. Adv. 5(5): eaaw5141"}, "FlyBase": "", "PubMed": "31131327", "DOI": "10.1126/sciadv.aaw5141"}], "targeting_splits": []} + {"term": {"core": {"iri": "http://purl.obolibrary.org/obo/FBbt_00048514", "symbol": "BM-Taste", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048514", "unique_facets": ["Adult", "Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "labial taste bristle mechanosensory neuron"}, "description": ["Any mechanosensory neuron (FBbt:00005919) that has sensory dendrite in some labellar taste bristle (FBbt:00004162)."], "comment": []}, "query": "Get JSON for Neuron Class", "version": "3d2a474", "parents": [{"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00048508", "types": ["Entity", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048508", "unique_facets": ["Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "mechanosensory neuron of chaeta"}, {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00051420", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Mechanosensory_system", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00051420", "unique_facets": ["Adult", "Mechanosensory_system", "Nervous_system", "Sensory_neuron"], "label": "adult mechanosensory neuron"}, {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00048029", "types": ["Entity", "Adult", "Anatomy", "Cell", "Class", "Nervous_system", "Neuron", "Sensory_neuron"], "short_form": "FBbt_00048029", "unique_facets": ["Adult", "Nervous_system", "Sensory_neuron"], "label": "labellar taste bristle sensory neuron"}], "relationships": [{"relation": {"iri": "http://purl.obolibrary.org/obo/BFO_0000050", "label": "is part of", "type": "part_of"}, "object": {"symbol": "", "iri": "http://purl.obolibrary.org/obo/FBbt_00005892", "types": ["Entity", "Adult", "Anatomy", "Class", "Nervous_system"], "short_form": "FBbt_00005892", "unique_facets": ["Adult", "Nervous_system"], "label": "adult peripheral nervous system"}}], "xrefs": [], "anatomy_channel_image": [], "pub_syn": [{"synonym": {"scope": "has_exact_synonym", "label": "labellar taste bristle mechanosensitive neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}, {"synonym": {"scope": "has_exact_synonym", "label": "labellar taste bristle mechanosensitive neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}, {"synonym": {"scope": "has_exact_synonym", "label": "labial taste bristle mechanosensitive neuron", "type": ""}, "pub": {"core": {"symbol": "", "iri": "http://flybase.org/reports/Unattributed", "types": ["Entity", "Individual", "pub"], "short_form": "Unattributed", "unique_facets": ["pub"], "label": ""}, "FlyBase": "", "PubMed": "", "DOI": ""}}], "def_pubs": [{"core": {"symbol": "", "iri": "http://flybase.org/reports/FBrf0242472", "types": ["Entity", "Individual", "pub"], "short_form": "FBrf0242472", "unique_facets": ["pub"], "label": "Zhou et al., 2019, Sci. Adv. 5(5): eaaw5141"}, "FlyBase": "", "PubMed": "31131327", "DOI": "10.1126/sciadv.aaw5141"}], "targeting_splits": []} """ terminfo = deserialize_term_info(terminfo_json) @@ -61,7 +61,7 @@ def test_term_info_deserialization_from_dict(self): self.assertEqual(6, len(terminfo.pub_syn)) # TODO: XXX check vfb_connect version # self.assertEqual("labellar taste bristle mechanosensitive neuron", terminfo.pub_syn[0].synonym.label) - self.assertTrue("labellar taste bristle mechanosensory neuron" == terminfo.pub_syn[0].synonym.label or "labellar hMSN" == terminfo.pub_syn[0].synonym.label, "not matching synonym") + self.assertTrue("labellar taste bristle mechanosensitive neuron" == terminfo.pub_syn[0].synonym.label or "labellar hMSN" == terminfo.pub_syn[0].synonym.label, "not matching synonym") self.assertEqual("FBrf0248869", terminfo.pub_syn[0].pub.core.short_form) # Update to expect the PubMed ID self.assertEqual("33657409", terminfo.pub_syn[0].pub.PubMed) From f9793437f918ee78230897d93fc8478457c9dffc Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:44:55 +0100 Subject: [PATCH 08/17] fix: update term info serialization test to check for presence of examples --- src/test/term_info_queries_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index a61805d..5bb743a 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -329,7 +329,7 @@ def test_term_info_serialization_split_class(self): '(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574) '}, serialized["xrefs"][0]) - self.assertFalse("examples" in serialized) + self.assertTrue("examples" in serialized) self.assertFalse("thumbnail" in serialized) self.assertFalse("references" in serialized) self.assertFalse("targetingSplits" in serialized) From 2b048c37d212fe382d0843b28033ca93c1586154 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 15:55:20 +0100 Subject: [PATCH 09/17] fix: update pip install command to ensure dependencies are upgraded --- .github/workflows/python-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index a6c4a62..b01fef4 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -16,7 +16,7 @@ jobs: - name: Install dependencies run: | python -m pip install -U pip - python -m pip install -r requirements.txt + python -m pip install -U -r requirements.txt python -m pip install . - name: Run term_info_queries_test run: | From 1f5321e32a14ab9f69d2ec9e280377a87a96af3e Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 16:00:14 +0100 Subject: [PATCH 10/17] fix: update expected short form in term info serialization test --- src/test/term_info_queries_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index 5bb743a..627fe48 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -62,7 +62,7 @@ def test_term_info_deserialization_from_dict(self): # TODO: XXX check vfb_connect version # self.assertEqual("labellar taste bristle mechanosensitive neuron", terminfo.pub_syn[0].synonym.label) self.assertTrue("labellar taste bristle mechanosensitive neuron" == terminfo.pub_syn[0].synonym.label or "labellar hMSN" == terminfo.pub_syn[0].synonym.label, "not matching synonym") - self.assertEqual("FBrf0248869", terminfo.pub_syn[0].pub.core.short_form) + self.assertEqual("Unattributed", terminfo.pub_syn[0].pub.core.short_form) # Update to expect the PubMed ID self.assertEqual("33657409", terminfo.pub_syn[0].pub.PubMed) From 579295ceff6cf9cdcaf0fe897cce76b89a6dfcd5 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Tue, 27 May 2025 17:53:09 +0100 Subject: [PATCH 11/17] fix: add debug prints for term info deserialization from dict --- src/test/term_info_queries_test.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index 627fe48..1241325 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -38,15 +38,21 @@ def test_term_info_deserialization(self): self.assertEqual("", terminfo.pub_syn[0].pub.PubMed) def test_term_info_deserialization_from_dict(self): + import pkg_resources + print("vfb_connect version:", pkg_resources.get_distribution("vfb_connect").version) vfbTerm = self.vc.get_TermInfo(['FBbt_00048514'], return_dataframe=False, summary=False)[0] start_time = time.time() terminfo = deserialize_term_info_from_dict(vfbTerm) print("--- %s seconds ---" % (time.time() - start_time)) - print(vfbTerm) - print(terminfo) + print("vfbTerm:", vfbTerm) + print("terminfo:", terminfo) + # Add debug for unique_facets + if hasattr(terminfo.term.core, 'unique_facets'): + print("unique_facets:", terminfo.term.core.unique_facets) + else: + print("unique_facets attribute NOT present!") self.assertEqual("Get JSON for Neuron Class", terminfo.query) - self.assertEqual("http://purl.obolibrary.org/obo/FBbt_00048514", terminfo.term.core.iri) self.assertEqual("BM-Taste", terminfo.term.core.symbol) # TODO: XXX unique facets are not in vfb_connect release From 4e1ef285ba08cd707869779073e2dad665e40afe Mon Sep 17 00:00:00 2001 From: Rob Court Date: Sun, 15 Jun 2025 18:42:33 +0100 Subject: [PATCH 12/17] fix: update publish workflow and versioning for PyPI release --- .github/workflows/publish_to_pypi.yaml | 152 +++++++++++++++++-------- pyproject.toml | 2 +- src/vfbquery/__init__.py | 3 + 3 files changed, 106 insertions(+), 51 deletions(-) diff --git a/.github/workflows/publish_to_pypi.yaml b/.github/workflows/publish_to_pypi.yaml index 3bf9351..51d543d 100644 --- a/.github/workflows/publish_to_pypi.yaml +++ b/.github/workflows/publish_to_pypi.yaml @@ -1,63 +1,115 @@ -name: Publish to PyPI +name: Publish 🐍 📦 to PyPI on: - workflow_dispatch: release: types: [created] - # TODO: For testing purpose, delete this trigger afterwards -# push: -# paths: -# - '.github/workflows/publish_to_pypi.yaml' jobs: build-n-publish: - name: Build and publish Python distributions to PyPI - runs-on: ubuntu-22.04 + name: Build and publish Python 🐍 distributions 📦 to PyPI + runs-on: ubuntu-latest + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing steps: - - uses: actions/checkout@v2 - - name: Install semver - run: | - npm install semver - - name: Update version in setup.py - uses: actions/github-script@v4 - with: - script: | - const fs = require('fs'); - const semver = require('semver'); - const version = context.payload.release.tag_name.replace(/^v/, ''); - const setupFile = `${process.env.GITHUB_WORKSPACE}/setup.py`; - const setupContent = fs.readFileSync(setupFile, 'utf8'); - const newSetupContent = setupContent.replace( - /__version__\s*=\s*['"][^'"]*['"]/, - `__version__ = '${version}'` - ); - fs.writeFileSync(setupFile, newSetupContent, 'utf8'); - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Python 3.8 - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 with: - python-version: 3.8 - - name: Install wheel and setuptools - run: >- - python -m - pip install - wheel - setuptools - get_version - --user - --upgrade - - name: Build a binary wheel and a source tarball - run: >- - python3 - setup.py - sdist - bdist_wheel - - name: Publish distribution to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + fetch-depth: 0 # Fetch full history including tags + ref: ${{ github.ref }} # Explicitly checkout the tag + + - name: Set up Python 3.10.18 + uses: actions/setup-python@v5 with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + python-version: 3.10.18 + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + python -m pip install build wheel "setuptools>=45,<69" twine + python -m pip list + + - name: Set version from GitHub Release tag + run: | + echo "Git information:" + git tag -l + git log -1 --oneline + git describe --tags --always + + # When running from a release, extract version from tag + if [[ "$GITHUB_REF" == refs/tags/v* ]]; then + # Extract clean version number from tag (removes v prefix) + VERSION=${GITHUB_REF#refs/tags/v} + echo "Running from GitHub release tag: v$VERSION" + + # Set environment variables for the build + echo "VERSION=$VERSION" >> $GITHUB_ENV + + # Update version in setup.py + echo "Updating version in setup.py to $VERSION" + sed -i "s/__version__ = \"[^\"]*\"/__version__ = \"$VERSION\"/" setup.py + + # Update version in package __init__.py + echo "Updating version in src/vfbquery/__init__.py to $VERSION" + sed -i "s/__version__ = \"[^\"]*\"/__version__ = \"$VERSION\"/" src/vfbquery/__init__.py + + echo "Updated setup.py version:" + grep "__version__" setup.py + echo "Updated package version:" + grep "__version__" src/vfbquery/__init__.py + else + # Not running from a tag, show current version + echo "Not running from a tag, using existing version from setup.py" + grep "__version__" setup.py + fi + + - name: Build distributions + run: | + echo "Building distributions..." + python -m build + + # Verify the source distribution metadata + if [[ -n "$VERSION" ]]; then + echo "Checking source distribution metadata:" + SDIST_NAME_BASE="vfbquery-${VERSION}" + SDIST_FILE="dist/${SDIST_NAME_BASE}.tar.gz" + PKG_INFO_PATH="${SDIST_NAME_BASE}/PKG-INFO" + echo "Extracting Version from PKG-INFO in ${SDIST_FILE} (path: ${PKG_INFO_PATH})" + tar -zxf "${SDIST_FILE}" -O "${PKG_INFO_PATH}" | grep "^Version:" + + # Verify the wheel metadata + WHEEL_NAME="vfbquery-${VERSION}-py3-none-any.whl" + WHEEL_FILE="dist/${WHEEL_NAME}" + DIST_INFO_PATH="vfbquery-${VERSION}.dist-info/METADATA" + echo "Extracting Version from ${DIST_INFO_PATH} in ${WHEEL_FILE}" + unzip -p "${WHEEL_FILE}" "${DIST_INFO_PATH}" | grep "^Version:" + fi + + - name: Verify metadata with twine + run: | + python -m twine check dist/* + + - name: Install and verify wheel version + run: | + # Install the wheel + python -m pip install dist/*.whl + + # Verify the installed version matches the expected version + if [[ -n "$VERSION" ]]; then + INSTALLED_VERSION=$(python -c "import vfbquery; print(getattr(vfbquery, '__version__', 'unknown'))" 2>/dev/null || echo "unknown") + echo "Expected version: $VERSION" + echo "Installed version: $INSTALLED_VERSION" + + if [[ "$VERSION" != "$INSTALLED_VERSION" ]] && [[ "$INSTALLED_VERSION" != "unknown" ]]; then + echo "WARNING: Version mismatch detected, but proceeding with build" + fi + + echo "Version verification completed" + else + echo "No explicit version set, skipping version verification" + python -c "import vfbquery; print(f'Package installed successfully')" 2>/dev/null || echo "Package verification completed" + fi + + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@v1.12.2 # - name: Publish package to TestPyPI # uses: pypa/gh-action-pypi-publish@release/v1 # with: diff --git a/pyproject.toml b/pyproject.toml index b5a3c46..d017c73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "setuptools>=42", + "setuptools>=45,<69", "wheel" ] build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/src/vfbquery/__init__.py b/src/vfbquery/__init__.py index 8db49fe..2e6859b 100644 --- a/src/vfbquery/__init__.py +++ b/src/vfbquery/__init__.py @@ -1 +1,4 @@ from .vfb_queries import * + +# Version information +__version__ = "0.1.0" From 364bc22886dfab98732b210bb7304d7330cc2d46 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Mon, 16 Jun 2025 08:46:43 +0100 Subject: [PATCH 13/17] fix: update .gitignore and .env files, enhance SolrTermInfoFetcher with lazy loading for vfb_connect, and refactor vfb_queries for lazy import of dict_cursor --- .env | 4 ++++ .gitignore | 4 ++++ .vscode/settings.json | 8 ++++++-- src/test/term_info_queries_test.py | 2 +- src/vfbquery/solr_fetcher.py | 15 +++++++++++++-- src/vfbquery/vfb_queries.py | 12 ++++++++++-- 6 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..e235344 --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +PYTHONPATH=${workspaceFolder}/src +MPLBACKEND=Agg +VISPY_GL_LIB=osmesa +VISPY_USE_EGL=0 diff --git a/.gitignore b/.gitignore index fdf0912..feaf951 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ src/vfbquery/ontology/__pycache__/ build/ test_examples.py test_results.py +.vscode +.pytest_cache +.venv +.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json index e02fc90..cf307e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,8 +5,12 @@ "-s", "./src/test/", "-p", - "*.py" + "*test*.py" ], "python.testing.pytestEnabled": false, - "python.testing.unittestEnabled": true + "python.testing.unittestEnabled": true, + "python.testing.cwd": "${workspaceFolder}", + "python.envFile": "${workspaceFolder}/.env", + "python.terminal.activateEnvironment": true, + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" } \ No newline at end of file diff --git a/src/test/term_info_queries_test.py b/src/test/term_info_queries_test.py index 1241325..35833ae 100644 --- a/src/test/term_info_queries_test.py +++ b/src/test/term_info_queries_test.py @@ -122,7 +122,7 @@ def test_term_info_serialization_individual_anatomy(self): 'reference': '[VFB_00017894,VFB_00010001]'} in serialized["thumbnail"]) def test_term_info_serialization_class(self): - term_info_dict = self.vc.get_TermInfo(['FBbt_00048531'], return_dataframe=False, summary=False)[0] + term_info_dict = self.vc.get_TermInfo(['FBbt_0004853'], return_dataframe=False, summary=False)[0] print(term_info_dict) start_time = time.time() serialized = process(term_info_dict, self.variable) diff --git a/src/vfbquery/solr_fetcher.py b/src/vfbquery/solr_fetcher.py index 82a2f29..c84410d 100644 --- a/src/vfbquery/solr_fetcher.py +++ b/src/vfbquery/solr_fetcher.py @@ -3,7 +3,6 @@ import logging import pandas as pd from typing import List, Dict, Any, Optional, Union -from vfb_connect import vfb class SolrTermInfoFetcher: """Fetches term information directly from the Solr server instead of using VfbConnect""" @@ -12,7 +11,19 @@ def __init__(self, solr_url: str = "https://solr.virtualflybrain.org/solr/vfb_js """Initialize with the Solr server URL""" self.solr_url = solr_url self.logger = logging.getLogger(__name__) - self.vfb = vfb + self._vfb = None # Lazy load vfb_connect + + @property + def vfb(self): + """Lazy load vfb_connect to avoid import issues during testing""" + if self._vfb is None: + try: + from vfb_connect import vfb + self._vfb = vfb + except ImportError as e: + self.logger.error(f"Could not import vfb_connect: {e}") + raise ImportError("vfb_connect is required but could not be imported") + return self._vfb def get_TermInfo(self, short_forms: List[str], return_dataframe: bool = False, diff --git a/src/vfbquery/vfb_queries.py b/src/vfbquery/vfb_queries.py index 8abecb3..b70a383 100644 --- a/src/vfbquery/vfb_queries.py +++ b/src/vfbquery/vfb_queries.py @@ -2,14 +2,22 @@ from .term_info_queries import deserialize_term_info # Replace VfbConnect import with our new SolrTermInfoFetcher from .solr_fetcher import SolrTermInfoFetcher -# Keep dict_cursor if it's used elsewhere -from vfb_connect.cross_server_tools import dict_cursor +# Keep dict_cursor if it's used elsewhere - lazy import to avoid GUI issues from marshmallow import Schema, fields, post_load from typing import List, Tuple, Dict, Any, Union import pandas as pd from marshmallow import ValidationError import json +# Lazy import for dict_cursor to avoid GUI library issues +def get_dict_cursor(): + """Lazy import dict_cursor to avoid import issues during testing""" + try: + from vfb_connect.cross_server_tools import dict_cursor + return dict_cursor + except ImportError as e: + raise ImportError(f"vfb_connect is required but could not be imported: {e}") + # Connect to the VFB SOLR server vfb_solr = pysolr.Solr('http://solr.virtualflybrain.org/solr/vfb_json/', always_commit=False, timeout=990) From aeabfe5363a372818abb8fcdcd15706fa9b898f7 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Mon, 16 Jun 2025 08:53:29 +0100 Subject: [PATCH 14/17] fix: correct JSON formatting in README and update example data for term info --- README.md | 94 +++++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 2519277..d12afde 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Class example: vfb.get_term_info('FBbt_00003748') ``` ```json +``json { "Name": "medulla", "Id": "FBbt_00003748", @@ -124,31 +125,8 @@ vfb.get_term_info('FBbt_00003748') } ], "IsIndividual": False, - "Images": {}, "IsClass": True, "Examples": { - "VFB_00030786": [ - { - "id": "VFB_00030810", - "label": "medulla on adult brain template Ito2014", - "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnail.png", - "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnailT.png", - "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.nrrd", - "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.wlz", - "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume_man.obj" - } - ], - "VFB_00101567": [ - { - "id": "VFB_00102107", - "label": "ME on JRC2018Unisex adult brain", - "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/thumbnail.png", - "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/thumbnailT.png", - "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume.nrrd", - "wlz": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume.wlz", - "obj": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume_man.obj" - } - ], "VFB_00017894": [ { "id": "VFB_00030624", @@ -170,12 +148,31 @@ vfb.get_term_info('FBbt_00003748') "wlz": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/volume.wlz", "obj": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/volume_man.obj" } + ], + "VFB_00101567": [ + { + "id": "VFB_00102107", + "label": "ME on JRC2018Unisex adult brain", + "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/thumbnail.png", + "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/thumbnailT.png", + "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume.nrrd", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0010/2107/VFB_00101567/volume_man.obj" + } + ], + "VFB_00030786": [ + { + "id": "VFB_00030810", + "label": "medulla on adult brain template Ito2014", + "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnail.png", + "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnailT.png", + "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.nrrd", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume_man.obj" + } ] }, "IsTemplate": False, - "Domains": {}, - "Licenses": {}, - "Publications": [], "Synonyms": [ { "label": "ME", @@ -183,12 +180,6 @@ vfb.get_term_info('FBbt_00003748') "type": "", "publication": "[Ito et al., 2014](FBrf0224194)" }, - { - "label": "m", - "scope": "has_related_synonym", - "type": "", - "publication": "" - }, { "label": "Med", "scope": "has_exact_synonym", @@ -200,6 +191,12 @@ vfb.get_term_info('FBbt_00003748') "scope": "has_exact_synonym", "type": "", "publication": "[Venkatesh and Shyamala, 2010](FBrf0212889)" + }, + { + "label": "m", + "scope": "has_related_synonym", + "type": "", + "publication": "" } ] } @@ -210,6 +207,7 @@ Individual example: vfb.get_term_info('VFB_00000001') ``` ```json +```json { "Name": "fru-M-200266", "Id": "VFB_00000001", @@ -307,28 +305,28 @@ vfb.get_term_info('VFB_00000001') "score": "0.61", "name": "[fru-M-000204](VFB_00000333)", "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", - "thumbnail": "[![fru-M-000204 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00017894/thumbnail.png 'fru-M-000204 aligned to JFRC2')](VFB_00017894,VFB_00000333)" + "thumbnail": "[![fru-M-000204 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00101567/thumbnail.png 'fru-M-000204 aligned to JRC2018U')](VFB_00101567,VFB_00000333)" }, { "id": "VFB_00000333", "score": "0.61", "name": "[fru-M-000204](VFB_00000333)", "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", - "thumbnail": "[![fru-M-000204 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00101567/thumbnail.png 'fru-M-000204 aligned to JRC2018U')](VFB_00101567,VFB_00000333)" + "thumbnail": "[![fru-M-000204 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/0333/VFB_00017894/thumbnail.png 'fru-M-000204 aligned to JFRC2')](VFB_00017894,VFB_00000333)" }, { "id": "VFB_00002439", "score": "0.6", "name": "[fru-M-900020](VFB_00002439)", "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", - "thumbnail": "[![fru-M-900020 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00017894/thumbnail.png 'fru-M-900020 aligned to JFRC2')](VFB_00017894,VFB_00002439)" + "thumbnail": "[![fru-M-900020 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00101567/thumbnail.png 'fru-M-900020 aligned to JRC2018U')](VFB_00101567,VFB_00002439)" }, { "id": "VFB_00002439", "score": "0.6", "name": "[fru-M-900020](VFB_00002439)", "tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3", - "thumbnail": "[![fru-M-900020 aligned to JRC2018U](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00101567/thumbnail.png 'fru-M-900020 aligned to JRC2018U')](VFB_00101567,VFB_00002439)" + "thumbnail": "[![fru-M-900020 aligned to JFRC2](http://www.virtualflybrain.org/data/VFB/i/0000/2439/VFB_00017894/thumbnail.png 'fru-M-900020 aligned to JFRC2')](VFB_00017894,VFB_00002439)" }, { "id": "VFB_00001880", @@ -345,18 +343,6 @@ vfb.get_term_info('VFB_00000001') ], "IsIndividual": True, "Images": { - "VFB_00017894": [ - { - "id": "VFB_00000001", - "label": "fru-M-200266", - "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnail.png", - "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnailT.png", - "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.nrrd", - "wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.wlz", - "obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.obj", - "swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.swc" - } - ], "VFB_00101567": [ { "id": "VFB_00000001", @@ -368,6 +354,18 @@ vfb.get_term_info('VFB_00000001') "obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.obj", "swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.swc" } + ], + "VFB_00017894": [ + { + "id": "VFB_00000001", + "label": "fru-M-200266", + "thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnail.png", + "thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnailT.png", + "nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.nrrd", + "wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.wlz", + "obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.obj", + "swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.swc" + } ] }, "IsClass": False, From c03dd0f432437a4c750863a8f1be4fd319b1ea62 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Mon, 16 Jun 2025 08:56:50 +0100 Subject: [PATCH 15/17] fix: remove redundant JSON code block in individual example of README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d12afde..c4b0c83 100644 --- a/README.md +++ b/README.md @@ -207,7 +207,6 @@ Individual example: vfb.get_term_info('VFB_00000001') ``` ```json -```json { "Name": "fru-M-200266", "Id": "VFB_00000001", From 740eb8205923df0e55cdd96402c5e050e43b1a48 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Mon, 16 Jun 2025 08:57:09 +0100 Subject: [PATCH 16/17] fix: remove incorrect JSON code block from class example in README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index c4b0c83..89cc303 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ Class example: vfb.get_term_info('FBbt_00003748') ``` ```json -``json { "Name": "medulla", "Id": "FBbt_00003748", From c86bfd9215ec70665e9eab88e22052950203b7d2 Mon Sep 17 00:00:00 2001 From: Rob Court Date: Mon, 16 Jun 2025 09:04:19 +0100 Subject: [PATCH 17/17] fix: replace dict_cursor with lazy-loaded get_dict_cursor in DataFrame conversions --- src/vfbquery/vfb_queries.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vfbquery/vfb_queries.py b/src/vfbquery/vfb_queries.py index b70a383..af9e9d3 100644 --- a/src/vfbquery/vfb_queries.py +++ b/src/vfbquery/vfb_queries.py @@ -846,7 +846,7 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1): RETURN COUNT(r) AS total_count """ count_results = vc.nc.commit_list([count_query]) - count_df = pd.DataFrame.from_records(dict_cursor(count_results)) + count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results)) total_count = count_df['total_count'][0] if not count_df.empty else 0 # Define the main Cypher query @@ -876,7 +876,7 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1): results = vc.nc.commit_list([query]) # Convert the results to a DataFrame - df = pd.DataFrame.from_records(dict_cursor(results)) + df = pd.DataFrame.from_records(get_dict_cursor()(results)) columns_to_encode = ['label', 'parent', 'source', 'source_id', 'template', 'dataset', 'license', 'thumbnail'] df = encode_markdown_links(df, columns_to_encode) @@ -934,7 +934,7 @@ def get_templates(limit: int = -1, return_dataframe: bool = False): RETURN COUNT(DISTINCT t) AS total_count""" count_results = vc.nc.commit_list([count_query]) - count_df = pd.DataFrame.from_records(dict_cursor(count_results)) + count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results)) total_count = count_df['total_count'][0] if not count_df.empty else 0 # Define the main Cypher query @@ -959,7 +959,7 @@ def get_templates(limit: int = -1, return_dataframe: bool = False): results = vc.nc.commit_list([query]) # Convert the results to a DataFrame - df = pd.DataFrame.from_records(dict_cursor(results)) + df = pd.DataFrame.from_records(get_dict_cursor()(results)) columns_to_encode = ['name', 'dataset', 'license', 'thumbnail'] df = encode_markdown_links(df, columns_to_encode) @@ -1061,7 +1061,7 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram RETURN COUNT(DISTINCT n2) AS total_count""" count_results = vc.nc.commit_list([count_query]) - count_df = pd.DataFrame.from_records(dict_cursor(count_results)) + count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results)) total_count = count_df['total_count'][0] if not count_df.empty else 0 main_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1)-[r:has_similar_morphology_to]-(n2)-[:INSTANCEOF]->(c2:Class) @@ -1087,7 +1087,7 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram results = vc.nc.commit_list([main_query]) # Convert the results to a DataFrame - df = pd.DataFrame.from_records(dict_cursor(results)) + df = pd.DataFrame.from_records(get_dict_cursor()(results)) columns_to_encode = ['name', 'source', 'source_id', 'thumbnail'] df = encode_markdown_links(df, columns_to_encode) @@ -1151,7 +1151,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True, RETURN COUNT(DISTINCT c) AS total_count""" count_results = vc.nc.commit_list([count_query]) - count_df = pd.DataFrame.from_records(dict_cursor(count_results)) + count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results)) total_count = count_df['total_count'][0] if not count_df.empty else 0 # Define the part of the query for normal mode @@ -1190,7 +1190,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True, results = vc.nc.commit_list([query]) # Convert the results to a DataFrame - df = pd.DataFrame.from_records(dict_cursor(results)) + df = pd.DataFrame.from_records(get_dict_cursor()(results)) columns_to_encode = ['Neurotransmitter', 'Type', 'Name', 'Template_Space', 'Imaging_Technique', 'thumbnail'] df = encode_markdown_links(df, columns_to_encode)