vfbquery 0.2.6__py3-none-any.whl → 0.2.8__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.
vfbquery/vfb_queries.py CHANGED
@@ -5,6 +5,7 @@ from marshmallow import Schema, fields, post_load
5
5
  from typing import List, Tuple
6
6
  import pandas as pd
7
7
  from marshmallow import ValidationError
8
+ import json
8
9
 
9
10
  # Connect to the VFB SOLR server
10
11
  vfb_solr = pysolr.Solr('http://solr.virtualflybrain.org/solr/vfb_json/', always_commit=False, timeout=990)
@@ -13,22 +14,101 @@ vfb_solr = pysolr.Solr('http://solr.virtualflybrain.org/solr/vfb_json/', always_
13
14
  vc = VfbConnect()
14
15
 
15
16
  class Query:
16
- def __init__(self, query, label, function, takes):
17
+ def __init__(self, query, label, function, takes, preview=0, preview_columns=[], preview_results=[], output_format="table", count=-1):
17
18
  self.query = query
18
- self.label = label
19
- self.function = function
20
- self.takes = takes
19
+ self.label = label
20
+ self.function = function
21
+ self.takes = takes
22
+ self.preview = preview
23
+ self.preview_columns = preview_columns
24
+ self.preview_results = preview_results
25
+ self.output_format = output_format
26
+ self.count = count
27
+
28
+ def __str__(self):
29
+ return f"Query: {self.query}, Label: {self.label}, Function: {self.function}, Takes: {self.takes}, Preview: {self.preview}, Preview Columns: {self.preview_columns}, Preview Results: {self.preview_results}, Count: {self.count}"
30
+
31
+ def to_dict(self):
32
+ return {
33
+ "query": self.query,
34
+ "label": self.label,
35
+ "function": self.function,
36
+ "takes": self.takes,
37
+ "preview": self.preview,
38
+ "preview_columns": self.preview_columns,
39
+ "preview_results": self.preview_results,
40
+ "output_format": self.output_format,
41
+ "count": self.count,
42
+ }
43
+
44
+ @classmethod
45
+ def from_dict(cls, data):
46
+ return cls(
47
+ query=data["query"],
48
+ label=data["label"],
49
+ function=data["function"],
50
+ takes=data["takes"],
51
+ preview=data["preview"],
52
+ preview_columns=data["preview_columns"],
53
+ preview_results=data["preview_results"],
54
+ output_format=data.get("output_format", 'table'),
55
+ count=data["count"],
56
+ )
21
57
 
22
58
  class TakesSchema(Schema):
23
59
  short_form = fields.Raw(required=True)
24
- default = fields.String(required=False, allow_none=True)
60
+ default = fields.Raw(required=False, allow_none=True)
25
61
 
26
62
  class QuerySchema(Schema):
27
63
  query = fields.String(required=True)
28
64
  label = fields.String(required=True)
29
65
  function = fields.String(required=True)
30
- takes = fields.Nested(TakesSchema(), many=True)
66
+ takes = fields.Nested(TakesSchema(), required=False, missing={})
67
+ preview = fields.Integer(required=False, missing=0)
68
+ preview_columns = fields.List(fields.String(), required=False, missing=[])
69
+ preview_results = fields.List(fields.Dict(), required=False, missing=[])
70
+ output_format = fields.String(required=False, missing='table')
71
+ count = fields.Integer(required=False, missing=-1)
72
+
73
+ class License:
74
+ def __init__(self, iri, short_form, label, icon, source, source_iri):
75
+ self.iri = iri
76
+ self.short_form = short_form
77
+ self.label = label
78
+ self.icon = icon
79
+ self.source = source
80
+ self.source_iri = source_iri
31
81
 
82
+ class LicenseSchema(Schema):
83
+ iri = fields.String(required=True)
84
+ short_form = fields.String(required=True)
85
+ label = fields.String(required=True)
86
+ icon = fields.String(required=True)
87
+ source = fields.String(required=True)
88
+ source_iri = fields.String(required=True)
89
+
90
+
91
+ class LicenseField(fields.Nested):
92
+ def __init__(self, **kwargs):
93
+ super().__init__(LicenseSchema(), **kwargs)
94
+
95
+ def _serialize(self, value, attr, obj, **kwargs):
96
+ if value is None:
97
+ return value
98
+ if not isinstance(value, License):
99
+ raise ValidationError("Invalid input")
100
+ return {"iri": value.iri
101
+ , "short_form": value.short_form
102
+ , "label": value.label
103
+ ,"icon": value.icon
104
+ , "source": value.source
105
+ , "source_iri": value.source_iri}
106
+
107
+ def _deserialize(self, value, attr, data, **kwargs):
108
+ if value is None:
109
+ return value
110
+ return LicenseSchema().load(value)
111
+
32
112
  class Coordinates:
33
113
  def __init__(self, X, Y, Z):
34
114
  self.X = X
@@ -100,7 +180,7 @@ class ImageSchema(Schema):
100
180
  class ImageField(fields.Nested):
101
181
  def __init__(self, **kwargs):
102
182
  super().__init__(ImageSchema(), **kwargs)
103
-
183
+
104
184
  def _serialize(self, value, attr, obj, **kwargs):
105
185
  if value is None:
106
186
  return value
@@ -120,30 +200,22 @@ class ImageField(fields.Nested):
120
200
  , "type_id": value.type_id
121
201
  , "type_label": value.type_label
122
202
  }
123
-
203
+
124
204
  def _deserialize(self, value, attr, data, **kwargs):
125
205
  if value is None:
126
206
  return value
127
207
  return ImageSchema().load(value)
128
208
 
129
- class QueryField(fields.Nested):
130
- def __init__(self, **kwargs):
131
- super().__init__(QuerySchema, **kwargs)
209
+ class QueryField(fields.Field):
132
210
  def _serialize(self, value, attr, obj, **kwargs):
133
211
  if value is None:
134
- return value
135
- return {"query": value.query
136
- , "label": value.label
137
- , "function": value.function
138
- , "takes": value.takes
139
- , "default": value.default
140
- }
212
+ return None
213
+ return value.to_dict()
141
214
 
142
215
  def _deserialize(self, value, attr, data, **kwargs):
143
- if value is None:
144
- return value
145
- return QuerySchema().load(value)
146
-
216
+ if not isinstance(value, dict):
217
+ raise ValidationError("Invalid input type.")
218
+ return Query.from_dict(value)
147
219
 
148
220
  class TermInfoOutputSchema(Schema):
149
221
  Name = fields.String(required=True)
@@ -151,14 +223,28 @@ class TermInfoOutputSchema(Schema):
151
223
  SuperTypes = fields.List(fields.String(), required=True)
