vfbquery 0.4.1__py3-none-any.whl → 0.5.1__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.
Files changed (39) hide show
  1. test/readme_parser.py +29 -27
  2. test/term_info_queries_test.py +46 -34
  3. test/test_dataset_template_queries.py +138 -0
  4. test/test_default_caching.py +89 -84
  5. test/test_examples_code.py +7 -0
  6. test/test_examples_diff.py +95 -172
  7. test/test_expression_overlaps.py +183 -0
  8. test/test_expression_pattern_fragments.py +123 -0
  9. test/test_images_neurons.py +152 -0
  10. test/test_images_that_develop_from.py +112 -0
  11. test/test_lineage_clones_in.py +190 -0
  12. test/test_nblast_queries.py +124 -0
  13. test/test_neuron_classes_fasciculating.py +187 -0
  14. test/test_neuron_inputs.py +193 -0
  15. test/test_neuron_neuron_connectivity.py +89 -0
  16. test/test_neuron_region_connectivity.py +117 -0
  17. test/test_neurons_part_here.py +203 -0
  18. test/test_new_owlery_queries.py +282 -0
  19. test/test_publication_transgene_queries.py +101 -0
  20. test/test_query_performance.py +739 -0
  21. test/test_similar_morphology.py +177 -0
  22. test/test_tracts_nerves_innervating.py +188 -0
  23. test/test_transcriptomics.py +223 -0
  24. vfbquery/__init__.py +47 -35
  25. vfbquery/cached_functions.py +772 -131
  26. vfbquery/neo4j_client.py +120 -0
  27. vfbquery/owlery_client.py +463 -0
  28. vfbquery/solr_cache_integration.py +34 -30
  29. vfbquery/solr_fetcher.py +1 -1
  30. vfbquery/solr_result_cache.py +338 -36
  31. vfbquery/term_info_queries.py +1 -1
  32. vfbquery/vfb_queries.py +2969 -627
  33. vfbquery-0.5.1.dist-info/METADATA +2806 -0
  34. vfbquery-0.5.1.dist-info/RECORD +40 -0
  35. vfbquery-0.4.1.dist-info/METADATA +0 -1315
  36. vfbquery-0.4.1.dist-info/RECORD +0 -19
  37. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/LICENSE +0 -0
  38. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/WHEEL +0 -0
  39. {vfbquery-0.4.1.dist-info → vfbquery-0.5.1.dist-info}/top_level.txt +0 -0
test/readme_parser.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import re
2
2
  import json
3
+ import ast
3
4
  import os.path
4
5
 
5
6
  def extract_code_blocks(readme_path):
@@ -24,6 +25,9 @@ def extract_code_blocks(readme_path):
24
25
  # Process Python blocks to extract vfb calls
25
26
  processed_python_blocks = []
26
27
  for block in python_blocks:
28
+ # Skip blocks that contain import statements
29
+ if 'import' in block:
30
+ continue
27
31
  # Look for vfb.* calls and extract them
28
32
  vfb_calls = re.findall(r'(vfb\.[^)]*\))', block)
29
33
  if vfb_calls:
@@ -32,29 +36,17 @@ def extract_code_blocks(readme_path):
32
36
  # - get_templates() doesn't support force_refresh (no SOLR cache)
33
37
  # - Performance test terms (FBbt_00003748, VFB_00101567) should use cache
34
38
  for call in vfb_calls:
35
- # Check if this is get_templates() - if so, don't add force_refresh
36
- if 'get_templates' in call:
37
- processed_python_blocks.append(call)
38
- continue
39
-
40
- # Check if this call uses performance test terms - skip force_refresh for those
41
- if 'FBbt_00003748' in call or 'VFB_00101567' in call:
42
- processed_python_blocks.append(call)
43
- continue
44
-
45
- # Check if the call already has parameters
46
- if '(' in call and ')' in call:
47
- # Insert force_refresh=True before the closing parenthesis
48
- # Handle both cases: with and without existing parameters
49
- if call.rstrip(')').endswith('('):
50
- # No parameters: vfb.function()
51
- modified_call = call[:-1] + 'force_refresh=True)'
39
+ if 'FBbt_00003748' in call:
40
+ # Add force_refresh for medulla calls
41
+ if '(' in call and ')' in call:
42
+ if 'force_refresh' not in call:
43
+ modified_call = call[:-1] + ', force_refresh=True)'
44
+ processed_python_blocks.append(modified_call)
45
+ else:
46
+ processed_python_blocks.append(call)
52
47
  else:
