vfbquery 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- test/term_info_queries_test.py +34 -20
- vfbquery/__init__.py +3 -0
- vfbquery/solr_fetcher.py +13 -2
- vfbquery/term_info_queries.py +18 -2
- vfbquery/test_utils.py +110 -3
- vfbquery/vfb_queries.py +96 -23
- {vfbquery-0.3.2.dist-info → vfbquery-0.3.4.dist-info}/METADATA +54 -62
- vfbquery-0.3.4.dist-info/RECORD +14 -0
- {vfbquery-0.3.2.dist-info → vfbquery-0.3.4.dist-info}/WHEEL +1 -1
- vfbquery-0.3.2.dist-info/RECORD +0 -14
- {vfbquery-0.3.2.dist-info → vfbquery-0.3.4.dist-info}/LICENSE +0 -0
- {vfbquery-0.3.2.dist-info → vfbquery-0.3.4.dist-info}/top_level.txt +0 -0
test/term_info_queries_test.py
CHANGED
|
@@ -12,7 +12,7 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
12
12
|
|
|
13
13
|
def test_term_info_deserialization(self):
|
|
14
14
|
terminfo_json = """
|
|
15
|
-
{"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
|
|
15
|
+
{"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": []}
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
terminfo = deserialize_term_info(terminfo_json)
|
|
@@ -38,15 +38,21 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
38
38
|
self.assertEqual("", terminfo.pub_syn[0].pub.PubMed)
|
|
39
39
|
|
|
40
40
|
def test_term_info_deserialization_from_dict(self):
|
|
41
|
+
import pkg_resources
|
|
42
|
+
print("vfb_connect version:", pkg_resources.get_distribution("vfb_connect").version)
|
|
41
43
|
vfbTerm = self.vc.get_TermInfo(['FBbt_00048514'], return_dataframe=False, summary=False)[0]
|
|
42
44
|
start_time = time.time()
|
|
43
45
|
terminfo = deserialize_term_info_from_dict(vfbTerm)
|
|
44
46
|
print("--- %s seconds ---" % (time.time() - start_time))
|
|
45
|
-
print(vfbTerm)
|
|
46
|
-
print(terminfo)
|
|
47
|
+
print("vfbTerm:", vfbTerm)
|
|
48
|
+
print("terminfo:", terminfo)
|
|
49
|
+
# Add debug for unique_facets
|
|
50
|
+
if hasattr(terminfo.term.core, 'unique_facets'):
|
|
51
|
+
print("unique_facets:", terminfo.term.core.unique_facets)
|
|
52
|
+
else:
|
|
53
|
+
print("unique_facets attribute NOT present!")
|
|
47
54
|
|
|
48
55
|
self.assertEqual("Get JSON for Neuron Class", terminfo.query)
|
|
49
|
-
|
|
50
56
|
self.assertEqual("http://purl.obolibrary.org/obo/FBbt_00048514", terminfo.term.core.iri)
|
|
51
57
|
self.assertEqual("BM-Taste", terminfo.term.core.symbol)
|
|
52
58
|
# TODO: XXX unique facets are not in vfb_connect release
|
|
@@ -58,13 +64,24 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
58
64
|
|
|
59
65
|
self.assertEqual(0, len(terminfo.xrefs))
|
|
60
66
|
|
|
61
|
-
self.assertEqual(
|
|
62
|
-
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
self.assertEqual(7, len(terminfo.pub_syn))
|
|
68
|
+
|
|
69
|
+
# Check that we have the expected synonym labels (order-independent)
|
|
70
|
+
synonym_labels = [entry.synonym.label for entry in terminfo.pub_syn]
|
|
71
|
+
expected_labels = ["labellar taste bristle mechanosensitive neuron", "labellar hMSN", "labial taste bristle mechanosensory neuron"]
|
|
72
|
+
|
|
73
|
+
# Check that at least one of the expected labels exists
|
|
74
|
+
found_labels = [label for label in expected_labels if label in synonym_labels]
|
|
75
|
+
self.assertTrue(len(found_labels) > 0, f"None of the expected synonym labels found. Found: {synonym_labels}")
|
|
76
|
+
|
|
77
|
+
# Check that entries with "Unattributed" pub exist (most entries should have this)
|
|
78
|
+
unattributed_entries = [entry for entry in terminfo.pub_syn if entry.pub.core.short_form == "Unattributed"]
|
|
79
|
+
self.assertTrue(len(unattributed_entries) > 0, "No entries with 'Unattributed' pub found")
|
|
80
|
+
|
|
81
|
+
# Check for the PubMed ID in the correct synonym entry (labellar hMSN)
|
|
82
|
+
labellar_hmsn_entry = next((entry for entry in terminfo.pub_syn if entry.synonym.label == "labellar hMSN"), None)
|
|
83
|
+
self.assertIsNotNone(labellar_hmsn_entry, "labellar hMSN entry not found")
|
|
84
|
+
self.assertEqual("33657409", labellar_hmsn_entry.pub.PubMed)
|
|
68
85
|
|
|
69
86
|
def test_term_info_serialization_individual_anatomy(self):
|
|
70
87
|
term_info_dict = self.vc.get_TermInfo(['VFB_00010001'], return_dataframe=False, summary=False)[0]
|
|
@@ -128,7 +145,7 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
128
145
|
self.assertFalse("link" in serialized)
|
|
129
146
|
self.assertEqual(4, len(serialized["types"]))
|
|
130
147
|
self.assertTrue("Anatomy" in serialized["types"])
|
|
131
|
-
self.assertEqual("Cyst composed of two cyst cells following the division of a newly-formed cystoblast in the germarium. The two cells are connected by a cytoplasmic bridge.\n([
|
|
148
|
+
self.assertEqual("Cyst composed of two cyst cells following the division of a newly-formed cystoblast in the germarium. The two cells are connected by a cytoplasmic bridge.\n([King, 1970](FBrf0021038))", serialized["description"])
|
|
132
149
|
self.assertTrue("synonyms" in serialized)
|
|
133
150
|
self.assertEqual(1, len(serialized["synonyms"]))
|
|
134
151
|
self.assertEqual("has_exact_synonym: germarial 2-cell cluster ([King, 1970](FBrf0021038))", serialized["synonyms"][0])
|
|
@@ -149,13 +166,10 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
149
166
|
self.assertFalse("examples" in serialized)
|
|
150
167
|
self.assertFalse("thumbnail" in serialized)
|
|
151
168
|
self.assertTrue("references" in serialized)
|
|
152
|
-
self.assertEqual(
|
|
153
|
-
self.assertEqual({'link': '[Spradling, 1993, Bate, Martinez Arias, 1993: 1--70](FBrf0064777)',
|
|
154
|
-
'refs': ['http://flybase.org/reports/FBrf0064777'],
|
|
155
|
-
'types': ' pub'}, serialized["references"][0])
|
|
169
|
+
self.assertEqual(1, len(serialized["references"]))
|
|
156
170
|
self.assertEqual({'link': '[King, 1970, Ovarian Development in Drosophila melanogaster. ](FBrf0021038)',
|
|
157
171
|
'refs': ['http://flybase.org/reports/FBrf0021038'],
|
|
158
|
-
'types': ' pub'}, serialized["references"][
|
|
172
|
+
'types': ' pub'}, serialized["references"][0])
|
|
159
173
|
self.assertFalse("targetingSplits" in serialized)
|
|
160
174
|
self.assertFalse("targetingNeurons" in serialized)
|
|
161
175
|
|
|
@@ -244,7 +258,7 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
244
258
|
self.assertTrue("Turner-Evans et al., 2020" in description)
|
|
245
259
|
|
|
246
260
|
self.assertTrue("synonyms" in serialized)
|
|
247
|
-
self.assertEqual(
|
|
261
|
+
self.assertEqual(10, len(serialized["synonyms"]))
|
|
248
262
|
print(serialized["synonyms"][0])
|
|
249
263
|
self.assertTrue("has_exact_synonym: EB-PB 1 glomerulus-D/Vgall neuron" in serialized["synonyms"])
|
|
250
264
|
self.assertFalse("source" in serialized)
|
|
@@ -276,7 +290,7 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
276
290
|
self.assertFalse("thumbnail" in serialized)
|
|
277
291
|
|
|
278
292
|
self.assertTrue("references" in serialized)
|
|
279
|
-
self.assertEqual(
|
|
293
|
+
self.assertEqual(7, len(serialized["references"]))
|
|
280
294
|
|
|
281
295
|
self.assertTrue("targetingSplits" in serialized)
|
|
282
296
|
self.assertEqual(6, len(serialized["targetingSplits"]))
|
|
@@ -329,7 +343,7 @@ class TermInfoQueriesTest(unittest.TestCase):
|
|
|
329
343
|
'(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574) '},
|
|
330
344
|
serialized["xrefs"][0])
|
|
331
345
|
|
|
332
|
-
self.
|
|
346
|
+
self.assertTrue("examples" in serialized)
|
|
333
347
|
self.assertFalse("thumbnail" in serialized)
|
|
334
348
|
self.assertFalse("references" in serialized)
|
|
335
349
|
self.assertFalse("targetingSplits" in serialized)
|
vfbquery/__init__.py
CHANGED
vfbquery/solr_fetcher.py
CHANGED
|
@@ -3,7 +3,6 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import pandas as pd
|
|
5
5
|
from typing import List, Dict, Any, Optional, Union
|
|
6
|
-
from vfb_connect import vfb
|
|
7
6
|
|
|
8
7
|
class SolrTermInfoFetcher:
|
|
9
8
|
"""Fetches term information directly from the Solr server instead of using VfbConnect"""
|
|
@@ -12,7 +11,19 @@ class SolrTermInfoFetcher:
|
|
|
12
11
|
"""Initialize with the Solr server URL"""
|
|
13
12
|
self.solr_url = solr_url
|
|
14
13
|
self.logger = logging.getLogger(__name__)
|
|
15
|
-
self.
|
|
14
|
+
self._vfb = None # Lazy load vfb_connect
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def vfb(self):
|
|
18
|
+
"""Lazy load vfb_connect to avoid import issues during testing"""
|
|
19
|
+
if self._vfb is None:
|
|
20
|
+
try:
|
|
21
|
+
from vfb_connect import vfb
|
|
22
|
+
self._vfb = vfb
|
|
23
|
+
except ImportError as e:
|
|
24
|
+
self.logger.error(f"Could not import vfb_connect: {e}")
|
|
25
|
+
raise ImportError("vfb_connect is required but could not be imported")
|
|
26
|
+
return self._vfb
|
|
16
27
|
|
|
17
28
|
def get_TermInfo(self, short_forms: List[str],
|
|
18
29
|
return_dataframe: bool = False,
|
vfbquery/term_info_queries.py
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import json
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
# Custom JSON encoder to handle NumPy and pandas types
|
|
6
|
+
class NumpyEncoder(json.JSONEncoder):
|
|
7
|
+
def default(self, obj):
|
|
8
|
+
if isinstance(obj, np.integer):
|
|
9
|
+
return int(obj)
|
|
10
|
+
elif isinstance(obj, np.floating):
|
|
11
|
+
return float(obj)
|
|
12
|
+
elif isinstance(obj, np.ndarray):
|
|
13
|
+
return obj.tolist()
|
|
14
|
+
elif isinstance(obj, np.bool_):
|
|
15
|
+
return bool(obj)
|
|
16
|
+
elif hasattr(obj, 'item'): # Handle pandas scalar types
|
|
17
|
+
return obj.item()
|
|
18
|
+
return super(NumpyEncoder, self).default(obj)
|
|
3
19
|
import requests
|
|
4
20
|
from dataclasses import dataclass
|
|
5
21
|
from dataclasses_json import dataclass_json
|
|
@@ -15,7 +31,7 @@ class Coordinates:
|
|
|
15
31
|
Z: float
|
|
16
32
|
|
|
17
33
|
def __str__(self):
|
|
18
|
-
return json.dumps([str(self.X), str(self.Y), str(self.Z)])
|
|
34
|
+
return json.dumps([str(self.X), str(self.Y), str(self.Z)], cls=NumpyEncoder)
|
|
19
35
|
|
|
20
36
|
|
|
21
37
|
class CoordinatesFactory:
|
|
@@ -1062,7 +1078,7 @@ def serialize_term_info_to_json(vfb_term: VfbTerminfo, show_types=False) -> str:
|
|
|
1062
1078
|
:return: json string representation of the term info object
|
|
1063
1079
|
"""
|
|
1064
1080
|
term_info_dict = serialize_term_info_to_dict(vfb_term, show_types)
|
|
1065
|
-
return json.dumps(term_info_dict, indent=4)
|
|
1081
|
+
return json.dumps(term_info_dict, indent=4, cls=NumpyEncoder)
|
|
1066
1082
|
|
|
1067
1083
|
|
|
1068
1084
|
def process(term_info_response: dict, variable, loaded_template: Optional[str] = None, show_types=False) -> dict:
|
vfbquery/test_utils.py
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
+
import json
|
|
3
|
+
import numpy as np
|
|
2
4
|
from typing import Any, Dict, Union
|
|
3
5
|
|
|
6
|
+
# Custom JSON encoder to handle NumPy and pandas types
|
|
7
|
+
class NumpyEncoder(json.JSONEncoder):
|
|
8
|
+
def default(self, obj):
|
|
9
|
+
if isinstance(obj, np.integer):
|
|
10
|
+
return int(obj)
|
|
11
|
+
elif isinstance(obj, np.floating):
|
|
12
|
+
return float(obj)
|
|
13
|
+
elif isinstance(obj, np.ndarray):
|
|
14
|
+
return obj.tolist()
|
|
15
|
+
elif isinstance(obj, np.bool_):
|
|
16
|
+
return bool(obj)
|
|
17
|
+
elif hasattr(obj, 'item'): # Handle pandas scalar types
|
|
18
|
+
return obj.item()
|
|
19
|
+
return super(NumpyEncoder, self).default(obj)
|
|
20
|
+
|
|
21
|
+
def safe_to_dict(df, sort_by_id=True):
|
|
22
|
+
"""Convert DataFrame to dict with numpy types converted to native Python types"""
|
|
23
|
+
if isinstance(df, pd.DataFrame):
|
|
24
|
+
# Convert numpy dtypes to native Python types
|
|
25
|
+
df_copy = df.copy()
|
|
26
|
+
for col in df_copy.columns:
|
|
27
|
+
if df_copy[col].dtype.name.startswith('int'):
|
|
28
|
+
df_copy[col] = df_copy[col].astype('object')
|
|
29
|
+
elif df_copy[col].dtype.name.startswith('float'):
|
|
30
|
+
df_copy[col] = df_copy[col].astype('object')
|
|
31
|
+
|
|
32
|
+
# Sort by id column in descending order if it exists and sort_by_id is True
|
|
33
|
+
if sort_by_id and 'id' in df_copy.columns:
|
|
34
|
+
df_copy = df_copy.sort_values('id', ascending=False)
|
|
35
|
+
|
|
36
|
+
return df_copy.to_dict("records")
|
|
37
|
+
return df
|
|
38
|
+
|
|
4
39
|
def safe_extract_row(result: Any, index: int = 0) -> Dict:
|
|
5
40
|
"""
|
|
6
41
|
Safely extract a row from a pandas DataFrame or return the object itself if not a DataFrame.
|
|
@@ -11,11 +46,83 @@ def safe_extract_row(result: Any, index: int = 0) -> Dict:
|
|
|
11
46
|
"""
|
|
12
47
|
if isinstance(result, pd.DataFrame):
|
|
13
48
|
if not result.empty and len(result.index) > index:
|
|
14
|
-
|
|
49
|
+
# Convert to dict using safe method to handle numpy types
|
|
50
|
+
row_series = result.iloc[index]
|
|
51
|
+
return {col: (val.item() if hasattr(val, 'item') else val) for col, val in row_series.items()}
|
|
15
52
|
else:
|
|
16
53
|
return {}
|
|
17
54
|
return result
|
|
18
55
|
|
|
56
|
+
def sanitize_for_json(obj: Any) -> Any:
|
|
57
|
+
"""
|
|
58
|
+
Recursively sanitize any data structure to make it JSON serializable.
|
|
59
|
+
Converts numpy types, pandas types, and other non-serializable types to native Python types.
|
|
60
|
+
|
|
61
|
+
:param obj: Object to sanitize
|
|
62
|
+
:return: JSON-serializable version of the object
|
|
63
|
+
"""
|
|
64
|
+
if isinstance(obj, dict):
|
|
65
|
+
return {key: sanitize_for_json(value) for key, value in obj.items()}
|
|
66
|
+
elif isinstance(obj, (list, tuple)):
|
|
67
|
+
return [sanitize_for_json(item) for item in obj]
|
|
68
|
+
elif isinstance(obj, np.integer):
|
|
69
|
+
return int(obj)
|
|
70
|
+
elif isinstance(obj, np.floating):
|
|
71
|
+
return float(obj)
|
|
72
|
+
elif isinstance(obj, np.ndarray):
|
|
73
|
+
return obj.tolist()
|
|
74
|
+
elif isinstance(obj, np.bool_):
|
|
75
|
+
return bool(obj)
|
|
76
|
+
elif hasattr(obj, 'item'): # Handle pandas scalar types
|
|
77
|
+
return obj.item()
|
|
78
|
+
elif isinstance(obj, pd.DataFrame):
|
|
79
|
+
return safe_to_dict(obj)
|
|
80
|
+
elif hasattr(obj, '__dict__'): # Handle custom objects
|
|
81
|
+
return sanitize_for_json(obj.__dict__)
|
|
82
|
+
else:
|
|
83
|
+
return obj
|
|
84
|
+
|
|
85
|
+
def safe_json_dumps(obj: Any, **kwargs) -> str:
|
|
86
|
+
"""
|
|
87
|
+
Safely serialize any object to JSON string, handling numpy and pandas types.
|
|
88
|
+
|
|
89
|
+
:param obj: Object to serialize
|
|
90
|
+
:param kwargs: Additional arguments to pass to json.dumps
|
|
91
|
+
:return: JSON string
|
|
92
|
+
"""
|
|
93
|
+
# Set default arguments
|
|
94
|
+
default_kwargs = {'indent': 2, 'ensure_ascii': False, 'cls': NumpyEncoder}
|
|
95
|
+
default_kwargs.update(kwargs)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# First try with the NumpyEncoder
|
|
99
|
+
return json.dumps(obj, **default_kwargs)
|
|
100
|
+
except (TypeError, ValueError):
|
|
101
|
+
# If that fails, sanitize the object first
|
|
102
|
+
sanitized_obj = sanitize_for_json(obj)
|
|
103
|
+
return json.dumps(sanitized_obj, **default_kwargs)
|
|
104
|
+
|
|
105
|
+
def pretty_print_vfb_result(result: Any, max_length: int = 1000) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Pretty print any VFB query result in a safe, readable format.
|
|
108
|
+
|
|
109
|
+
:param result: Result from any VFB query function
|
|
110
|
+
:param max_length: Maximum length of output (truncates if longer)
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
json_str = safe_json_dumps(result)
|
|
114
|
+
if len(json_str) > max_length:
|
|
115
|
+
print(json_str[:max_length] + f'\n... (truncated, full length: {len(json_str)} characters)')
|
|
116
|
+
else:
|
|
117
|
+
print(json_str)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
print(f'Error printing result: {e}')
|
|
120
|
+
print(f'Result type: {type(result)}')
|
|
121
|
+
if hasattr(result, '__dict__'):
|
|
122
|
+
print(f'Result attributes: {list(result.__dict__.keys())}')
|
|
123
|
+
else:
|
|
124
|
+
print(f'Result: {str(result)[:max_length]}...')
|
|
125
|
+
|
|
19
126
|
def patch_vfb_connect_query_wrapper():
|
|
20
127
|
"""
|
|
21
128
|
Apply monkey patches to VfbConnect.neo_query_wrapper to make it handle DataFrame results safely.
|
|
@@ -28,8 +135,8 @@ def patch_vfb_connect_query_wrapper():
|
|
|
28
135
|
def patched_get_term_info(self, terms, *args, **kwargs):
|
|
29
136
|
result = original_get_term_info(self, terms, *args, **kwargs)
|
|
30
137
|
if isinstance(result, pd.DataFrame):
|
|
31
|
-
# Return list of row dictionaries instead of DataFrame
|
|
32
|
-
return
|
|
138
|
+
# Return list of row dictionaries instead of DataFrame using safe conversion
|
|
139
|
+
return safe_to_dict(result)
|
|
33
140
|
return result
|
|
34
141
|
|
|
35
142
|
NeoQueryWrapper._get_TermInfo = patched_get_term_info
|
vfbquery/vfb_queries.py
CHANGED
|
@@ -2,13 +2,55 @@ import pysolr
|
|
|
2
2
|
from .term_info_queries import deserialize_term_info
|
|
3
3
|
# Replace VfbConnect import with our new SolrTermInfoFetcher
|
|
4
4
|
from .solr_fetcher import SolrTermInfoFetcher
|
|
5
|
-
# Keep dict_cursor if it's used elsewhere
|
|
6
|
-
from vfb_connect.cross_server_tools import dict_cursor
|
|
5
|
+
# Keep dict_cursor if it's used elsewhere - lazy import to avoid GUI issues
|
|
7
6
|
from marshmallow import Schema, fields, post_load
|
|
8
7
|
from typing import List, Tuple, Dict, Any, Union
|
|
9
8
|
import pandas as pd
|
|
10
9
|
from marshmallow import ValidationError
|
|
11
10
|
import json
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
# Custom JSON encoder to handle NumPy and pandas types
|
|
14
|
+
class NumpyEncoder(json.JSONEncoder):
|
|
15
|
+
def default(self, obj):
|
|
16
|
+
if isinstance(obj, np.integer):
|
|
17
|
+
return int(obj)
|
|
18
|
+
elif isinstance(obj, np.floating):
|
|
19
|
+
return float(obj)
|
|
20
|
+
elif isinstance(obj, np.ndarray):
|
|
21
|
+
return obj.tolist()
|
|
22
|
+
elif isinstance(obj, np.bool_):
|
|
23
|
+
return bool(obj)
|
|
24
|
+
elif hasattr(obj, 'item'): # Handle pandas scalar types
|
|
25
|
+
return obj.item()
|
|
26
|
+
return super(NumpyEncoder, self).default(obj)
|
|
27
|
+
|
|
28
|
+
def safe_to_dict(df, sort_by_id=True):
|
|
29
|
+
"""Convert DataFrame to dict with numpy types converted to native Python types"""
|
|
30
|
+
if isinstance(df, pd.DataFrame):
|
|
31
|
+
# Convert numpy dtypes to native Python types
|
|
32
|
+
df_copy = df.copy()
|
|
33
|
+
for col in df_copy.columns:
|
|
34
|
+
if df_copy[col].dtype.name.startswith('int'):
|
|
35
|
+
df_copy[col] = df_copy[col].astype('object')
|
|
36
|
+
elif df_copy[col].dtype.name.startswith('float'):
|
|
37
|
+
df_copy[col] = df_copy[col].astype('object')
|
|
38
|
+
|
|
39
|
+
# Sort by id column in descending order if it exists and sort_by_id is True
|
|
40
|
+
if sort_by_id and 'id' in df_copy.columns:
|
|
41
|
+
df_copy = df_copy.sort_values('id', ascending=False)
|
|
42
|
+
|
|
43
|
+
return df_copy.to_dict("records")
|
|
44
|
+
return df
|
|
45
|
+
|
|
46
|
+
# Lazy import for dict_cursor to avoid GUI library issues
|
|
47
|
+
def get_dict_cursor():
|
|
48
|
+
"""Lazy import dict_cursor to avoid import issues during testing"""
|
|
49
|
+
try:
|
|
50
|
+
from vfb_connect.cross_server_tools import dict_cursor
|
|
51
|
+
return dict_cursor
|
|
52
|
+
except ImportError as e:
|
|
53
|
+
raise ImportError(f"vfb_connect is required but could not be imported: {e}")
|
|
12
54
|
|
|
13
55
|
# Connect to the VFB SOLR server
|
|
14
56
|
vfb_solr = pysolr.Solr('http://solr.virtualflybrain.org/solr/vfb_json/', always_commit=False, timeout=990)
|
|
@@ -481,6 +523,11 @@ def term_info_parse_object(results, short_form):
|
|
|
481
523
|
if "image_" in key and not ("thumbnail" in key or "folder" in key) and len(vars(image.channel_image.image)[key]) > 1:
|
|
482
524
|
record[key.replace("image_","")] = vars(image.channel_image.image)[key].replace("http://","https://")
|
|
483
525
|
images[image.channel_image.image.template_anatomy.short_form].append(record)
|
|
526
|
+
|
|
527
|
+
# Sort each template's images by id in descending order (newest first)
|
|
528
|
+
for template_key in images:
|
|
529
|
+
images[template_key] = sorted(images[template_key], key=lambda x: x["id"], reverse=True)
|
|
530
|
+
|
|
484
531
|
termInfo["Examples"] = images
|
|
485
532
|
# add a query to `queries` list for listing all available images
|
|
486
533
|
q = ListAllAvailableImages_to_schema(termInfo["Name"], {"short_form":vfbTerm.term.core.short_form})
|
|
@@ -504,6 +551,11 @@ def term_info_parse_object(results, short_form):
|
|
|
504
551
|
if "image_" in key and not ("thumbnail" in key or "folder" in key) and len(vars(image.image)[key]) > 1:
|
|
505
552
|
record[key.replace("image_","")] = vars(image.image)[key].replace("http://","https://")
|
|
506
553
|
images[image.image.template_anatomy.short_form].append(record)
|
|
554
|
+
|
|
555
|
+
# Sort each template's images by id in descending order (newest first)
|
|
556
|
+
for template_key in images:
|
|
557
|
+
images[template_key] = sorted(images[template_key], key=lambda x: x["id"], reverse=True)
|
|
558
|
+
|
|
507
559
|
# Add the thumbnails to the term info
|
|
508
560
|
termInfo["Images"] = images
|
|
509
561
|
|
|
@@ -525,13 +577,29 @@ def term_info_parse_object(results, short_form):
|
|
|
525
577
|
images = {}
|
|
526
578
|
image = vfbTerm.template_channel
|
|
527
579
|
record = {}
|
|
528
|
-
|
|
529
|
-
|
|
580
|
+
|
|
581
|
+
# Validate that the channel ID matches the template ID (numeric part should be the same)
|
|
582
|
+
template_id = vfbTerm.term.core.short_form
|
|
583
|
+
channel_id = vfbTerm.template_channel.channel.short_form
|
|
584
|
+
|
|
585
|
+
# Extract numeric parts for validation
|
|
586
|
+
if template_id and channel_id:
|
|
587
|
+
template_numeric = template_id.replace("VFB_", "") if template_id.startswith("VFB_") else ""
|
|
588
|
+
channel_numeric = channel_id.replace("VFBc_", "") if channel_id.startswith("VFBc_") else ""
|
|
589
|
+
|
|
590
|
+
if template_numeric != channel_numeric:
|
|
591
|
+
print(f"Warning: Template ID {template_id} does not match channel ID {channel_id}")
|
|
592
|
+
label = vfbTerm.template_channel.channel.label
|
|
593
|
+
record["id"] = channel_id
|
|
594
|
+
else:
|
|
595
|
+
label = vfbTerm.term.core.label
|
|
596
|
+
record["id"] = template_id
|
|
597
|
+
|
|
530
598
|
if vfbTerm.template_channel.channel.symbol != "" and len(vfbTerm.template_channel.channel.symbol) > 0:
|
|
531
599
|
label = vfbTerm.template_channel.channel.symbol
|
|
532
600
|
record["label"] = label
|
|
533
|
-
if not
|
|
534
|
-
images[
|
|
601
|
+
if not template_id in images.keys():
|
|
602
|
+
images[template_id]=[]
|
|
535
603
|
record["thumbnail"] = image.image_thumbnail.replace("http://","https://").replace("thumbnailT.png","thumbnail.png")
|
|
536
604
|
record["thumbnail_transparent"] = image.image_thumbnail.replace("http://","https://").replace("thumbnail.png","thumbnailT.png")
|
|
537
605
|
for key in vars(image).keys():
|
|
@@ -549,7 +617,7 @@ def term_info_parse_object(results, short_form):
|
|
|
549
617
|
record['voxel'] = image.get_voxel()
|
|
550
618
|
if 'orientation' in image_vars.keys():
|
|
551
619
|
record['orientation'] = image.orientation
|
|
552
|
-
images[
|
|
620
|
+
images[template_id].append(record)
|
|
553
621
|
|
|
554
622
|
# Add the thumbnails to the term info
|
|
555
623
|
termInfo["Images"] = images
|
|
@@ -756,8 +824,13 @@ def ListAllAvailableImages_to_schema(name, take_default):
|
|
|
756
824
|
return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns)
|
|
757
825
|
|
|
758
826
|
def serialize_solr_output(results):
|
|
759
|
-
#
|
|
760
|
-
|
|
827
|
+
# Create a copy of the document and remove Solr-specific fields
|
|
828
|
+
doc = dict(results.docs[0])
|
|
829
|
+
# Remove the _version_ field which can cause serialization issues with large integers
|
|
830
|
+
doc.pop('_version_', None)
|
|
831
|
+
|
|
832
|
+
# Serialize the sanitized dictionary to JSON using NumpyEncoder
|
|
833
|
+
json_string = json.dumps(doc, ensure_ascii=False, cls=NumpyEncoder)
|
|
761
834
|
json_string = json_string.replace('\\', '')
|
|
762
835
|
json_string = json_string.replace('"{', '{')
|
|
763
836
|
json_string = json_string.replace('}"', '}')
|
|
@@ -822,7 +895,7 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1):
|
|
|
822
895
|
RETURN COUNT(r) AS total_count
|
|
823
896
|
"""
|
|
824
897
|
count_results = vc.nc.commit_list([count_query])
|
|
825
|
-
count_df = pd.DataFrame.from_records(
|
|
898
|
+
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
826
899
|
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
827
900
|
|
|
828
901
|
# Define the main Cypher query
|
|
@@ -852,7 +925,7 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1):
|
|
|
852
925
|
results = vc.nc.commit_list([query])
|
|
853
926
|
|
|
854
927
|
# Convert the results to a DataFrame
|
|
855
|
-
df = pd.DataFrame.from_records(
|
|
928
|
+
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
856
929
|
|
|
857
930
|
columns_to_encode = ['label', 'parent', 'source', 'source_id', 'template', 'dataset', 'license', 'thumbnail']
|
|
858
931
|
df = encode_markdown_links(df, columns_to_encode)
|
|
@@ -890,7 +963,7 @@ def get_instances(short_form: str, return_dataframe=True, limit: int = -1):
|
|
|
890
963
|
"thumbnail"
|
|
891
964
|
]
|
|
892
965
|
}
|
|
893
|
-
for row in df
|
|
966
|
+
for row in safe_to_dict(df)
|
|
894
967
|
],
|
|
895
968
|
"count": total_count
|
|
896
969
|
}
|
|
@@ -910,7 +983,7 @@ def get_templates(limit: int = -1, return_dataframe: bool = False):
|
|
|
910
983
|
RETURN COUNT(DISTINCT t) AS total_count"""
|
|
911
984
|
|
|
912
985
|
count_results = vc.nc.commit_list([count_query])
|
|
913
|
-
count_df = pd.DataFrame.from_records(
|
|
986
|
+
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
914
987
|
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
915
988
|
|
|
916
989
|
# Define the main Cypher query
|
|
@@ -935,7 +1008,7 @@ def get_templates(limit: int = -1, return_dataframe: bool = False):
|
|
|
935
1008
|
results = vc.nc.commit_list([query])
|
|
936
1009
|
|
|
937
1010
|
# Convert the results to a DataFrame
|
|
938
|
-
df = pd.DataFrame.from_records(
|
|
1011
|
+
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
939
1012
|
|
|
940
1013
|
columns_to_encode = ['name', 'dataset', 'license', 'thumbnail']
|
|
941
1014
|
df = encode_markdown_links(df, columns_to_encode)
|
|
@@ -978,7 +1051,7 @@ def get_templates(limit: int = -1, return_dataframe: bool = False):
|
|
|
978
1051
|
"license"
|
|
979
1052
|
]
|
|
980
1053
|
}
|
|
981
|
-
for row in df
|
|
1054
|
+
for row in safe_to_dict(df)
|
|
982
1055
|
],
|
|
983
1056
|
"count": total_count
|
|
984
1057
|
}
|
|
@@ -1037,7 +1110,7 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
|
|
|
1037
1110
|
RETURN COUNT(DISTINCT n2) AS total_count"""
|
|
1038
1111
|
|
|
1039
1112
|
count_results = vc.nc.commit_list([count_query])
|
|
1040
|
-
count_df = pd.DataFrame.from_records(
|
|
1113
|
+
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
1041
1114
|
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
1042
1115
|
|
|
1043
1116
|
main_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1)-[r:has_similar_morphology_to]-(n2)-[:INSTANCEOF]->(c2:Class)
|
|
@@ -1063,7 +1136,7 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
|
|
|
1063
1136
|
results = vc.nc.commit_list([main_query])
|
|
1064
1137
|
|
|
1065
1138
|
# Convert the results to a DataFrame
|
|
1066
|
-
df = pd.DataFrame.from_records(
|
|
1139
|
+
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
1067
1140
|
|
|
1068
1141
|
columns_to_encode = ['name', 'source', 'source_id', 'thumbnail']
|
|
1069
1142
|
df = encode_markdown_links(df, columns_to_encode)
|
|
@@ -1094,7 +1167,7 @@ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_datafram
|
|
|
1094
1167
|
"thumbnail"
|
|
1095
1168
|
]
|
|
1096
1169
|
}
|
|
1097
|
-
for row in df
|
|
1170
|
+
for row in safe_to_dict(df)
|
|
1098
1171
|
],
|
|
1099
1172
|
"count": total_count
|
|
1100
1173
|
}
|
|
@@ -1127,7 +1200,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True,
|
|
|
1127
1200
|
RETURN COUNT(DISTINCT c) AS total_count"""
|
|
1128
1201
|
|
|
1129
1202
|
count_results = vc.nc.commit_list([count_query])
|
|
1130
|
-
count_df = pd.DataFrame.from_records(
|
|
1203
|
+
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
1131
1204
|
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
1132
1205
|
|
|
1133
1206
|
# Define the part of the query for normal mode
|
|
@@ -1166,7 +1239,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True,
|
|
|
1166
1239
|
results = vc.nc.commit_list([query])
|
|
1167
1240
|
|
|
1168
1241
|
# Convert the results to a DataFrame
|
|
1169
|
-
df = pd.DataFrame.from_records(
|
|
1242
|
+
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
1170
1243
|
|
|
1171
1244
|
columns_to_encode = ['Neurotransmitter', 'Type', 'Name', 'Template_Space', 'Imaging_Technique', 'thumbnail']
|
|
1172
1245
|
df = encode_markdown_links(df, columns_to_encode)
|
|
@@ -1204,7 +1277,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True,
|
|
|
1204
1277
|
"Images"
|
|
1205
1278
|
]
|
|
1206
1279
|
}
|
|
1207
|
-
for row in df
|
|
1280
|
+
for row in safe_to_dict(df)
|
|
1208
1281
|
],
|
|
1209
1282
|
"count": total_count
|
|
1210
1283
|
}
|
|
@@ -1224,7 +1297,7 @@ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True,
|
|
|
1224
1297
|
"Weight",
|
|
1225
1298
|
]
|
|
1226
1299
|
}
|
|
1227
|
-
for row in df
|
|
1300
|
+
for row in safe_to_dict(df)
|
|
1228
1301
|
],
|
|
1229
1302
|
"count": total_count
|
|
1230
1303
|
}
|
|
@@ -1289,7 +1362,7 @@ def fill_query_results(term_info):
|
|
|
1289
1362
|
filtered_item = item
|
|
1290
1363
|
filtered_result.append(filtered_item)
|
|
1291
1364
|
elif isinstance(result, pd.DataFrame):
|
|
1292
|
-
filtered_result = result[query['preview_columns']]
|
|
1365
|
+
filtered_result = safe_to_dict(result[query['preview_columns']])
|
|
1293
1366
|
else:
|
|
1294
1367
|
print(f"Unsupported result format for filtering columns in {query['function']}")
|
|
1295
1368
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vfbquery
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: Wrapper for querying VirtualFlyBrain knowledge graph.
|
|
5
5
|
Home-page: https://github.com/VirtualFlyBrain/VFBquery
|
|
6
6
|
Author: VirtualFlyBrain
|
|
@@ -53,7 +53,7 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
53
53
|
"Meta": {
|
|
54
54
|
"Name": "[medulla](FBbt_00003748)",
|
|
55
55
|
"Description": "The second optic neuropil, sandwiched between the lamina and the lobula complex. It is divided into 10 layers: 1-6 make up the outer (distal) medulla, the seventh (or serpentine) layer exhibits a distinct architecture and layers 8-10 make up the inner (proximal) medulla (Ito et al., 2014).",
|
|
56
|
-
"Comment": "",
|
|
56
|
+
"Comment": "Nern et al. (2025) - doi:10.1038/s41586-025-08746-0 say distal is M1-5 and M6-7 is central medulla.",
|
|
57
57
|
"Types": "[anterior ectoderm derivative](FBbt_00025991); [synaptic neuropil domain](FBbt_00040007)",
|
|
58
58
|
"Relationships": "[develops from](RO_0002202): [medulla anlage](FBbt_00001935); [is part of](BFO_0000050): [adult optic lobe](FBbt_00003701)"
|
|
59
59
|
},
|
|
@@ -143,19 +143,18 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
143
143
|
"count": 4
|
|
144
144
|
}
|
|
145
145
|
],
|
|
146
|
-
"IsIndividual":
|
|
147
|
-
"
|
|
148
|
-
"IsClass": True,
|
|
146
|
+
"IsIndividual": false,
|
|
147
|
+
"IsClass": true,
|
|
149
148
|
"Examples": {
|
|
150
|
-
"
|
|
149
|
+
"VFB_00101384": [
|
|
151
150
|
{
|
|
152
|
-
"id": "
|
|
153
|
-
"label": "
|
|
154
|
-
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/
|
|
155
|
-
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/
|
|
156
|
-
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/
|
|
157
|
-
"wlz": "https://www.virtualflybrain.org/data/VFB/i/
|
|
158
|
-
"obj": "https://www.virtualflybrain.org/data/VFB/i/
|
|
151
|
+
"id": "VFB_00101385",
|
|
152
|
+
"label": "ME(R) on JRC_FlyEM_Hemibrain",
|
|
153
|
+
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/thumbnail.png",
|
|
154
|
+
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/thumbnailT.png",
|
|
155
|
+
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/volume.nrrd",
|
|
156
|
+
"wlz": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/volume.wlz",
|
|
157
|
+
"obj": "https://www.virtualflybrain.org/data/VFB/i/0010/1385/VFB_00101384/volume_man.obj"
|
|
159
158
|
}
|
|
160
159
|
],
|
|
161
160
|
"VFB_00101567": [
|
|
@@ -180,22 +179,19 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
180
179
|
"obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0624/VFB_00017894/volume_man.obj"
|
|
181
180
|
}
|
|
182
181
|
],
|
|
183
|
-
"
|
|
182
|
+
"VFB_00030786": [
|
|
184
183
|
{
|
|
185
|
-
"id": "
|
|
186
|
-
"label": "
|
|
187
|
-
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/
|
|
188
|
-
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/
|
|
189
|
-
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/
|
|
190
|
-
"wlz": "https://www.virtualflybrain.org/data/VFB/i/
|
|
191
|
-
"obj": "https://www.virtualflybrain.org/data/VFB/i/
|
|
184
|
+
"id": "VFB_00030810",
|
|
185
|
+
"label": "medulla on adult brain template Ito2014",
|
|
186
|
+
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnail.png",
|
|
187
|
+
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/thumbnailT.png",
|
|
188
|
+
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.nrrd",
|
|
189
|
+
"wlz": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume.wlz",
|
|
190
|
+
"obj": "https://www.virtualflybrain.org/data/VFB/i/0003/0810/VFB_00030786/volume_man.obj"
|
|
192
191
|
}
|
|
193
192
|
]
|
|
194
193
|
},
|
|
195
|
-
"IsTemplate":
|
|
196
|
-
"Domains": {},
|
|
197
|
-
"Licenses": {},
|
|
198
|
-
"Publications": [],
|
|
194
|
+
"IsTemplate": false,
|
|
199
195
|
"Synonyms": [
|
|
200
196
|
{
|
|
201
197
|
"label": "ME",
|
|
@@ -203,12 +199,6 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
203
199
|
"type": "",
|
|
204
200
|
"publication": "[Ito et al., 2014](FBrf0224194)"
|
|
205
201
|
},
|
|
206
|
-
{
|
|
207
|
-
"label": "m",
|
|
208
|
-
"scope": "has_related_synonym",
|
|
209
|
-
"type": "",
|
|
210
|
-
"publication": ""
|
|
211
|
-
},
|
|
212
202
|
{
|
|
213
203
|
"label": "Med",
|
|
214
204
|
"scope": "has_exact_synonym",
|
|
@@ -220,6 +210,12 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
220
210
|
"scope": "has_exact_synonym",
|
|
221
211
|
"type": "",
|
|
222
212
|
"publication": "[Venkatesh and Shyamala, 2010](FBrf0212889)"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"label": "m",
|
|
216
|
+
"scope": "has_related_synonym",
|
|
217
|
+
"type": "",
|
|
218
|
+
"publication": ""
|
|
223
219
|
}
|
|
224
220
|
]
|
|
225
221
|
}
|
|
@@ -259,7 +255,8 @@ vfb.get_term_info('VFB_00000001')
|
|
|
259
255
|
"Tags": [
|
|
260
256
|
"Adult",
|
|
261
257
|
"Expression_pattern_fragment",
|
|
262
|
-
"Neuron"
|
|
258
|
+
"Neuron",
|
|
259
|
+
"lineage_CM3"
|
|
263
260
|
],
|
|
264
261
|
"Queries": [
|
|
265
262
|
{
|
|
@@ -325,41 +322,41 @@ vfb.get_term_info('VFB_00000001')
|
|
|
325
322
|
"id": "VFB_00000333",
|
|
326
323
|
"score": "0.61",
|
|
327
324
|
"name": "[fru-M-000204](VFB_00000333)",
|
|
328
|
-
"tags": "Expression_pattern_fragment|Neuron|Adult",
|
|
329
|
-
"thumbnail": "[](VFB_00017894,VFB_00000333)"
|
|
330
327
|
},
|
|
331
328
|
{
|
|
332
329
|
"id": "VFB_00000333",
|
|
333
330
|
"score": "0.61",
|
|
334
331
|
"name": "[fru-M-000204](VFB_00000333)",
|
|
335
|
-
"tags": "Expression_pattern_fragment|Neuron|Adult",
|
|
336
|
-
"thumbnail": "[](VFB_00101567,VFB_00000333)"
|
|
337
334
|
},
|
|
338
335
|
{
|
|
339
336
|
"id": "VFB_00002439",
|
|
340
337
|
"score": "0.6",
|
|
341
338
|
"name": "[fru-M-900020](VFB_00002439)",
|
|
342
|
-
"tags": "Expression_pattern_fragment|Neuron|Adult",
|
|
343
|
-
"thumbnail": "[](VFB_00101567,VFB_00002439)"
|
|
344
341
|
},
|
|
345
342
|
{
|
|
346
343
|
"id": "VFB_00002439",
|
|
347
344
|
"score": "0.6",
|
|
348
345
|
"name": "[fru-M-900020](VFB_00002439)",
|
|
349
|
-
"tags": "Expression_pattern_fragment|Neuron|Adult",
|
|
350
|
-
"thumbnail": "[](VFB_00017894,VFB_00002439)"
|
|
351
348
|
},
|
|
352
349
|
{
|
|
353
|
-
"id": "
|
|
350
|
+
"id": "VFB_00000845",
|
|
354
351
|
"score": "0.59",
|
|
355
|
-
"name": "[fru-M-
|
|
356
|
-
"tags": "Expression_pattern_fragment|Neuron|Adult",
|
|
357
|
-
"thumbnail": "[",
|
|
353
|
+
"tags": "Expression_pattern_fragment|Neuron|Adult|lineage_CM3",
|
|
354
|
+
"thumbnail": "[](VFB_00101567,VFB_00000845)"
|
|
358
355
|
}
|
|
359
356
|
]
|
|
360
357
|
},
|
|
361
358
|
"output_format": "table",
|
|
362
|
-
"count":
|
|
359
|
+
"count": 60
|
|
363
360
|
}
|
|
364
361
|
],
|
|
365
362
|
"IsIndividual": True,
|
|
@@ -368,11 +365,11 @@ vfb.get_term_info('VFB_00000001')
|
|
|
368
365
|
{
|
|
369
366
|
"id": "VFB_00000001",
|
|
370
367
|
"label": "fru-M-200266",
|
|
371
|
-
"thumbnail": "https://virtualflybrain.org/
|
|
372
|
-
"thumbnail_transparent": "https://virtualflybrain.org/
|
|
368
|
+
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnail.png",
|
|
369
|
+
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/thumbnailT.png",
|
|
373
370
|
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.nrrd",
|
|
374
|
-
"wlz": "https://virtualflybrain.org/
|
|
375
|
-
"obj": "https://virtualflybrain.org/
|
|
371
|
+
"wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.wlz",
|
|
372
|
+
"obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.obj",
|
|
376
373
|
"swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00017894/volume.swc"
|
|
377
374
|
}
|
|
378
375
|
],
|
|
@@ -380,19 +377,17 @@ vfb.get_term_info('VFB_00000001')
|
|
|
380
377
|
{
|
|
381
378
|
"id": "VFB_00000001",
|
|
382
379
|
"label": "fru-M-200266",
|
|
383
|
-
"thumbnail": "https://virtualflybrain.org/
|
|
384
|
-
"thumbnail_transparent": "https://virtualflybrain.org/
|
|
380
|
+
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/thumbnail.png",
|
|
381
|
+
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/thumbnailT.png",
|
|
385
382
|
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.nrrd",
|
|
386
|
-
"wlz": "https://virtualflybrain.org/
|
|
387
|
-
"obj": "https://virtualflybrain.org/
|
|
383
|
+
"wlz": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.wlz",
|
|
384
|
+
"obj": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.obj",
|
|
388
385
|
"swc": "https://www.virtualflybrain.org/data/VFB/i/0000/0001/VFB_00101567/volume.swc"
|
|
389
386
|
}
|
|
390
387
|
]
|
|
391
388
|
},
|
|
392
389
|
"IsClass": False,
|
|
393
|
-
"Examples": {},
|
|
394
390
|
"IsTemplate": False,
|
|
395
|
-
"Domains": {},
|
|
396
391
|
"Licenses": {
|
|
397
392
|
"0": {
|
|
398
393
|
"iri": "http://virtualflybrain.org/reports/VFBlicense_FlyCircuit_License",
|
|
@@ -402,9 +397,7 @@ vfb.get_term_info('VFB_00000001')
|
|
|
402
397
|
"source": "FlyCircuit 1.0 - single neurons (Chiang2010)",
|
|
403
398
|
"source_iri": "http://virtualflybrain.org/reports/Chiang2010"
|
|
404
399
|
}
|
|
405
|
-
}
|
|
406
|
-
"Publications": [],
|
|
407
|
-
"Synonyms": []
|
|
400
|
+
}
|
|
408
401
|
}
|
|
409
402
|
```
|
|
410
403
|
|
|
@@ -412,7 +405,6 @@ Template example:
|
|
|
412
405
|
```python
|
|
413
406
|
vfb.get_term_info('VFB_00101567')
|
|
414
407
|
```
|
|
415
|
-
|
|
416
408
|
```json
|
|
417
409
|
{
|
|
418
410
|
"Name": "JRC2018U",
|
|
@@ -441,10 +433,10 @@ vfb.get_term_info('VFB_00101567')
|
|
|
441
433
|
"Queries": [],
|
|
442
434
|
"IsIndividual": True,
|
|
443
435
|
"Images": {
|
|
444
|
-
"
|
|
436
|
+
"VFB_00101567": [
|
|
445
437
|
{
|
|
446
|
-
"id": "
|
|
447
|
-
"label": "
|
|
438
|
+
"id": "VFB_00101567",
|
|
439
|
+
"label": "JRC2018Unisex",
|
|
448
440
|
"thumbnail": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/thumbnail.png",
|
|
449
441
|
"thumbnail_transparent": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/thumbnailT.png",
|
|
450
442
|
"nrrd": "https://www.virtualflybrain.org/data/VFB/i/0010/1567/VFB_00101567/volume.nrrd",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
test/readme_parser.py,sha256=puvcq4_oEltjx_faw1kQJ8mmIWiQU-40oLJjtJBQCsQ,4170
|
|
3
|
+
test/term_info_queries_test.py,sha256=EiL6Od5L9W6Xm6MPRMU4V_4TfoVsSSWjO0wh0F2ITH0,34242
|
|
4
|
+
test/test_examples_diff.py,sha256=ep_BzA-7az2OUPxUIsS3ReFV8LwuzGv8yIL0HirOGsc,15699
|
|
5
|
+
vfbquery/__init__.py,sha256=ZirzaFa-rgWYvqvK08rwxachUEz_qjhKrm3C8s3tlZY,76
|
|
6
|
+
vfbquery/solr_fetcher.py,sha256=U8mHaBJrwjncl1eU_gnNj5CGhEb-s9dCpcUTXTifQOY,3984
|
|
7
|
+
vfbquery/term_info_queries.py,sha256=oE-Ogm7jCPPlKtD3W3EtttYZcHnInwDOpOj-phAEOaI,42009
|
|
8
|
+
vfbquery/test_utils.py,sha256=7wUA3xgaGu3eLnjC98msNYt1wL538nOimVJjkC0ZLjU,5791
|
|
9
|
+
vfbquery/vfb_queries.py,sha256=NnkWB3shgnv2ovG-WimcuzXZtQCjRzIqdWPnQvoY4Hs,66014
|
|
10
|
+
vfbquery-0.3.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
+
vfbquery-0.3.4.dist-info/METADATA,sha256=T4Kxnz0tLOR_hmgIc-ZwfNWbkW_twWUYnMsy9t5jmzc,63097
|
|
12
|
+
vfbquery-0.3.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
13
|
+
vfbquery-0.3.4.dist-info/top_level.txt,sha256=UgaRTTOy4JBdKbkr_gkeknT4eaibm3ztF520G4NTQZs,14
|
|
14
|
+
vfbquery-0.3.4.dist-info/RECORD,,
|
vfbquery-0.3.2.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
test/readme_parser.py,sha256=puvcq4_oEltjx_faw1kQJ8mmIWiQU-40oLJjtJBQCsQ,4170
|
|
3
|
-
test/term_info_queries_test.py,sha256=Rd7KeS6dVLjIB74s3poj5jr7h5s_DIViVpuEWnhzBB4,33423
|
|
4
|
-
test/test_examples_diff.py,sha256=ep_BzA-7az2OUPxUIsS3ReFV8LwuzGv8yIL0HirOGsc,15699
|
|
5
|
-
vfbquery/__init__.py,sha256=KPkQWJsiUtew3IrygX17djJJfCxJtqw3cy3rB-e3cL4,28
|
|
6
|
-
vfbquery/solr_fetcher.py,sha256=_e0W87_tLwGeXSmok0FfBnpjIiM2lqTelKNkpdzxL1k,3529
|
|
7
|
-
vfbquery/term_info_queries.py,sha256=79Bm2RJzAZyVPQE5HWhsvybeBYrz2AbFgbM0ympIxao,41399
|
|
8
|
-
vfbquery/test_utils.py,sha256=HKFsQ2wqZYxR_wS9V6RIM3SguIi9kX5kyYDAXgpfp1A,1623
|
|
9
|
-
vfbquery/vfb_queries.py,sha256=VE5-RBzgVMdadcWTjSmF5oQxefQ3fEDNwaQevG69Img,62760
|
|
10
|
-
vfbquery-0.3.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
-
vfbquery-0.3.2.dist-info/METADATA,sha256=xllJx3ZOH91sagWa-_2O6_DI86wAjs0TQt8xsuEoUXA,62863
|
|
12
|
-
vfbquery-0.3.2.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
|
|
13
|
-
vfbquery-0.3.2.dist-info/top_level.txt,sha256=UgaRTTOy4JBdKbkr_gkeknT4eaibm3ztF520G4NTQZs,14
|
|
14
|
-
vfbquery-0.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|