152
224
  Meta = fields.Dict(keys=fields.String(), values=fields.String(), required=True)
153
225
  Tags = fields.List(fields.String(), required=True)
154
- Queries = fields.Raw(required=False) #having issues to serialize
226
+ Queries = fields.List(QueryField(), required=False)
155
227
  IsIndividual = fields.Bool(missing=False, required=False)
156
228
  Images = fields.Dict(keys=fields.String(), values=fields.List(fields.Nested(ImageSchema()), missing={}), required=False, allow_none=True)
157
229
  IsClass = fields.Bool(missing=False, required=False)
158
230
  Examples = fields.Dict(keys=fields.String(), values=fields.List(fields.Nested(ImageSchema()), missing={}), required=False, allow_none=True)
159
231
  IsTemplate = fields.Bool(missing=False, required=False)
160
232
  Domains = fields.Dict(keys=fields.Integer(), values=fields.Nested(ImageSchema()), required=False, allow_none=True)
233
+ Licenses = fields.Dict(keys=fields.Integer(), values=fields.Nested(LicenseSchema()), required=False, allow_none=True)
234
+
235
+ @post_load
236
+ def make_term_info(self, data, **kwargs):
237
+ if "Queries" in data:
238
+ data["Queries"] = [query.to_dict() for query in data["Queries"]]
239
+ return data
161
240
 
241
+ def __str__(self):
242
+ term_info_data = self.make_term_info(self.data)
243
+ if "Queries" in term_info_data:
244
+ term_info_data["Queries"] = [query.to_dict() for query in term_info_data["Queries"]]
245
+ return str(self.dump(term_info_data))
246
+
247
+
162
248
  def term_info_parse_object(results, short_form):
163
249
  termInfo = {}
164
250
  if results.hits > 0 and results.docs and len(results.docs) > 0:
@@ -196,6 +282,75 @@ def term_info_parse_object(results, short_form):
196
282
  pass
197
283
  except AttributeError:
198
284
  print(f"vfbTerm.term.comment: {vfbTerm.term}")
285
+
286
+ if vfbTerm.parents and len(vfbTerm.parents) > 0:
287
+ parents = []
288
+
289
+ # Sort the parents alphabetically
290
+ sorted_parents = sorted(vfbTerm.parents, key=lambda parent: parent.label)
291
+
292
+ for parent in sorted_parents:
293
+ parents.append("[%s](%s)"%(parent.label, parent.short_form))
294
+ termInfo["Meta"]["Types"] = "; ".join(parents)
295
+
296
+ if vfbTerm.relationships and len(vfbTerm.relationships) > 0:
297
+ relationships = []
298
+
299
+ # Group relationships by relation type and remove duplicates
300
+ grouped_relationships = {}
301
+ for relationship in vfbTerm.relationships:
302
+ if relationship.relation.short_form:
303
+ relation_key = (relationship.relation.label, relationship.relation.short_form)
304
+ elif relationship.relation.iri:
305
+ relation_key = (relationship.relation.label, relationship.relation.iri.split('/')[-1])
306
+ elif relationship.relation.label:
307
+ relation_key = (relationship.relation.label, relationship.relation.label)
308
+ object_key = (relationship.object.label, relationship.object.short_form)
309
+ if relation_key not in grouped_relationships:
310
+ grouped_relationships[relation_key] = set()
311
+ grouped_relationships[relation_key].add(object_key)
312
+
313
+ # Sort the grouped_relationships by keys
314
+ sorted_grouped_relationships = dict(sorted(grouped_relationships.items()))
315
+
316
+ # Append the grouped relationships to termInfo
317
+ for relation_key, object_set in sorted_grouped_relationships.items():
318
+ # Sort the object_set by object_key
319
+ sorted_object_set = sorted(list(object_set))
320
+ relation_objects = []
321
+ for object_key in sorted_object_set:
322
+ relation_objects.append("[%s](%s)" % (object_key[0], object_key[1]))
323
+ relationships.append("[%s](%s): %s" % (relation_key[0], relation_key[1], ', '.join(relation_objects)))
324
+ termInfo["Meta"]["Relationships"] = "; ".join(relationships)
325
+
326
+
327
+ if vfbTerm.xrefs and len(vfbTerm.xrefs) > 0:
328
+ xrefs = []
329
+
330
+ # Group xrefs by site
331
+ grouped_xrefs = {}
332
+ for xref in vfbTerm.xrefs:
333
+ site_key = (xref.site.label, xref.homepage, xref.icon)
334
+ link_key = (xref.accession, xref.link())
335
+ if site_key not in grouped_xrefs:
336
+ grouped_xrefs[site_key] = set()
337
+ grouped_xrefs[site_key].add(link_key)
338
+
339
+ # Sort the grouped_xrefs by site_key
340
+ sorted_grouped_xrefs = dict(sorted(grouped_xrefs.items()))
341
+
342
+ # Append the grouped xrefs to termInfo
343
+ for site_key, link_set in sorted_grouped_xrefs.items():
344
+ # Sort the link_set by link_key
345
+ sorted_link_set = sorted(list(link_set))
346
+ links = []
347
+ for link_key in sorted_link_set:
348
+ links.append("[%s](%s)" % (link_key[0], link_key[1]))
349
+ if site_key[2]:
350
+ xrefs.append("![%s](%s) [%s](%s): %s" % (site_key[0], site_key[2], site_key[0], site_key[1], ', '.join(links)))
351
+ else:
352
+ xrefs.append("[%s](%s): %s" % (site_key[0], site_key[1], ', '.join(links)))
353
+ termInfo["Meta"]["Cross References"] = "; ".join(xrefs)
199
354
 
200
355
  # If the term has anatomy channel images, retrieve the images and associated information
201
356
  if vfbTerm.anatomy_channel_image and len(vfbTerm.anatomy_channel_image) > 0:
@@ -217,9 +372,9 @@ def term_info_parse_object(results, short_form):
217
372
  images[image.channel_image.image.template_anatomy.short_form].append(record)
218
373
  termInfo["Examples"] = images
219
374
  # add a query to `queries` list for listing all available images
220
- q = ListAllAvailableImages_to_schemma(termInfo["Name"], vfbTerm.term.core.short_form)
375
+ q = ListAllAvailableImages_to_schema(termInfo["Name"], {"short_form":vfbTerm.term.core.short_form})
221
376
  queries.append(q)
222
-
377
+
223
378
  # If the term has channel images but not anatomy channel images, create thumbnails from channel images.
224
379
  if vfbTerm.channel_image and len(vfbTerm.channel_image) > 0:
225
380
  images = {}