53
- # Has parameters: vfb.function(param1, param2)
54
- modified_call = call[:-1] + ', force_refresh=True)'
55
- processed_python_blocks.append(modified_call)
48
+ processed_python_blocks.append(call)
56
49
  else:
57
- # Shouldn't happen, but include original call if no parentheses
58
50
  processed_python_blocks.append(call)
59
51
 
60
52
  # Process JSON blocks
@@ -63,9 +55,6 @@ def extract_code_blocks(readme_path):
63
55
  try:
64
56
  # Clean up the JSON text
65
57
  json_text = block.strip()
66
- # Convert Python boolean literals to JSON booleans using regex
67
- json_text = re.sub(r'\bTrue\b', 'true', json_text)
68
- json_text = re.sub(r'\bFalse\b', 'false', json_text)
69
58
  # Parse the JSON and add to results
70
59
  json_obj = json.loads(json_text)
71
60
  processed_json_blocks.append(json_obj)
@@ -89,6 +78,16 @@ def generate_python_file(python_blocks, output_path):
89
78
  for block in python_blocks:
90
79
  f.write(f'results.append({block})\n')
91
80
 
81
+ def generate_code_strings_file(python_blocks, output_path):
82
+ """
83
+ Generates a Python file containing the extracted code blocks as strings in a results list.
84
+ """
85
+ with open(output_path, 'w') as f:
86
+ f.write('results = [\n')
87
+ for block in python_blocks:
88
+ f.write(f' "{block}",\n')
89
+ f.write(']\n')
90
+
92
91
  def generate_json_file(json_blocks, output_path):
93
92
  """
94
93
  Generates a Python file containing the extracted JSON blocks as a Python list.
@@ -107,12 +106,13 @@ def generate_json_file(json_blocks, output_path):
107
106
 
108
107
  f.write(python_list)
109
108
 
110
- def process_readme(readme_path, python_output_path, json_output_path):
109
+ def process_readme(readme_path, python_output_path, code_strings_output_path, json_output_path):
111
110
  """
112
111
  Process the README file and generate the test files.
113
112
  """
114
113
  python_blocks, json_blocks = extract_code_blocks(readme_path)
115
114
  generate_python_file(python_blocks, python_output_path)
115
+ generate_code_strings_file(python_blocks, code_strings_output_path)
116
116
  generate_json_file(json_blocks, json_output_path)
117
117
 
118
118
  return len(python_blocks), len(json_blocks)
@@ -122,10 +122,12 @@ if __name__ == "__main__":
122
122
  readme_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'README.md')
123
123
  python_blocks, json_blocks = extract_code_blocks(readme_path)
124
124
 
125
- python_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'test_examples.py')
126
- json_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'test_results.py')
125
+ python_path = os.path.join(os.path.dirname(__file__), 'test_examples.py')
126
+ code_strings_path = os.path.join(os.path.dirname(__file__), 'test_examples_code.py')
127
+ json_path = os.path.join(os.path.dirname(__file__), 'test_results.py')
127
128
 
128
129
  generate_python_file(python_blocks, python_path)
130
+ generate_code_strings_file(python_blocks, code_strings_path)
129
131
  generate_json_file(json_blocks, json_path)
130
132
 
131
133
  print(f"Extracted {len(python_blocks)} Python blocks and {len(json_blocks)} JSON blocks")
@@ -1,7 +1,7 @@
1
1
  import unittest
2
2
  import time
3
- from src.vfbquery.term_info_queries import deserialize_term_info, deserialize_term_info_from_dict, process
4
- from src.vfbquery.solr_fetcher import SolrTermInfoFetcher
3
+ from vfbquery.term_info_queries import deserialize_term_info, deserialize_term_info_from_dict, process
4
+ from vfbquery.solr_fetcher import SolrTermInfoFetcher
5
5
 
6
6
 
7
7
  class TermInfoQueriesTest(unittest.TestCase):
@@ -10,6 +10,12 @@ class TermInfoQueriesTest(unittest.TestCase):
10
10
  self.vc = SolrTermInfoFetcher()
11
11
  self.variable = TestVariable("my_id", "my_name")
12
12
 
13
+ def get_term_info_or_skip(self, term_id):
14
+ try:
15
+ return self.vc.get_TermInfo([term_id], return_dataframe=False, summary=False)[0]
16
+ except Exception as e:
17
+ self.skipTest(f"SOLR server not available: {e}")
18
+
13
19
  def test_term_info_deserialization(self):
14
20
  terminfo_json = """