@@ -240,6 +395,19 @@ def term_info_parse_object(results, short_form):
240
395
  images[image.image.template_anatomy.short_form].append(record)
241
396
  # Add the thumbnails to the term info
242
397
  termInfo["Images"] = images
398
+
399
+ if vfbTerm.dataset_license and len(vfbTerm.dataset_license) > 0:
400
+ licenses = {}
401
+ for idx, dataset_license in enumerate(vfbTerm.dataset_license):
402
+ record = {}
403
+ record['iri'] = dataset_license.license.core.iri
404
+ record['short_form'] = dataset_license.license.core.short_form
405
+ record['label'] = dataset_license.license.core.label
406
+ record['icon'] = dataset_license.license.icon
407
+ record['source_iri'] = dataset_license.dataset.core.iri
408
+ record['source'] = dataset_license.dataset.core.label
409
+ licenses[idx] = record
410
+ termInfo["Licenses"] = licenses
243
411
 
244
412
  if vfbTerm.template_channel and vfbTerm.template_channel.channel.short_form:
245
413
  termInfo["IsTemplate"] = True
@@ -271,195 +439,544 @@ def term_info_parse_object(results, short_form):
271
439
  if 'orientation' in image_vars.keys():
272
440
  record['orientation'] = image.orientation
273
441
  images[vfbTerm.template_channel.channel.short_form].append(record)
274
-
442
+
275
443
  # Add the thumbnails to the term info
276
444
  termInfo["Images"] = images
277
-
278
- if vfbTerm.template_domains and len(vfbTerm.template_domains) > 0:
279
- images = {}
280
- termInfo["IsTemplate"] = True
281
- for image in vfbTerm.template_domains:
282
- record = {}
283
- record["id"] = image.anatomical_individual.short_form
284
- label = image.anatomical_individual.label
285
- if image.anatomical_individual.symbol != "" and len(image.anatomical_individual.symbol) > 0:
286
- label = image.anatomical_individual.symbol
287
- record["label"] = label
288
- record["type_id"] = image.anatomical_type.short_form
289
- label = image.anatomical_type.label
290
- if image.anatomical_type.symbol != "" and len(image.anatomical_type.symbol) > 0:
291
- label = image.anatomical_type.symbol
292
- record["type_label"] = label
293
- record["index"] = int(image.index[0])
294
- record["thumbnail"] = image.folder.replace("http://","https://") + "thumbnail.png"
295
- record["thumbnail_transparent"] = image.folder.replace("http://","https://") + "thumbnailT.png"
296
- for key in vars(image).keys():
297
- if "image_" in key and not ("thumbnail" in key or "folder" in key) and len(vars(image)[key]) > 1:
298
- record[key.replace("image_","")] = vars(image)[key].replace("http://","https://")
299
- record["center"] = image.get_center()
300
- images[record["index"]] = record
301
-
302
- # Add the thumbnails to the term info
303
- termInfo["Domains"] = images
304
-
305
- if contains_all_tags(termInfo["SuperTypes"],["Individual","Neuron"]):
306
- q = SimilarMorphologyTo_to_schemma(termInfo["Name"], vfbTerm.term.core.short_form)
307
- queries.append(q)
445
+
446
+ if vfbTerm.template_domains and len(vfbTerm.template_domains) > 0:
447
+ images = {}
448
+ termInfo["IsTemplate"] = True
449
+ for image in vfbTerm.template_domains:
450
+ record = {}
451
+ record["id"] = image.anatomical_individual.short_form
452
+ label = image.anatomical_individual.label
453
+ if image.anatomical_individual.symbol != "" and len(image.anatomical_individual.symbol) > 0:
454
+ label = image.anatomical_individual.symbol
455
+ record["label"] = label
456
+ record["type_id"] = image.anatomical_type.short_form
457
+ label = image.anatomical_type.label
458
+ if image.anatomical_type.symbol != "" and len(image.anatomical_type.symbol) > 0:
459
+ label = image.anatomical_type.symbol
460
+ record["type_label"] = label
461
+ record["index"] = int(image.index[0])
462
+ record["thumbnail"] = image.folder.replace("http://", "https://") + "thumbnail.png"
463
+ record["thumbnail_transparent"] = image.folder.replace("http://", "https://") + "thumbnailT.png"
464
+ for key in vars(image).keys():
465
+ if "image_" in key and not ("thumbnail" in key or "folder" in key) and len(vars(image)[key]) > 1:
466
+ record[key.replace("image_", "")] = vars(image)[key].replace("http://", "https://")
467
+ record["center"] = image.get_center()
468
+ images[record["index"]] = record
469
+
470
+ # Sort the domains by their index and add them to the term info
471
+ sorted_images = {int(key): value for key, value in sorted(images.items(), key=lambda x: x[0])}
472
+ termInfo["Domains"] = sorted_images
473
+
474
+ if contains_all_tags(termInfo["SuperTypes"], ["Individual", "Neuron"]):
475
+ q = SimilarMorphologyTo_to_schema(termInfo["Name"], {"neuron": vfbTerm.term.core.short_form, "similarity_score": "NBLAST_score"})
476
+ queries.append(q)
477
+ if contains_all_tags(termInfo["SuperTypes"], ["Individual", "Neuron", "has_neuron_connectivity"]):
478
+ q = NeuronInputsTo_to_schema(termInfo["Name"], {"neuron_short_form": vfbTerm.term.core.short_form})
479
+ queries.append(q)
308
480
  # Add the queries to the term info
309
481
  termInfo["Queries"] = queries
310
-
311
- print(termInfo)
312
-
482
+
483
+ # print("termInfo object after loading:", termInfo)
484
+ if "Queries" in termInfo:
485
+ termInfo["Queries"] = [query.to_dict() for query in termInfo["Queries"]]
486
+ # print("termInfo object before schema validation:", termInfo)
313
487
  return TermInfoOutputSchema().load(termInfo)
314
488
 
315
- def SimilarMorphologyTo_to_schemma(name, take_default):
316
- query = {}
317
- query["query"] = "SimilarMorphologyTo"
318
- query["label"] = "Find similar neurons to %s"%(name)
319
- query["function"] = "get_similar_neurons"
320
- takes = {}
321
- takes["short_form"] = {}
322
- takes["short_form"]["$and"] = ["Individual","Neuron"]
323
- takes["default"] = take_default
324
- query["takes"] = takes
325
- return query
326
-
327
- def ListAllAvailableImages_to_schemma(name, take_default):
328
- query = {}
329
- query["query"] = "ListAllAvailableImages"
330
- query["label"] = "List all available images of %s"%(name)
331
- query["function"] = "get_instances"
332
- takes = {}
333
- takes["short_form"] = {}
334
- takes["short_form"]["$and"] = ["Class","Anatomy"]
335
- takes["default"] = take_default
336
- query["takes"] = takes
337
- return query
338
-
339
- def get_term_info(short_form: str):
489
+ def NeuronInputsTo_to_schema(name, take_default):
490
+ query = "NeuronInputsTo"
491
+ label = f"Find neurons with synapses into {name}"
492
+ function = "get_individual_neuron_inputs"
493
+ takes = {
494
+ "neuron_short_form": {"$and": ["Individual", "Neuron"]},
495
+ "default": take_default,
496
+ }
497
+ preview = -1
498
+ preview_columns = ["Neurotransmitter", "Weight"]
499
+ output_format = "ribbon"
500
+
501
+ return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns, output_format=output_format)
502
+
503
+ def SimilarMorphologyTo_to_schema(name, take_default):
504
+ query = "SimilarMorphologyTo"
505
+ label = f"Find similar neurons to {name}"
506
+ function = "get_similar_neurons"
507
+ takes = {
508
+ "short_form": {"$and": ["Individual", "Neuron"]},
509
+ "default": take_default,
510
+ }
511
+ preview = 5
512
+ preview_columns = ["id","score","name","tags","thumbnail"]
513
+
514
+ return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns)
515
+
516
+ def ListAllAvailableImages_to_schema(name, take_default):
517
+ query = "ListAllAvailableImages"
518
+ label = f"List all available images of {name}"
519
+ function = "get_instances"
520
+ takes = {
521
+ "short_form": {"$and": ["Class", "Anatomy"]},
522
+ "default": take_default,
523
+ }
524
+ preview = 0
525
+ preview_columns = ["id","label","tags","thumbnail"]
526
+
527
+ return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns)
528
+
529
+ def serialize_solr_output(results):
530
+ # Serialize the sanitized dictionary to JSON
531
+ json_string = json.dumps(results.docs[0], ensure_ascii=False)
532
+ json_string = json_string.replace('\\', '')
533
+ json_string = json_string.replace('"{', '{')
534
+ json_string = json_string.replace('}"', '}')
535
+ json_string = json_string.replace("\'", '-')
536
+ return json_string
537
+
538
+ def get_term_info(short_form: str, preview: bool = False):
340
539
  """
341
540
  Retrieves the term info for the given term short form.
342
541
 
343
542
  :param short_form: short form of the term
344
543
  :return: term info
345
544
  """
545
+ parsed_object = None
346
546
  try:
347
547
  # Search for the term in the SOLR server
348
548
  results = vfb_solr.search('id:' + short_form)
549
+ sanitized_results = serialize_solr_output(results)
550
+ print(sanitized_results)
349
551
  # Check if any results were returned
350
552
  parsed_object = term_info_parse_object(results, short_form)
553
+ term_info = fill_query_results(parsed_object)
554
+ if not term_info:
555
+ print("Failed to fill query preview results!")
556
+ return term_info
351
557
  return parsed_object
352
558
  except ValidationError as e:
353
- # handle the validation error
354
- print("Schemma validation error when parsing response")
559
+ # handle the validation error
560
+ print("Schema validation error when parsing response")
561
+ print("Error details:", e)
562
+ print("Original data:", results)
563
+ print("Parsed object:", parsed_object)
355
564
  except IndexError:
356
565
  print(f"No results found for ID '{short_form}'")
357
- print("Error accessing SOLR server!")
566
+ print("Error accessing SOLR server!")
358
567
 
359
-
360
- def get_instances(short_form: str):
568
+ def get_instances(short_form: str, return_dataframe=True, limit: int = -1):
361
569
  """
362
570
  Retrieves available instances for the given class short form.
363
571
  :param short_form: short form of the class
572
+ :param limit: maximum number of results to return (default -1, returns all results)
364
573
  :return: results rows
365
574
  """
366
575
 
367
- # Define the Cypher query
576
+ # Get the total count of rows
577
+ count_query = f"""
578
+ MATCH (i:Individual:has_image)-[:INSTANCEOF]->(p:Class {{ short_form: '{short_form}' }}),
579
+ (i)<-[:depicts]-(:Individual)-[r:in_register_with]->(:Template)
580
+ RETURN COUNT(r) AS total_count
581
+ """
582
+ count_results = vc.nc.commit_list([count_query])
583
+ count_df = pd.DataFrame.from_records(dict_cursor(count_results))
584
+ total_count = count_df['total_count'][0] if not count_df.empty else 0
585
+
586
+ # Define the main Cypher query
368
587
  query = f"""
369
- MATCH (i:Individual)-[:INSTANCEOF]->(p:Class {{ short_form: '{short_form}' }}),
370
- (i)<-[:depicts]-(:Individual)-[:in_register_with]->(:Template)-[:depicts]->(templ:Template),
588
+ MATCH (i:Individual:has_image)-[:INSTANCEOF]->(p:Class {{ short_form: '{short_form}' }}),
589
+ (i)<-[:depicts]-(:Individual)-[r:in_register_with]->(:Template)-[:depicts]->(templ:Template),
371
590
  (i)-[:has_source]->(ds:DataSet)
372
591
  OPTIONAL MATCH (i)-[rx:database_cross_reference]->(site:Site)
373
592
  OPTIONAL MATCH (ds)-[:license|licence]->(lic:License)
374
- RETURN apoc.text.format("[%s](%s)",[COALESCE(i.symbol[0],i.label),i.short_form]) AS label,
375
- apoc.text.join(i.uniqueFacets, '|') AS tags,
376
- apoc.text.format("[%s](%s)",[COALESCE(p.symbol[0],p.label),p.short_form]) AS parent,
377
- REPLACE(apoc.text.format("[%s](%s)",[COALESCE(site.symbol[0],site.label),site.short_form]), '[null](null)', '') AS source,
378
- REPLACE(apoc.text.format("[%s](%s)",[rx.accession,site.link_base[0] + rx.accession[0]]), '[null](null)', '') AS source_id,
379
- apoc.text.format("[%s](%s)",[COALESCE(templ.symbol[0],templ.label),templ.short_form]) AS template,
380
- apoc.text.format("[%s](%s)",[COALESCE(ds.symbol[0],ds.label),ds.short_form]) AS dataset,
381
- REPLACE(apoc.text.format("[%s](%s)",[COALESCE(lic.symbol[0],lic.label),lic.short_form]), '[null](null)', '') AS license
593
+ RETURN i.short_form as id,
594
+ apoc.text.format("[%s](%s)",[COALESCE(i.symbol[0],i.label),i.short_form]) AS label,
595
+ apoc.text.join(i.uniqueFacets, '|') AS tags,
596
+ apoc.text.format("[%s](%s)",[COALESCE(p.symbol[0],p.label),p.short_form]) AS parent,
597
+ REPLACE(apoc.text.format("[%s](%s)",[COALESCE(site.symbol[0],site.label),site.short_form]), '[null](null)', '') AS source,
598
+ REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0],site.link_base[0] + rx.accession[0]]), '[null](null)', '') AS source_id,
599
+ apoc.text.format("[%s](%s)",[COALESCE(templ.symbol[0],templ.label),templ.short_form]) AS template,
600
+ apoc.text.format("[%s](%s)",[COALESCE(ds.symbol[0],ds.label),ds.short_form]) AS dataset,
601
+ REPLACE(apoc.text.format("[%s](%s)",[COALESCE(lic.symbol[0],lic.label),lic.short_form]), '[null](null)', '') AS license,
602
+ REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[COALESCE(i.symbol[0],i.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), REPLACE(COALESCE(r.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), COALESCE(i.symbol[0],i.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), templ.short_form + "," + i.short_form]), "[![null]( 'null')](null)", "") as thumbnail
603
+ ORDER BY id Desc
382
604
  """