15
21
  {"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": []}
@@ -40,12 +46,12 @@ class TermInfoQueriesTest(unittest.TestCase):
40
46
  def test_term_info_deserialization_from_dict(self):
41
47
  import pkg_resources
42
48
  print("vfb_connect version:", pkg_resources.get_distribution("vfb_connect").version)
43
- vfbTerm = self.vc.get_TermInfo(['FBbt_00048514'], return_dataframe=False, summary=False)[0]
49
+ vfbTerm = self.get_term_info_or_skip('FBbt_00048514')
44
50
  start_time = time.time()
45
51
  terminfo = deserialize_term_info_from_dict(vfbTerm)
46
52
  print("--- %s seconds ---" % (time.time() - start_time))
47
- print("vfbTerm:", vfbTerm)
48
- print("terminfo:", terminfo)
53
+ # print("vfbTerm:", vfbTerm)
54
+ # print("terminfo:", terminfo)
49
55
  # Add debug for unique_facets
50
56
  if hasattr(terminfo.term.core, 'unique_facets'):
51
57
  print("unique_facets:", terminfo.term.core.unique_facets)
@@ -84,7 +90,7 @@ class TermInfoQueriesTest(unittest.TestCase):
84
90
  self.assertEqual("33657409", labellar_hmsn_entry.pub.PubMed)
85
91
 
86
92
  def test_term_info_serialization_individual_anatomy(self):
87
- term_info_dict = self.vc.get_TermInfo(['VFB_00010001'], return_dataframe=False, summary=False)[0]
93
+ term_info_dict = self.get_term_info_or_skip('VFB_00010001')
88
94
  print(term_info_dict)
89
95
  start_time = time.time()
90
96
  serialized = process(term_info_dict, self.variable)
@@ -133,7 +139,7 @@ class TermInfoQueriesTest(unittest.TestCase):
133
139
  'reference': '[VFB_00017894,VFB_00010001]'} in serialized["thumbnail"])
134
140
 
135
141
  def test_term_info_serialization_class(self):
136
- term_info_dict = self.vc.get_TermInfo(['FBbt_00048531'], return_dataframe=False, summary=False)[0]
142
+ term_info_dict = self.get_term_info_or_skip('FBbt_00048531')
137
143
  print(term_info_dict)
138
144
  start_time = time.time()
139
145
  serialized = process(term_info_dict, self.variable)
@@ -176,7 +182,7 @@ class TermInfoQueriesTest(unittest.TestCase):
176
182
  self.assertFalse("downloads_label" in serialized)
177
183
 
178
184
  def test_term_info_serialization_neuron_class(self):
179
- term_info_dict = self.vc.get_TermInfo(['FBbt_00048999'], return_dataframe=False, summary=False)[0]
185
+ term_info_dict = self.get_term_info_or_skip('FBbt_00048999')
180
186
  print(term_info_dict)
181
187
  start_time = time.time()
182
188
  serialized = process(term_info_dict, self.variable)
@@ -234,7 +240,7 @@ class TermInfoQueriesTest(unittest.TestCase):
234
240
  self.assertFalse("template" in serialized)
235
241
 
236
242
  def test_term_info_serialization_neuron_class2(self):
237
- term_info_dict = self.vc.get_TermInfo(['FBbt_00047030'], return_dataframe=False, summary=False)[0]
243
+ term_info_dict = self.get_term_info_or_skip('FBbt_00047030')
238
244
  print(term_info_dict)
239
245
  start_time = time.time()
240
246
  serialized = process(term_info_dict, self.variable)
@@ -303,7 +309,7 @@ class TermInfoQueriesTest(unittest.TestCase):
303
309
  self.assertFalse("template" in serialized)
304
310
 
305
311
  def test_term_info_serialization_split_class(self):
306
- term_info_dict = self.vc.get_TermInfo(['VFBexp_FBtp0124468FBtp0133404'], return_dataframe=False, summary=False)[0]
312
+ term_info_dict = self.get_term_info_or_skip('VFBexp_FBtp0124468FBtp0133404')
307
313
  print(term_info_dict)
308
314
  start_time = time.time()
309
315
  serialized = process(term_info_dict, self.variable)
@@ -330,18 +336,21 @@ class TermInfoQueriesTest(unittest.TestCase):
330
336
 