383
605
 
606
+ if limit != -1:
607
+ query += f" LIMIT {limit}"
608
+
384
609
  # Run the query using VFB_connect
385
610
  results = vc.nc.commit_list([query])
386
611
 
387
612
  # Convert the results to a DataFrame
388
613
  df = pd.DataFrame.from_records(dict_cursor(results))
389
614
 
615
+ if return_dataframe:
616
+ return df
617
+
390
618
  # Format the results
391
619
  formatted_results = {
392
620
  "headers": {
621
+ "id": {"title": "Add", "type": "selection_id", "order": -1},
393
622
  "label": {"title": "Name", "type": "markdown", "order": 0, "sort": {0: "Asc"}},
394
623
  "parent": {"title": "Parent Type", "type": "markdown", "order": 1},
395
624
  "template": {"title": "Template", "type": "markdown", "order": 4},
396
625
  "tags": {"title": "Gross Types", "type": "tags", "order": 3},
397
626
  "source": {"title": "Data Source", "type": "markdown", "order": 5},
398
627
  "source_id": {"title": "Data Source", "type": "markdown", "order": 6},
628
+ "dataset": {"title": "Dataset", "type": "markdown", "order": 7},
629
+ "license": {"title": "License", "type": "markdown", "order": 8},
630
+ "thumbnail": {"title": "Thumbnail", "type": "markdown", "order": 9}
399
631
  },
400
- "rows": df.to_dict('records')
632
+ "rows": [
633
+ {
634
+ key: row[key]
635
+ for key in [
636
+ "id",
637
+ "label",
638
+ "tags",
639
+ "parent",
640
+ "source",
641
+ "source_id",
642
+ "template",
643
+ "dataset",
644
+ "license",
645
+ "thumbnail"
646
+ ]
647
+ }
648
+ for row in df.to_dict("records")
649
+ ],
650
+ "count": total_count
401
651
  }
402
652
 
403
653
  return formatted_results
404
-
405
- def get_similar_neurons(short_form: str, similarity_score='NBLAST_score'):
654
+
655
+ def get_templates(limit: int = -1, return_dataframe: bool = False):
656
+ """Get list of templates
657
+
658
+ :param limit: maximum number of results to return (default -1, returns all results)
659
+ :param return_dataframe: Returns pandas dataframe if true, otherwise returns list of dicts.
660
+ :return: list of templates (id, label, tags, source (db) id, accession_in_source) + similarity score.
661
+ :rtype: pandas.DataFrame or list of dicts
662
+
406
663
  """
407
- Retrieves available similar neurons for the given neuron short form.
664
+ count_query = """MATCH (t:Template)<-[:depicts]-(tc:Template)-[r:in_register_with]->(tc:Template)
665
+ RETURN COUNT(DISTINCT t) AS total_count"""
408
666
 
409
- :param short_form: short form of the neuron
410
- :param similarity_score: optionally specify similarity score to choose
411
- :return: results rows
667
+ count_results = vc.nc.commit_list([count_query])
668
+ count_df = pd.DataFrame.from_records(dict_cursor(count_results))
669
+ total_count = count_df['total_count'][0] if not count_df.empty else 0
670
+
671
+ # Define the main Cypher query
672
+ query = f"""
673
+ MATCH (t:Template)-[:INSTANCEOF]->(p:Class),
674
+ (t)<-[:depicts]-(tc:Template)-[r:in_register_with]->(tc:Template),
675
+ (t)-[:has_source]->(ds:DataSet)-[:has_license]->(lic:License)
676
+ RETURN t.short_form as id,
677
+ apoc.text.format("[%s](%s)",[COALESCE(t.symbol[0],t.label),t.short_form]) AS name,
678
+ apoc.text.join(t.uniqueFacets, '|') AS tags,
679
+ apoc.text.format("[%s](%s)",[COALESCE(ds.symbol[0],ds.label),ds.short_form]) AS dataset,
680
+ REPLACE(apoc.text.format("[%s](%s)",[COALESCE(lic.symbol[0],lic.label),lic.short_form]), '[null](null)', '') AS license,
681
+ REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[COALESCE(t.symbol[0],t.label), REPLACE(COALESCE(r.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), COALESCE(t.symbol[0],t.label), t.short_form]), "[![null]( 'null')](null)", "") as thumbnail,
682
+ 99 as order
683
+ ORDER BY id Desc
412
684
  """
413
685
 
414
- df = vc.get_similar_neurons(short_form, similarity_score=similarity_score, return_dataframe=True)
415
-
416
- results = {'headers':
417
- {
418
- 'score': {'title': 'Score', 'type': 'numeric', 'order': 1, 'sort': {0: 'Desc'}},
419
- 'name': {'title': 'Name', 'type': 'markdown', 'order': 1, 'sort': {1: 'Asc'}},
420
- 'tags': {'title': 'Tags', 'type': 'tags', 'order': 2},
421
- 'source': {'title': 'Source', 'type': 'metadata', 'order': 3},
422
- 'source_id': {'title': 'Source ID', 'type': 'metadata', 'order': 4},
423
- },
424
- 'rows': formatDataframe(df).to_dict('records')
425
- }
426
-
686
+ if limit != -1:
687
+ query += f" LIMIT {limit}"
688
+
689
+ # Run the query using VFB_connect
690
+ results = vc.nc.commit_list([query])
691
+
692
+ # Convert the results to a DataFrame
693
+ df = pd.DataFrame.from_records(dict_cursor(results))
694
+
695
+ template_order = ["VFB_00101567","VFB_00200000","VFB_00017894","VFB_00101384","VFB_00050000","VFB_00049000","VFB_00100000","VFB_00030786","VFB_00110000","VFB_00120000"]
696
+
697
+ order = 1
698
+
699
+ for template in template_order:
700
+ df.loc[df['id'] == template, 'order'] = order
701
+ order += 1
702
+
703
+ # Sort the DataFrame by 'order'
704
+ df = df.sort_values('order')
705
+
706
+ if return_dataframe:
707
+ return df
708
+
709
+ # Format the results
710
+ formatted_results = {
711
+ "headers": {
712
+ "id": {"title": "Add", "type": "selection_id", "order": -1},
713
+ "order": {"title": "Order", "type": "numeric", "order": 1, "sort": {0: "Asc"}},
714
+ "name": {"title": "Name", "type": "markdown", "order": 1, "sort": {1: "Asc"}},
715
+ "tags": {"title": "Tags", "type": "tags", "order": 2},
716
+ "thumbnail": {"title": "Thumbnail", "type": "markdown", "order": 9},
717
+ "dataset": {"title": "Dataset", "type": "metadata", "order": 3},
718
+ "license": {"title": "License", "type": "metadata", "order": 4}
719
+ },
720
+ "rows": [
721
+ {
722
+ key: row[key]
723
+ for key in [
724
+ "id",
725
+ "order",
726
+ "name",
727
+ "tags",
728
+ "thumbnail",
729
+ "dataset",
730
+ "license"
731
+ ]
732
+ }
733
+ for row in df.to_dict("records")
734
+ ],
735
+ "count": total_count
736
+ }
737
+ return formatted_results
738
+
739
+ def get_related_anatomy(template_short_form: str, limit: int = -1, return_dataframe: bool = False):
740
+ """
741
+ Retrieve related anatomical structures for a given template.
742
+
743
+ :param template_short_form: The short form of the template to query.
744
+ :param limit: Maximum number of results to return. Default is -1, which returns all results.
745
+ :param return_dataframe: If True, returns results as a pandas DataFrame. Otherwise, returns a list of dicts.
746
+ :return: Related anatomical structures and paths.
747
+ """
748
+
749
+ # Define the Cypher query
750
+ query = f"""
751
+ MATCH (root:Class)<-[:INSTANCEOF]-(t:Template {{short_form:'{template_short_form}'}})<-[:depicts]-(tc:Template)<-[ie:in_register_with]-(c:Individual)-[:depicts]->(image:Individual)-[r:INSTANCEOF]->(anat:Class:Anatomy)
752
+ WHERE exists(ie.index)
753
+ WITH root, anat,r,image
754
+ MATCH p=allshortestpaths((root)<-[:SUBCLASSOF|part_of*..50]-(anat))
755
+ UNWIND nodes(p) as n
756
+ UNWIND nodes(p) as m
757
+ WITH * WHERE id(n) < id(m)
758
+ MATCH path = allShortestPaths( (n)-[:SUBCLASSOF|part_of*..1]-(m) )
759
+ RETURN collect(distinct {{ node_id: id(anat), short_form: anat.short_form, image: image.short_form }}) AS image_nodes, id(root) AS root, collect(path)
760
+ """
761
+
762
+ if limit != -1:
763
+ query += f" LIMIT {limit}"
764
+
765
+ # Execute the query using your database connection (e.g., VFB_connect)
766
+ results = vc.nc.commit_list([query])
767
+
768
+ # Convert the results to a DataFrame (if needed)
769
+ if return_dataframe:
770
+ df = pd.DataFrame.from_records(results)
771
+ return df
772
+
773
+ # Otherwise, return the raw results
427
774
  return results
428
775
 
429
- def formatDataframe(df):
776
+ def get_similar_neurons(neuron, similarity_score='NBLAST_score', return_dataframe=True, limit: int = -1):
777
+ """Get JSON report of individual neurons similar to input neuron
778
+
779
+ :param neuron:
780
+ :param similarity_score: Optionally specify similarity score to chose
781
+ :param return_dataframe: Returns pandas dataframe if true, otherwise returns list of dicts.
782
+ :param limit: maximum number of results to return (default -1, returns all results)
783
+ :return: list of similar neurons (id, label, tags, source (db) id, accession_in_source) + similarity score.
784
+ :rtype: pandas.DataFrame or list of dicts
785
+
430
786
  """
431
- Merge label/id pairs into a markdown link and update column names.
787
+ count_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1)-[r:has_similar_morphology_to]-(n2)-[:INSTANCEOF]->(c2:Class)
788
+ WHERE n1.short_form = '{neuron}' and exists(r.{similarity_score})
789
+ RETURN COUNT(DISTINCT n2) AS total_count"""
790
+
791
+ count_results = vc.nc.commit_list([count_query])
792
+ count_df = pd.DataFrame.from_records(dict_cursor(count_results))
793
+ total_count = count_df['total_count'][0] if not count_df.empty else 0
794
+
795
+ main_query = f"""MATCH (c1:Class)<-[:INSTANCEOF]-(n1)-[r:has_similar_morphology_to]-(n2)-[:INSTANCEOF]->(c2:Class)
796
+ WHERE n1.short_form = '{neuron}' and exists(r.{similarity_score})
797
+ WITH c1, n1, r, n2, c2
798
+ OPTIONAL MATCH (n2)-[rx:database_cross_reference]->(site:Site)
799
+ WHERE site.is_data_source
800
+ WITH n2, r, c2, rx, site
801
+ OPTIONAL MATCH (n2)<-[:depicts]-(:Individual)-[ri:in_register_with]->(:Template)-[:depicts]->(templ:Template)
802
+ RETURN DISTINCT n2.short_form as id,
803
+ apoc.text.format("[%s](%s)", [n2.label, n2.short_form]) AS name,
804
+ r.{similarity_score}[0] AS score,
805
+ apoc.text.join(n2.uniqueFacets, '|') AS tags,
806
+ REPLACE(apoc.text.format("[%s](%s)",[COALESCE(site.symbol[0],site.label),site.short_form]), '[null](null)', '') AS source,
807
+ REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0], (site.link_base[0] + rx.accession[0])]), '[null](null)', '') AS source_id,
808
+ REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[COALESCE(n2.symbol[0],n2.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), REPLACE(COALESCE(ri.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), COALESCE(n2.symbol[0],n2.label) + " aligned to " + COALESCE(templ.symbol[0],templ.label), templ.short_form + "," + n2.short_form]), "[![null]( 'null')](null)", "") as thumbnail
809
+ ORDER BY score DESC"""
810
+
811
+ if limit != -1:
812
+ main_query += f" LIMIT {limit}"
813
+
814
+ # Run the query using VFB_connect
815
+ results = vc.nc.commit_list([main_query])
432
816
 
433
- :param df: pandas DataFrame
434
- :return: pandas DataFrame with merged label/id pairs in 'label' and 'parent' columns
817
+ # Convert the results to a DataFrame
818
+ df = pd.DataFrame.from_records(dict_cursor(results))
819
+
820
+ if return_dataframe:
821
+ return df
822
+ else:
823
+ formatted_results = {
824
+ "headers": {
825
+ "id": {"title": "Add", "type": "selection_id", "order": -1},
826
+ "score": {"title": "Score", "type": "numeric", "order": 1, "sort": {0: "Desc"}},
827
+ "name": {"title": "Name", "type": "markdown", "order": 1, "sort": {1: "Asc"}},
828
+ "tags": {"title": "Tags", "type": "tags", "order": 2},
829
+ "source": {"title": "Source", "type": "metadata", "order": 3},
830
+ "source_id": {"title": "Source ID", "type": "metadata", "order": 4},
831
+ "thumbnail": {"title": "Thumbnail", "type": "markdown", "order": 9}
832
+ },
833
+ "rows": [
834
+ {
835
+ key: row[key]
836
+ for key in [
837
+ "id",
838
+ "name",
839
+ "score",
840
+ "tags",
841
+ "source",
842
+ "source_id",
843
+ "thumbnail"
844
+ ]
845
+ }
846
+ for row in df.to_dict("records")
847
+ ],
848
+ "count": total_count
849
+ }
850
+ return formatted_results
851
+
852
+ def get_individual_neuron_inputs(neuron_short_form: str, return_dataframe=True, limit: int = -1, summary_mode: bool = False):
435
853
  """