331
337
  self.assertTrue("relationships" in serialized)
332
338
  self.assertEqual(2, len(serialized["relationships"]))
333
- self.assertTrue(serialized["relationships"][0] == "has hemidriver [P{VT043927-GAL4.DBD}](FBtp0124468)" or serialized["relationships"][0] == "has hemidriver [P{VT017491-p65.AD}](FBtp0133404)", "Hemidriver Missing")
339
+ expected_rel_1 = "has hemidriver [P{VT043927-GAL4.DBD}](FBtp0124468)"
340
+ expected_rel_2 = "has hemidriver [P{VT017491-p65.AD}](FBtp0133404)"
341
+ self.assertIn(expected_rel_1, serialized["relationships"])
342
+ self.assertIn(expected_rel_2, serialized["relationships"])
334
343
 
335
344
  self.assertFalse("related_individuals" in serialized)
336
345
  self.assertTrue("xrefs" in serialized)
337
346
  self.assertEqual(2, len(serialized["xrefs"]))
338
- self.assertEqual({'icon': 'http://www.virtualflybrain.org/data/VFB/logos/fly_light_color.png',
339
- 'label': '[P{VT043927-GAL4.DBD} ∩ P{VT017491-p65.AD} expression pattern on '
340
- 'Driver Line on the FlyLight Split-GAL4 Site]'
341
- '(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574)',
342
- 'site': '[FlyLightSplit]'
343
- '(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574) '},
344
- serialized["xrefs"][0])
347
+ expected_xref = {'icon': 'https://www.virtualflybrain.org/data/VFB/logos/fly_light_color.png',
348
+ 'label': '[P{VT043927-GAL4.DBD} ∩ P{VT017491-p65.AD} expression pattern on '
349
+ 'Driver Line on the FlyLight Split-GAL4 Site]'
350
+ '(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574)',
351
+ 'site': '[FlyLightSplit]'
352
+ '(http://splitgal4.janelia.org/cgi-bin/view_splitgal4_imagery.cgi?line=SS50574) '}
353
+ self.assertIn(expected_xref, serialized["xrefs"])
345
354
 
346
355
  self.assertTrue("examples" in serialized)
347
356
  self.assertFalse("thumbnail" in serialized)
@@ -357,7 +366,7 @@ class TermInfoQueriesTest(unittest.TestCase):
357
366
  self.assertFalse("template" in serialized)
358
367
 
359
368
  def test_term_info_serialization_dataset(self):
360
- term_info_dict = self.vc.get_TermInfo(['Ito2013'], return_dataframe=False, summary=False)[0]
369
+ term_info_dict = self.get_term_info_or_skip('Ito2013')
361
370
  print(term_info_dict)
362
371
  start_time = time.time()
363
372
  serialized = process(term_info_dict, self.variable)
@@ -395,7 +404,7 @@ class TermInfoQueriesTest(unittest.TestCase):
395
404
  self.assertTrue("clone of Ito 2013" in sample_example["name"])
396
405
 
397
406
  def test_term_info_serialization_license(self):
398
- term_info_dict = self.vc.get_TermInfo(['VFBlicense_CC_BY_NC_3_0'], return_dataframe=False, summary=False)[0]
407
+ term_info_dict = self.get_term_info_or_skip('VFBlicense_CC_BY_NC_3_0')
399
408
  print(term_info_dict)
400
409
  start_time = time.time()
401
410
  serialized = process(term_info_dict, self.variable)
@@ -430,7 +439,7 @@ class TermInfoQueriesTest(unittest.TestCase):
430
439
  self.assertFalse("template" in serialized)
431
440
 
432
441
  def test_term_info_serialization_template(self):
433
- term_info_dict = self.vc.get_TermInfo(['VFB_00200000'], return_dataframe=False, summary=False)[0]
442
+ term_info_dict = self.get_term_info_or_skip('VFB_00200000')
434
443
  print(term_info_dict)
435
444
  start_time = time.time()
436
445
  serialized = process(term_info_dict, self.variable)
@@ -458,7 +467,7 @@ class TermInfoQueriesTest(unittest.TestCase):
458
467
  self.assertFalse("examples" in serialized)
459
468
  self.assertTrue("thumbnail" in serialized)
460
469
  self.assertEqual(1, len(serialized["thumbnail"]))