436
- if 'label' in df.columns and 'id' in df.columns:
437
- # Merge label/id pairs for both label/id and parent_label/parent_id columns
438
- df['label'] = df.apply(lambda row: '[%s](%s)' % (row['label'], row['id']), axis=1)
439
- # Drop the original label/id columns
440
- df.drop(columns=['id'], inplace=True)
441
- if 'parent_label' in df.columns and 'parent_id' in df.columns:
442
- df['parent'] = df.apply(lambda row: '[%s](%s)' % (row['parent_label'], row['parent_id']), axis=1)
443
- # Drop the original parent_label/parent_id columns
444
- df.drop(columns=['parent_label', 'parent_id'], inplace=True)
445
- if 'tags' in df.columns:
446
- # Check tags is a list
447
- def merge_tags(tags):
448
- if isinstance(tags, str):
449
- tags_list = tags.split('|')
450
- return tags_list
451
- else:
452
- return tags
453
- df['tags'] = df['tags'].apply(merge_tags)
454
- # Rename column headers if they occur
455
- df = replace_column_header(df, 'datasource', 'source')
456
- df = replace_column_header(df, 'accession', 'source')
457
- df = replace_column_header(df, 'source_id', 'source')
458
- df = replace_column_header(df, 'accession_in_source', 'source_id')
459
- df = replace_column_header(df, 'NBLAST_score', 'score')
854
+ Retrieve neurons that have synapses into the specified neuron, along with the neurotransmitter
855
+ types, and additional information about the neurons.
856
+
857
+ :param neuron_short_form: The short form identifier of the neuron to query.
858
+ :param return_dataframe: If True, returns results as a pandas DataFrame. Otherwise, returns a dictionary.
859
+ :param limit: Maximum number of results to return. Default is -1, which returns all results.
860
+ :param summary_mode: If True, returns a preview of the results with summed weights for each neurotransmitter type.
861
+ :return: Neurons, neurotransmitter types, and additional neuron information.
862
+ """
863
+
864
+ # Define the common part of the Cypher query
865
+ query_common = f"""
866
+ MATCH (a:has_neuron_connectivity {{short_form:'{neuron_short_form}'}})<-[r:synapsed_to]-(b:has_neuron_connectivity)
867
+ UNWIND(labels(b)) as l
868
+ WITH * WHERE l contains "ergic"
869
+ OPTIONAL MATCH (c:Class:Neuron) WHERE c.short_form starts with "FBbt_" AND toLower(c.label)=toLower(l+" neuron")
870
+ """
871
+ if not summary_mode:
872
+ count_query = f"""{query_common}
873
+ RETURN COUNT(DISTINCT b) AS total_count"""
874
+ else:
875
+ count_query = f"""{query_common}
876
+ RETURN COUNT(DISTINCT c) AS total_count"""
877
+
878
+ count_results = vc.nc.commit_list([count_query])
879
+ count_df = pd.DataFrame.from_records(dict_cursor(count_results))
880
+ total_count = count_df['total_count'][0] if not count_df.empty else 0
881
+
882
+ # Define the part of the query for normal mode
883
+ query_normal = f"""
884
+ OPTIONAL MATCH (b)-[:INSTANCEOF]->(neuronType:Class),
885
+ (b)<-[:depicts]-(imageChannel:Individual)-[image:in_register_with]->(templateChannel:Template)-[:depicts]->(templ:Template),
886
+ (imageChannel)-[:is_specified_output_of]->(imagingTechnique:Class)
887
+ RETURN
888
+ b.short_form as id,
889
+ apoc.text.format("[%s](%s)", [l, c.short_form]) as Neurotransmitter,
890
+ sum(r.weight[0]) as Weight,
891
+ apoc.text.format("[%s](%s)", [b.label, b.short_form]) as Name,
892
+ apoc.text.format("[%s](%s)", [neuronType.label, neuronType.short_form]) as Type,
893
+ apoc.text.join(b.uniqueFacets, '|') as Gross_Type,
894
+ apoc.text.join(collect(apoc.text.format("[%s](%s)", [templ.label, templ.short_form])), ', ') as Template_Space,
895
+ apoc.text.format("[%s](%s)", [imagingTechnique.label, imagingTechnique.short_form]) as Imaging_Technique,
896
+ apoc.text.join(collect(REPLACE(apoc.text.format("[![%s](%s '%s')](%s)",[COALESCE(b.symbol[0],b.label), REPLACE(COALESCE(image.thumbnail[0],""),"thumbnailT.png","thumbnail.png"), COALESCE(b.symbol[0],b.label), b.short_form]), "[![null]( 'null')](null)", "")), ' | ') as Images
897
+ ORDER BY Weight Desc
898
+ """
899
+
900
+ # Define the part of the query for preview mode
901
+ query_preview = f"""
902
+ RETURN DISTINCT c.short_form as id,
903
+ apoc.text.format("[%s](%s)", [l, c.short_form]) as Neurotransmitter,
904
+ sum(r.weight[0]) as Weight
905
+ ORDER BY Weight Desc
906
+ """
907
+
908
+ # Choose the appropriate part of the query based on the summary_mode parameter
909
+ query = query_common + (query_preview if summary_mode else query_normal)
910
+
911
+ if limit != -1 and not summary_mode:
912
+ query += f" LIMIT {limit}"
913
+
914
+ # Execute the query using your database connection (e.g., vc.nc)
915
+ results = vc.nc.commit_list([query])
916
+
917
+ # Convert the results to a DataFrame
918
+ df = pd.DataFrame.from_records(dict_cursor(results))
919
+
920
+ # If return_dataframe is True, return the results as a DataFrame
921
+ if return_dataframe:
922
+ return df
923
+
924
+ # Format the results for the preview
925
+ if not summary_mode:
926
+ results = {
927
+ "headers": {
928
+ "id": {"title": "ID", "type": "text", "order": -1},
929
+ "Neurotransmitter": {"title": "Neurotransmitter", "type": "markdown", "order": 0},
930
+ "Weight": {"title": "Weight", "type": "numeric", "order": 1},
931
+ "Name": {"title": "Name", "type": "markdown", "order": 2},
932
+ "Type": {"title": "Type", "type": "markdown", "order": 3},
933
+ "Gross_Type": {"title": "Gross Type", "type": "text", "order": 4},
934
+ "Template_Space": {"title": "Template Space", "type": "markdown", "order": 5},
935
+ "Imaging_Technique": {"title": "Imaging Technique", "type": "markdown", "order": 6},
936
+ "Images": {"title": "Images", "type": "markdown", "order": 7}
937
+ },
938
+ "rows": [
939
+ {
940
+ key: row[key]
941
+ for key in [
942
+ "id",
943
+ "Neurotransmitter",
944
+ "Weight",
945
+ "Name",
946
+ "Type",
947
+ "Gross_Type",
948
+ "Template_Space",
949
+ "Imaging_Technique",
950
+ "Images"
951
+ ]
952
+ }
953
+ for row in df.to_dict("records")
954
+ ],
955
+ "count": total_count
956
+ }
957
+ else:
958
+ results = {
959
+ "headers": {
960
+ "id": {"title": "ID", "type": "text", "order": -1},
961
+ "Neurotransmitter": {"title": "Neurotransmitter", "type": "markdown", "order": 0},
962
+ "Weight": {"title": "Weight", "type": "numeric", "order": 1},
963
+ },
964
+ "rows": [
965
+ {
966
+ key: row[key]
967
+ for key in [
968
+ "id",
969
+ "Neurotransmitter",
970
+ "Weight",
971
+ ]
972
+ }
973
+ for row in df.to_dict("records")
974
+ ],
975
+ "count": total_count
976
+ }
460
977
 
461
- # Return the updated DataFrame
462
- return df
978
+ return results
979
+
463
980
 
464
981
  def contains_all_tags(lst: List[str], tags: List[str]) -> bool:
465
982
  """