461
- self.assertEqual({'data': 'http://www.virtualflybrain.org/data/VFB/i/0020/0000/VFB_00200000/thumbnailT.png',
470
+ self.assertEqual({'data': 'https://www.virtualflybrain.org/data/VFB/i/0020/0000/VFB_00200000/thumbnailT.png',
462
471
  'format': 'PNG',
463
472
  'name': 'JRC2018UnisexVNC',
464
473
  'reference': 'VFB_00200000'}, serialized["thumbnail"][0])
@@ -486,7 +495,7 @@ class TermInfoQueriesTest(unittest.TestCase):
486
495
  self.assertEqual("[JRC2018UnisexVNC](VFB_00200000)", serialized["template"])
487
496
 
488
497
  def test_term_info_serialization_pub(self):
489
- term_info_dict = self.vc.get_TermInfo(['FBrf0243986'], return_dataframe=False, summary=False)[0]
498
+ term_info_dict = self.get_term_info_or_skip('FBrf0243986')
490
499
  print(term_info_dict)
491
500
  start_time = time.time()
492
501
  serialized = process(term_info_dict, self.variable)
@@ -531,15 +540,18 @@ class TermInfoQueriesTest(unittest.TestCase):
531
540
  """
532
541
  import vfbquery as vfb
533
542
 
534
- # Test performance for FBbt_00003748 (mushroom body)
535
- start_time = time.time()
536
- result_1 = vfb.get_term_info('FBbt_00003748')
537
- duration_1 = time.time() - start_time
538
-
539
- # Test performance for VFB_00101567 (individual anatomy)
540
- start_time = time.time()
541
- result_2 = vfb.get_term_info('VFB_00101567')
542
- duration_2 = time.time() - start_time
543
+ try:
544
+ # Test performance for FBbt_00003748 (mushroom body)
545
+ start_time = time.time()
546
+ result_1 = vfb.get_term_info('FBbt_00003748')
547
+ duration_1 = time.time() - start_time
548
+
549
+ # Test performance for VFB_00101567 (individual anatomy)
550
+ start_time = time.time()
551
+ result_2 = vfb.get_term_info('VFB_00101567')
552
+ duration_2 = time.time() - start_time
553
+ except Exception as e:
554
+ self.skipTest(f"SOLR server not available: {e}")
543
555
 
544
556
  # Print performance metrics for GitHub Actions logs
545
557
  print(f"\n" + "="*50)
@@ -569,8 +581,8 @@ class TermInfoQueriesTest(unittest.TestCase):
569
581
 
570
582
  # Performance assertions - fail if queries take too long
571
583
  # These thresholds are based on observed performance characteristics
572
- max_single_query_time = 3.0 # seconds (increased from 2.0 to account for SOLR cache overhead)
573
- max_total_time = 6.0 # seconds (2 queries * 3 seconds each)
584
+ max_single_query_time = 5.0 # seconds (increased from 2.0 to account for SOLR cache overhead)
585
+ max_total_time = 10.0 # seconds (2 queries * 5 seconds each)
574
586
 
575
587
  self.assertLess(duration_1, max_single_query_time,
576
588
  f"FBbt_00003748 query took {duration_1:.4f}s, exceeding {max_single_query_time}s threshold")
@@ -0,0 +1,138 @@
1
+ """
2
+ Unit tests for dataset and template queries.
3
+
4
+ This tests all 5 dataset/template-related queries:
5
+ 1. get_painted_domains - Template painted anatomy domains
6
+ 2. get_dataset_images - Images in a dataset
7
+ 3. get_all_aligned_images - All images aligned to template
8
+ 4. get_aligned_datasets - All datasets aligned to template
9
+ 5. get_all_datasets - All available datasets
10
+
11
+ Test terms:
12
+ - VFBc_00050000 - Adult Brain template
13
+ - VFBc_00101384 - Example dataset
14
+ """
15
+
16
+ import unittest
17
+ import sys
18
+ import os
19
+
20
+ # Add src directory to path
21
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
22
+
23
+ from vfbquery.vfb_queries import (
24
+ get_painted_domains,
25
+ get_dataset_images,
26
+ get_all_aligned_images,
27
+ get_aligned_datasets,
28
+ get_all_datasets,
29
+ PaintedDomains_to_schema,
30
+ DatasetImages_to_schema,
31
+ AllAlignedImages_to_schema,
32
+ AlignedDatasets_to_schema,
33
+ AllDatasets_to_schema
34
+ )
35
+
36
+
37
+ class DatasetTemplateQueriesTest(unittest.TestCase):
38
+ """Test cases for dataset and template queries"""
39
+
40
+ def setUp(self):
41
+ """Set up test fixtures"""
42
+ self.template_term = 'VFBc_00050000' # Adult Brain template
43
+ self.dataset_term = 'VFBc_00101384' # Example dataset
44
+
45
+ def test_get_painted_domains(self):
46
+ """Test get_painted_domains query"""
47
+ result = get_painted_domains(self.template_term, return_dataframe=True, limit=10)
48
+ self.assertIsNotNone(result, "Result should not be None")
49
+
50
+ import pandas as pd
51
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
52
+ print(f"\n✓ Found {len(result)} painted domains for {self.template_term}")
53
+ self.assertIn('id', result.columns)
54
+ self.assertIn('label', result.columns)
55
+ self.assertIn('thumbnail', result.columns)
56
+
57
+ def test_get_painted_domains_formatted(self):
58
+ """Test get_painted_domains with formatted output"""
59
+ result = get_painted_domains(self.template_term, return_dataframe=False, limit=5)
60
+ self.assertIsNotNone(result)
61
+
62
+ if isinstance(result, dict):
63
+ self.assertIn('headers', result)
64
+ self.assertIn('rows', result)
65
+
66
+ def test_get_dataset_images(self):
67
+ """Test get_dataset_images query"""
68
+ result = get_dataset_images(self.dataset_term, return_dataframe=True, limit=10)
69
+ self.assertIsNotNone(result)
70
+
71
+ import pandas as pd
72
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
73
+ print(f"\n✓ Found {len(result)} images in dataset {self.dataset_term}")
74
+ self.assertIn('id', result.columns)
75
+
76
+ def test_get_all_aligned_images(self):
77
+ """Test get_all_aligned_images query"""
78
+ result = get_all_aligned_images(self.template_term, return_dataframe=True, limit=10)
79
+ self.assertIsNotNone(result)
80
+
81
+ import pandas as pd
82
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
83
+ print(f"\n✓ Found {len(result)} aligned images for {self.template_term}")
84
+
85
+ def test_get_aligned_datasets(self):
86
+ """Test get_aligned_datasets query"""
87
+ result = get_aligned_datasets(self.template_term, return_dataframe=True, limit=10)
88
+ self.assertIsNotNone(result)
89
+
90
+ import pandas as pd
91
+ if isinstance(result, pd.DataFrame) and len(result) > 0:
92
+ print(f"\n✓ Found {len(result)} aligned datasets for {self.template_term}")
93
+
94
+ def test_get_all_datasets(self):
95
+ """Test get_all_datasets query (no parameters)"""
96
+ result = get_all_datasets(return_dataframe=True, limit=20)
97
+ self.assertIsNotNone(result)
98
+
99
+ import pandas as pd
100
+ if isinstance(result, pd.DataFrame):
101
+ print(f"\n✓ Found {len(result)} total datasets")
102
+ self.assertGreater(len(result), 0, "Should find at least some datasets")
103
+ self.assertIn('id', result.columns)
104
+ self.assertIn('name', result.columns)
105
+
106
+ def test_get_all_datasets_formatted(self):
107
+ """Test get_all_datasets with formatted output"""
108
+ result = get_all_datasets(return_dataframe=False, limit=10)
109
+ self.assertIsNotNone(result)
110
+
111
+ if isinstance(result, dict):
112
+ self.assertIn('headers', result)
113
+ self.assertIn('rows', result)
114
+
115
+ def test_schema_functions_exist(self):
116
+ """Test that all dataset/template schema functions exist and are callable"""
117
+ schema_functions = [
118
+ PaintedDomains_to_schema,
119
+ DatasetImages_to_schema,
120
+ AllAlignedImages_to_schema,
121
+ AlignedDatasets_to_schema,
122
+ AllDatasets_to_schema
123
+ ]
124
+
125
+ for func in schema_functions:
126
+ self.assertTrue(callable(func), f"{func.__name__} should be callable")
127
+
128
+ def test_limit_parameter(self):
129
+ """Test that limit parameter works correctly"""
130
+ result = get_all_datasets(return_dataframe=True, limit=5)
131
+
132
+ import pandas as pd
133
+ if isinstance(result, pd.DataFrame):
134
+ self.assertLessEqual(len(result), 5, "Result should respect limit parameter")
135
+
136
+
137
+ if __name__ == '__main__':
138
+ unittest.main()