@@ -471,15 +988,63 @@ def contains_all_tags(lst: List[str], tags: List[str]) -> bool:
471
988
  """
472
989
  return all(tag in lst for tag in tags)
473
990
 
474
- def replace_column_header(df, original_col, replacement_col):
475
- """
476
- Replaces a given original column header with a replacement column header.
477
-
478
- :param df: pandas DataFrame
479
- :param original_col: str, the original column header to replace
480
- :param replacement_col: str, the replacement column header
481
- :return: pandas DataFrame with replaced column header
482
- """
483
- if original_col in df.columns:
484
- df.rename(columns={original_col: replacement_col}, inplace=True)
485
- return df
991
+ def fill_query_results(term_info):
992
+ for query in term_info['Queries']:
993
+ # print(f"Query Keys:{query.keys()}")
994
+
995
+ if "preview" in query.keys() and (query['preview'] > 0 or query['count'] < 0) and query['count'] != 0:
996
+ function = globals().get(query['function'])
997
+ summary_mode = query.get('output_format', 'table') == 'ribbon'
998
+
999
+ if function:
1000
+ # print(f"Function {query['function']} found")
1001
+
1002
+ # Unpack the default dictionary and pass its contents as arguments
1003
+ function_args = query['takes'].get("default", {})
1004
+ # print(f"Function args: {function_args}")
1005
+
1006
+ # Modify this line to use the correct arguments and pass the default arguments
1007
+ if summary_mode:
1008
+ result = function(return_dataframe=False, limit=query['preview'], summary_mode=summary_mode, **function_args)
1009
+ else:
1010
+ result = function(return_dataframe=False, limit=query['preview'], **function_args)
1011
+ # print(f"Function result: {result}")
1012
+
1013
+ # Filter columns based on preview_columns
1014
+ filtered_result = []
1015
+ filtered_headers = {}
1016
+
1017
+ if isinstance(result, dict) and 'rows' in result:
1018
+ for item in result['rows']:
1019
+ if 'preview_columns' in query.keys() and len(query['preview_columns']) > 0:
1020
+ filtered_item = {col: item[col] for col in query['preview_columns']}
1021
+ else:
1022
+ filtered_item = item
1023
+ filtered_result.append(filtered_item)
1024
+
1025
+ if 'headers' in result:
1026
+ if 'preview_columns' in query.keys() and len(query['preview_columns']) > 0:
1027
+ filtered_headers = {col: result['headers'][col] for col in query['preview_columns']}
1028
+ else:
1029
+ filtered_headers = result['headers']
1030
+ elif isinstance(result, list) and all(isinstance(item, dict) for item in result):
1031
+ for item in result:
1032
+ if 'preview_columns' in query.keys() and len(query['preview_columns']) > 0:
1033
+ filtered_item = {col: item[col] for col in query['preview_columns']}
1034
+ else:
1035
+ filtered_item = item
1036
+ filtered_result.append(filtered_item)
1037
+ elif isinstance(result, pd.DataFrame):
1038
+ filtered_result = result[query['preview_columns']].to_dict('records')
1039
+ else:
1040
+ print(f"Unsupported result format for filtering columns in {query['function']}")
1041
+
1042
+ query['preview_results'] = {'headers': filtered_headers, 'rows': filtered_result}
1043
+ query['count'] = result['count']
1044
+ # print(f"Filtered result: {filtered_result}")
1045
+ else:
1046
+ print(f"Function {query['function']} not found")
1047
+ else:
1048
+ print("Preview key not found or preview is 0")
1049
+ return term_info
1050
+