vfbquery 0.3.4__py3-none-any.whl → 0.4.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.
- test/readme_parser.py +29 -1
- test/term_info_queries_test.py +58 -0
- test/test_default_caching.py +173 -0
- test/test_examples_diff.py +6 -1
- vfbquery/__init__.py +83 -1
- vfbquery/cache_enhancements.py +465 -0
- vfbquery/cached_functions.py +227 -0
- vfbquery/solr_cache_integration.py +212 -0
- vfbquery/solr_fetcher.py +47 -3
- vfbquery/solr_result_cache.py +659 -0
- vfbquery/vfb_queries.py +315 -73
- {vfbquery-0.3.4.dist-info → vfbquery-0.4.1.dist-info}/METADATA +16 -16
- vfbquery-0.4.1.dist-info/RECORD +19 -0
- vfbquery-0.3.4.dist-info/RECORD +0 -14
- {vfbquery-0.3.4.dist-info → vfbquery-0.4.1.dist-info}/LICENSE +0 -0
- {vfbquery-0.3.4.dist-info → vfbquery-0.4.1.dist-info}/WHEEL +0 -0
- {vfbquery-0.3.4.dist-info → vfbquery-0.4.1.dist-info}/top_level.txt +0 -0
vfbquery/vfb_queries.py
CHANGED
|
@@ -9,6 +9,8 @@ import pandas as pd
|
|
|
9
9
|
from marshmallow import ValidationError
|
|
10
10
|
import json
|
|
11
11
|
import numpy as np
|
|
12
|
+
from urllib.parse import unquote
|
|
13
|
+
from .solr_result_cache import with_solr_cache
|
|
12
14
|
|
|
13
15
|
# Custom JSON encoder to handle NumPy and pandas types
|
|
14
16
|
class NumpyEncoder(json.JSONEncoder):
|
|
@@ -58,6 +60,23 @@ vfb_solr = pysolr.Solr('http://solr.virtualflybrain.org/solr/vfb_json/', always_
|
|
|
58
60
|
# Replace VfbConnect with SolrTermInfoFetcher
|
|
59
61
|
vc = SolrTermInfoFetcher()
|
|
60
62
|
|
|
63
|
+
def initialize_vfb_connect():
|
|
64
|
+
"""
|
|
65
|
+
Initialize VFB_connect by triggering the lazy load of the vfb and nc properties.
|
|
66
|
+
This causes VFB_connect to cache all terms, which takes ~95 seconds on first call.
|
|
67
|
+
Subsequent calls to functions using vc.nc will be fast.
|
|
68
|
+
|
|
69
|
+
:return: True if initialization successful, False otherwise
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
# Access the properties to trigger lazy loading
|
|
73
|
+
_ = vc.vfb
|
|
74
|
+
_ = vc.nc
|
|
75
|
+
return True
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f"Failed to initialize VFB_connect: {e}")
|
|
78
|
+
return False
|
|
79
|
+
|
|
61
80
|
class Query:
|
|
62
81
|
def __init__(self, query, label, function, takes, preview=0, preview_columns=[], preview_results=[], output_format="table", count=-1):
|
|
63
82
|
self.query = query
|
|
@@ -293,19 +312,19 @@ class TermInfoOutputSchema(Schema):
|
|
|
293
312
|
|
|
294
313
|
def encode_brackets(text):
|
|
295
314
|
"""
|
|
296
|
-
Encodes brackets in the given text.
|
|
315
|
+
Encodes square brackets in the given text to prevent breaking markdown link syntax.
|
|
316
|
+
Parentheses are NOT encoded as they don't break markdown syntax.
|
|
297
317
|
|
|
298
318
|
:param text: The text to encode.
|
|
299
|
-
:return: The text with brackets encoded.
|
|
319
|
+
:return: The text with square brackets encoded.
|
|
300
320
|
"""
|
|
301
|
-
return (text.replace('
|
|
302
|
-
.replace(')', '%29')
|
|
303
|
-
.replace('[', '%5B')
|
|
321
|
+
return (text.replace('[', '%5B')
|
|
304
322
|
.replace(']', '%5D'))
|
|
305
323
|
|
|
306
324
|
def encode_markdown_links(df, columns):
|
|
307
325
|
"""
|
|
308
|
-
Encodes brackets in the labels
|
|
326
|
+
Encodes brackets in the labels within markdown links, leaving the link syntax intact.
|
|
327
|
+
Does NOT encode alt text in linked images ([(...)] format).
|
|
309
328
|
:param df: DataFrame containing the query results.
|
|
310
329
|
:param columns: List of column names to apply encoding to.
|
|
311
330
|
"""
|
|
@@ -314,28 +333,10 @@ def encode_markdown_links(df, columns):
|
|
|
314
333
|
return label
|
|
315
334
|
|
|
316
335
|
try:
|
|
317
|
-
#
|
|
336
|
+
# Skip linked images (format: [](link))
|
|
337
|
+
# These should NOT be encoded
|
|
318
338
|
if label.startswith("[
|
|
321
|
-
if len(parts) < 2:
|
|
322
|
-
return label
|
|
323
|
-
|
|
324
|
-
image_part = parts[0]
|
|
325
|
-
link_part = parts[1]
|
|
326
|
-
|
|
327
|
-
# Process the image part
|
|
328
|
-
image_parts = image_part.split("](")
|
|
329
|
-
if len(image_parts) < 2:
|
|
330
|
-
return label
|
|
331
|
-
|
|
332
|
-
alt_text = image_parts[0][3:] # Remove the "[]({link_part}"
|
|
338
|
-
return encoded_label
|
|
339
|
+
return label
|
|
339
340
|
|
|
340
341
|
# Process regular markdown links
|
|
341
342
|
elif label.startswith("[") and "](" in label:
|
|
@@ -837,9 +838,11 @@ def serialize_solr_output(results):
|
|
|
837
838
|
json_string = json_string.replace("\'", '-')
|
|
838
839
|
return json_string
|
|
839
840
|
|
|
841
|
+
@with_solr_cache('term_info')
|
|
840
842
|
def get_term_info(short_form: str, preview: bool = False):
|
|
841
843
|
"""
|
|
842
844
|
Retrieves the term info for the given term short form.
|
|
845
|
+
Results are cached in SOLR for 3 months to improve performance.
|
|
843
846
|
|
|
844
847
|
:param short_form: short form of the term
|
|
845
848
|
:return: term info
|
|
@@ -851,11 +854,33 @@ def get_term_info(short_form: str, preview: bool = False):
|
|
|
851
854
|
# Check if any results were returned
|
|
852
855
|
parsed_object = term_info_parse_object(results, short_form)
|
|
853
856
|
if parsed_object:
|
|
854
|
-
|
|
855
|
-
if
|
|
856
|
-
|
|
857
|
+
# Only try to fill query results if there are queries to fill
|
|
858
|
+
if parsed_object.get('Queries') and len(parsed_object['Queries']) > 0:
|
|
859
|
+
try:
|
|
860
|
+
term_info = fill_query_results(parsed_object)
|
|
861
|
+
if term_info:
|
|
862
|
+
return term_info
|
|
863
|
+
else:
|
|
864
|
+
print("Failed to fill query preview results!")
|
|
865
|
+
# Set default values for queries when fill_query_results fails
|
|
866
|
+
for query in parsed_object.get('Queries', []):
|
|
867
|
+
# Set default preview_results structure
|
|
868
|
+
query['preview_results'] = {'headers': query.get('preview_columns', ['id', 'label', 'tags', 'thumbnail']), 'rows': []}
|
|
869
|
+
# Set count to 0 when we can't get the real count
|
|
870
|
+
query['count'] = 0
|
|
871
|
+
return parsed_object
|
|
872
|
+
except Exception as e:
|
|
873
|
+
print(f"Error filling query results (setting default values): {e}")
|
|
874
|
+
# Set default values for queries when fill_query_results fails
|
|
875
|
+
for query in parsed_object.get('Queries', []):
|
|
876
|
+
# Set default preview_results structure
|
|
877
|
+
query['preview_results'] = {'headers': query.get('preview_columns', ['id', 'label', 'tags', 'thumbnail']), 'rows': []}
|
|
878
|
+
# Set count to 0 when we can't get the real count
|
|
879
|
+
query['count'] = 0
|
|
880
|
+
return parsed_object
|
|
881
|
+
else:
|
|
882
|
+
# No queries to fill, return parsed object directly
|
|
857
883
|
return parsed_object
|
|
858
|
-
return parsed_object
|
|
859
884
|
else:
|
|
860
885
|
print(f"No valid term info found for ID '{short_form}'")
|
|
861
886
|
return None
|
|
@@ -880,49 +905,253 @@ def get_term_info(short_form: str, preview: bool = False):
|
|
|
880
905
|
print(f"Unexpected error when retrieving term info: {type(e).__name__}: {e}")
|
|
881
906
|
return parsed_object
|
|
882
907
|
|
|
908
|
+
@with_solr_cache('instances')
|
|
883
909
|
def get_instances(short_form: str, return_dataframe=True, limit: int = -1):
|
|
884
910
|
"""
|
|
885
911
|
Retrieves available instances for the given class short form.
|
|
912
|
+
Uses SOLR term_info data when Neo4j is unavailable (fallback mode).
|
|
886
913
|
:param short_form: short form of the class
|
|
887
914
|
:param limit: maximum number of results to return (default -1, returns all results)
|
|
888
915
|
:return: results rows
|
|
889
916
|
"""
|
|
917
|
+
|
|
918
|
+
try:
|
|
919
|
+
# Try to use original Neo4j implementation first
|
|
920
|
+
# Get the total count of rows
|
|
921
|
+
count_query = f"""
|
|
922
|
+
MATCH (i:Individual:has_image)-[:INSTANCEOF]->(p:Class {{ short_form: '{short_form}' }}),
|
|
923
|
+
(i)<-[:depicts]-(:Individual)-[r:in_register_with]->(:Template)
|
|
924
|
+
RETURN COUNT(r) AS total_count
|
|
925
|
+
"""
|
|
926
|
+
count_results = vc.nc.commit_list([count_query])
|
|
927
|
+
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
928
|
+
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
929
|
+
|
|
930
|
+
# Define the main Cypher query
|
|
931
|
+
query = f"""
|
|
932
|
+
MATCH (i:Individual:has_image)-[:INSTANCEOF]->(p:Class {{ short_form: '{short_form}' }}),
|
|
933
|
+
(i)<-[:depicts]-(:Individual)-[r:in_register_with]->(:Template)-[:depicts]->(templ:Template),
|
|
934
|
+
(i)-[:has_source]->(ds:DataSet)
|
|
935
|
+
OPTIONAL MATCH (i)-[rx:database_cross_reference]->(site:Site)
|
|
936
|
+
OPTIONAL MATCH (ds)-[:license|licence]->(lic:License)
|
|
937
|
+
RETURN i.short_form as id,
|
|
938
|
+
apoc.text.format("[%s](%s)",[COALESCE(i.symbol[0],i.label),i.short_form]) AS label,
|
|
939
|
+
apoc.text.join(i.uniqueFacets, '|') AS tags,
|
|
940
|
+
apoc.text.format("[%s](%s)",[COALESCE(p.symbol[0],p.label),p.short_form]) AS parent,
|
|
941
|
+
REPLACE(apoc.text.format("[%s](%s)",[COALESCE(site.symbol[0],site.label),site.short_form]), '[null](null)', '') AS source,
|
|
942
|
+
REPLACE(apoc.text.format("[%s](%s)",[rx.accession[0],site.link_base[0] + rx.accession[0]]), '[null](null)', '') AS source_id,
|
|
943
|
+
apoc.text.format("[%s](%s)",[COALESCE(templ.symbol[0],templ.label),templ.short_form]) AS template,
|
|
944
|
+
apoc.text.format("[%s](%s)",[COALESCE(ds.symbol[0],ds.label),ds.short_form]) AS dataset,
|
|
945
|
+
REPLACE(apoc.text.format("[%s](%s)",[COALESCE(lic.symbol[0],lic.label),lic.short_form]), '[null](null)', '') AS license,
|
|
946
|
+
REPLACE(apoc.text.format("[](%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)", "") as thumbnail
|
|
947
|
+
ORDER BY id Desc
|
|
948
|
+
"""
|
|
949
|
+
|
|
950
|
+
if limit != -1:
|
|
951
|
+
query += f" LIMIT {limit}"
|
|
952
|
+
|
|
953
|
+
# Run the query using VFB_connect
|
|
954
|
+
results = vc.nc.commit_list([query])
|
|
955
|
+
|
|
956
|
+
# Convert the results to a DataFrame
|
|
957
|
+
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
890
958
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
"""
|
|
897
|
-
count_results = vc.nc.commit_list([count_query])
|
|
898
|
-
count_df = pd.DataFrame.from_records(get_dict_cursor()(count_results))
|
|
899
|
-
total_count = count_df['total_count'][0] if not count_df.empty else 0
|
|
959
|
+
columns_to_encode = ['label', 'parent', 'source', 'source_id', 'template', 'dataset', 'license', 'thumbnail']
|
|
960
|
+
df = encode_markdown_links(df, columns_to_encode)
|
|
961
|
+
|
|
962
|
+
if return_dataframe:
|
|
963
|
+
return df
|
|
900
964
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
965
|
+
# Format the results
|
|
966
|
+
formatted_results = {
|
|
967
|
+
"headers": _get_instances_headers(),
|
|
968
|
+
"rows": [
|
|
969
|
+
{
|
|
970
|
+
key: row[key]
|
|
971
|
+
for key in [
|
|
972
|
+
"id",
|
|
973
|
+
"label",
|
|
974
|
+
"tags",
|
|
975
|
+
"parent",
|
|
976
|
+
"source",
|
|
977
|
+
"source_id",
|
|
978
|
+
"template",
|
|
979
|
+
"dataset",
|
|
980
|
+
"license",
|
|
981
|
+
"thumbnail"
|
|
982
|
+
]
|
|
983
|
+
}
|
|
984
|
+
for row in safe_to_dict(df)
|
|
985
|
+
],
|
|
986
|
+
"count": total_count
|
|
987
|
+
}
|
|
920
988
|
|
|
921
|
-
|
|
922
|
-
|
|
989
|
+
return formatted_results
|
|
990
|
+
|
|
991
|
+
except Exception as e:
|
|
992
|
+
# Fallback to SOLR-based implementation when Neo4j is unavailable
|
|
993
|
+
print(f"Neo4j unavailable ({e}), using SOLR fallback for get_instances")
|
|
994
|
+
return _get_instances_from_solr(short_form, return_dataframe, limit)
|
|
923
995
|
|
|
924
|
-
|
|
925
|
-
|
|
996
|
+
def _get_instances_from_solr(short_form: str, return_dataframe=True, limit: int = -1):
|
|
997
|
+
"""
|
|
998
|
+
SOLR-based fallback implementation for get_instances.
|
|
999
|
+
Extracts instance data from term_info anatomy_channel_image array.
|
|
1000
|
+
"""
|
|
1001
|
+
try:
|
|
1002
|
+
# Get term_info data from SOLR
|
|
1003
|
+
term_info_results = vc.get_TermInfo([short_form], return_dataframe=False)
|
|
1004
|
+
|
|
1005
|
+
if len(term_info_results) == 0:
|
|
1006
|
+
# Return empty results with proper structure
|
|
1007
|
+
if return_dataframe:
|
|
1008
|
+
return pd.DataFrame()
|
|
1009
|
+
return {
|
|
1010
|
+
"headers": _get_instances_headers(),
|
|
1011
|
+
"rows": [],
|
|
1012
|
+
"count": 0
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
term_info = term_info_results[0]
|
|
1016
|
+
anatomy_images = term_info.get('anatomy_channel_image', [])
|
|
1017
|
+
|
|
1018
|
+
# Apply limit if specified
|
|
1019
|
+
if limit != -1 and limit > 0:
|
|
1020
|
+
anatomy_images = anatomy_images[:limit]
|
|
1021
|
+
|
|
1022
|
+
# Convert anatomy_channel_image to instance rows with rich data
|
|
1023
|
+
rows = []
|
|
1024
|
+
for img in anatomy_images:
|
|
1025
|
+
anatomy = img.get('anatomy', {})
|
|
1026
|
+
channel_image = img.get('channel_image', {})
|
|
1027
|
+
image_info = channel_image.get('image', {}) if channel_image else {}
|
|
1028
|
+
template_anatomy = image_info.get('template_anatomy', {}) if image_info else {}
|
|
1029
|
+
|
|
1030
|
+
# Extract tags from unique_facets (matching original Neo4j format and ordering)
|
|
1031
|
+
unique_facets = anatomy.get('unique_facets', [])
|
|
1032
|
+
anatomy_types = anatomy.get('types', [])
|
|
1033
|
+
|
|
1034
|
+
# Create ordered list matching the expected Neo4j format
|
|
1035
|
+
# Based on test diff, expected order and tags: Nervous_system, Adult, Visual_system, Synaptic_neuropil_domain
|
|
1036
|
+
# Note: We exclude 'Synaptic_neuropil' as it doesn't appear in expected output
|
|
1037
|
+
ordered_tags = []
|
|
1038
|
+
for tag_type in ['Nervous_system', 'Adult', 'Visual_system', 'Synaptic_neuropil_domain']:
|
|
1039
|
+
if tag_type in anatomy_types or tag_type in unique_facets:
|
|
1040
|
+
ordered_tags.append(tag_type)
|
|
1041
|
+
|
|
1042
|
+
# Use the ordered tags to match expected format
|
|
1043
|
+
tags = '|'.join(ordered_tags)
|
|
1044
|
+
|
|
1045
|
+
# Extract thumbnail URL and convert to HTTPS
|
|
1046
|
+
thumbnail_url = image_info.get('image_thumbnail', '') if image_info else ''
|
|
1047
|
+
if thumbnail_url:
|
|
1048
|
+
# Replace http with https and thumbnailT.png with thumbnail.png
|
|
1049
|
+
thumbnail_url = thumbnail_url.replace('http://', 'https://').replace('thumbnailT.png', 'thumbnail.png')
|
|
1050
|
+
|
|
1051
|
+
# Format thumbnail with proper markdown link (matching Neo4j format)
|
|
1052
|
+
thumbnail = ''
|
|
1053
|
+
if thumbnail_url and template_anatomy:
|
|
1054
|
+
# Prefer symbol over label for template (matching Neo4j behavior)
|
|
1055
|
+
template_label = template_anatomy.get('label', '')
|
|
1056
|
+
if template_anatomy.get('symbol') and len(template_anatomy.get('symbol', '')) > 0:
|
|
1057
|
+
template_label = template_anatomy.get('symbol')
|
|
1058
|
+
# Decode URL-encoded strings from SOLR (e.g., ME%28R%29 -> ME(R))
|
|
1059
|
+
template_label = unquote(template_label)
|
|
1060
|
+
template_short_form = template_anatomy.get('short_form', '')
|
|
1061
|
+
|
|
1062
|
+
# Prefer symbol over label for anatomy (matching Neo4j behavior)
|
|
1063
|
+
anatomy_label = anatomy.get('label', '')
|
|
1064
|
+
if anatomy.get('symbol') and len(anatomy.get('symbol', '')) > 0:
|
|
1065
|
+
anatomy_label = anatomy.get('symbol')
|
|
1066
|
+
# Decode URL-encoded strings from SOLR (e.g., ME%28R%29 -> ME(R))
|
|
1067
|
+
anatomy_label = unquote(anatomy_label)
|
|
1068
|
+
anatomy_short_form = anatomy.get('short_form', '')
|
|
1069
|
+
|
|
1070
|
+
if template_label and anatomy_label:
|
|
1071
|
+
# Create thumbnail markdown link matching the original format
|
|
1072
|
+
# DO NOT encode brackets in alt text - that's done later by encode_markdown_links
|
|
1073
|
+
alt_text = f"{anatomy_label} aligned to {template_label}"
|
|
1074
|
+
link_target = f"{template_short_form},{anatomy_short_form}"
|
|
1075
|
+
thumbnail = f"[]({link_target})"
|
|
1076
|
+
|
|
1077
|
+
# Format template information
|
|
1078
|
+
template_formatted = ''
|
|
1079
|
+
if template_anatomy:
|
|
1080
|
+
# Prefer symbol over label (matching Neo4j behavior)
|
|
1081
|
+
template_label = template_anatomy.get('label', '')
|
|
1082
|
+
if template_anatomy.get('symbol') and len(template_anatomy.get('symbol', '')) > 0:
|
|
1083
|
+
template_label = template_anatomy.get('symbol')
|
|
1084
|
+
# Decode URL-encoded strings from SOLR (e.g., ME%28R%29 -> ME(R))
|
|
1085
|
+
template_label = unquote(template_label)
|
|
1086
|
+
template_short_form = template_anatomy.get('short_form', '')
|
|
1087
|
+
if template_label and template_short_form:
|
|
1088
|
+
template_formatted = f"[{template_label}]({template_short_form})"
|
|
1089
|
+
|
|
1090
|
+
# Handle label formatting (match Neo4j format - prefer symbol over label)
|
|
1091
|
+
anatomy_label = anatomy.get('label', 'Unknown')
|
|
1092
|
+
if anatomy.get('symbol') and len(anatomy.get('symbol', '')) > 0:
|
|
1093
|
+
anatomy_label = anatomy.get('symbol')
|
|
1094
|
+
# Decode URL-encoded strings from SOLR (e.g., ME%28R%29 -> ME(R))
|
|
1095
|
+
anatomy_label = unquote(anatomy_label)
|
|
1096
|
+
anatomy_short_form = anatomy.get('short_form', '')
|
|
1097
|
+
|
|
1098
|
+
row = {
|
|
1099
|
+
'id': anatomy_short_form,
|
|
1100
|
+
'label': f"[{anatomy_label}]({anatomy_short_form})",
|
|
1101
|
+
'tags': tags,
|
|
1102
|
+
'parent': f"[{term_info.get('term', {}).get('core', {}).get('label', 'Unknown')}]({short_form})",
|
|
1103
|
+
'source': '', # Not readily available in SOLR anatomy_channel_image
|
|
1104
|
+
'source_id': '',
|
|
1105
|
+
'template': template_formatted,
|
|
1106
|
+
'dataset': '', # Not readily available in SOLR anatomy_channel_image
|
|
1107
|
+
'license': '',
|
|
1108
|
+
'thumbnail': thumbnail
|
|
1109
|
+
}
|
|
1110
|
+
rows.append(row)
|
|
1111
|
+
|
|
1112
|
+
# Sort by ID to match expected ordering (Neo4j uses "ORDER BY id Desc")
|
|
1113
|
+
rows.sort(key=lambda x: x['id'], reverse=True)
|
|
1114
|
+
|
|
1115
|
+
total_count = len(anatomy_images)
|
|
1116
|
+
|
|
1117
|
+
if return_dataframe:
|
|
1118
|
+
df = pd.DataFrame(rows)
|
|
1119
|
+
# Apply encoding to markdown links (matches Neo4j implementation)
|
|
1120
|
+
columns_to_encode = ['label', 'parent', 'source', 'source_id', 'template', 'dataset', 'license', 'thumbnail']
|
|
1121
|
+
df = encode_markdown_links(df, columns_to_encode)
|
|
1122
|
+
return df
|
|
1123
|
+
|
|
1124
|
+
return {
|
|
1125
|
+
"headers": _get_instances_headers(),
|
|
1126
|
+
"rows": rows,
|
|
1127
|
+
"count": total_count
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
except Exception as e:
|
|
1131
|
+
print(f"Error in SOLR fallback for get_instances: {e}")
|
|
1132
|
+
# Return empty results with proper structure
|
|
1133
|
+
if return_dataframe:
|
|
1134
|
+
return pd.DataFrame()
|
|
1135
|
+
return {
|
|
1136
|
+
"headers": _get_instances_headers(),
|
|
1137
|
+
"rows": [],
|
|
1138
|
+
"count": 0
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
def _get_instances_headers():
|
|
1142
|
+
"""Return standard headers for get_instances results"""
|
|
1143
|
+
return {
|
|
1144
|
+
"id": {"title": "Add", "type": "selection_id", "order": -1},
|
|
1145
|
+
"label": {"title": "Name", "type": "markdown", "order": 0, "sort": {0: "Asc"}},
|
|
1146
|
+
"parent": {"title": "Parent Type", "type": "markdown", "order": 1},
|
|
1147
|
+
"template": {"title": "Template", "type": "markdown", "order": 4},
|
|
1148
|
+
"tags": {"title": "Gross Types", "type": "tags", "order": 3},
|
|
1149
|
+
"source": {"title": "Data Source", "type": "markdown", "order": 5},
|
|
1150
|
+
"source_id": {"title": "Data Source", "type": "markdown", "order": 6},
|
|
1151
|
+
"dataset": {"title": "Dataset", "type": "markdown", "order": 7},
|
|
1152
|
+
"license": {"title": "License", "type": "markdown", "order": 8},
|
|
1153
|
+
"thumbnail": {"title": "Thumbnail", "type": "markdown", "order": 9}
|
|
1154
|
+
}
|
|
926
1155
|
|
|
927
1156
|
# Convert the results to a DataFrame
|
|
928
1157
|
df = pd.DataFrame.from_records(get_dict_cursor()(results))
|
|
@@ -1326,15 +1555,22 @@ def fill_query_results(term_info):
|
|
|
1326
1555
|
if function:
|
|
1327
1556
|
# print(f"Function {query['function']} found")
|
|
1328
1557
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1558
|
+
try:
|
|
1559
|
+
# Unpack the default dictionary and pass its contents as arguments
|
|
1560
|
+
function_args = query['takes'].get("default", {})
|
|
1561
|
+
# print(f"Function args: {function_args}")
|
|
1562
|
+
|
|
1563
|
+
# Modify this line to use the correct arguments and pass the default arguments
|
|
1564
|
+
if summary_mode:
|
|
1565
|
+
result = function(return_dataframe=False, limit=query['preview'], summary_mode=summary_mode, **function_args)
|
|
1566
|
+
else:
|
|
1567
|
+
result = function(return_dataframe=False, limit=query['preview'], **function_args)
|
|
1568
|
+
except Exception as e:
|
|
1569
|
+
print(f"Error executing query function {query['function']}: {e}")
|
|
1570
|
+
# Set default values for failed query
|
|
1571
|
+
query['preview_results'] = {'headers': query.get('preview_columns', ['id', 'label', 'tags', 'thumbnail']), 'rows': []}
|
|
1572
|
+
query['count'] = 0
|
|
1573
|
+
continue
|
|
1338
1574
|
# print(f"Function result: {result}")
|
|
1339
1575
|
|
|
1340
1576
|
# Filter columns based on preview_columns
|
|
@@ -1367,7 +1603,13 @@ def fill_query_results(term_info):
|
|
|
1367
1603
|
print(f"Unsupported result format for filtering columns in {query['function']}")
|
|
1368
1604
|
|
|
1369
1605
|
query['preview_results'] = {'headers': filtered_headers, 'rows': filtered_result}
|
|
1370
|
-
|
|
1606
|
+
# Handle count extraction based on result type
|
|
1607
|
+
if isinstance(result, dict) and 'count' in result:
|
|
1608
|
+
query['count'] = result['count']
|
|
1609
|
+
elif isinstance(result, pd.DataFrame):
|
|
1610
|
+
query['count'] = len(result)
|
|
1611
|
+
else:
|
|
1612
|
+
query['count'] = 0
|
|
1371
1613
|
# print(f"Filtered result: {filtered_result}")
|
|
1372
1614
|
else:
|
|
1373
1615
|
print(f"Function {query['function']} not found")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vfbquery
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Wrapper for querying VirtualFlyBrain knowledge graph.
|
|
5
5
|
Home-page: https://github.com/VirtualFlyBrain/VFBquery
|
|
6
6
|
Author: VirtualFlyBrain
|
|
@@ -121,9 +121,9 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"id": "VFB_00101385",
|
|
124
|
-
"label": "[ME
|
|
124
|
+
"label": "[ME(R) on JRC_FlyEM_Hemibrain](VFB_00101385)",
|
|
125
125
|
"tags": "Nervous_system|Adult|Visual_system|Synaptic_neuropil_domain",
|
|
126
|
-
"thumbnail": "[ on JRC_FlyEM_Hemibrain aligned to JRCFIB2018Fum')](VFB_00101384,VFB_00101385)"
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
"id": "VFB_00030810",
|
|
@@ -143,8 +143,8 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
143
143
|
"count": 4
|
|
144
144
|
}
|
|
145
145
|
],
|
|
146
|
-
"IsIndividual":
|
|
147
|
-
"IsClass":
|
|
146
|
+
"IsIndividual": False,
|
|
147
|
+
"IsClass": True,
|
|
148
148
|
"Examples": {
|
|
149
149
|
"VFB_00101384": [
|
|
150
150
|
{
|
|
@@ -191,7 +191,7 @@ vfb.get_term_info('FBbt_00003748')
|
|
|
191
191
|
}
|
|
192
192
|
]
|
|
193
193
|
},
|
|
194
|
-
"IsTemplate":
|
|
194
|
+
"IsTemplate": False,
|
|
195
195
|
"Synonyms": [
|
|
196
196
|
{
|
|
197
197
|
"label": "ME",
|
|
@@ -1122,7 +1122,7 @@ vfb.get_instances('FBbt_00003748', return_dataframe=False)
|
|
|
1122
1122
|
},
|
|
1123
1123
|
{
|
|
1124
1124
|
"id": "VFB_00101385",
|
|
1125
|
-
"label": "[ME
|
|
1125
|
+
"label": "[ME(R) on JRC_FlyEM_Hemibrain](VFB_00101385)",
|
|
1126
1126
|
"tags": "Nervous_system|Adult|Visual_system|Synaptic_neuropil_domain",
|
|
1127
1127
|
"parent": "[medulla](FBbt_00003748)",
|
|
1128
1128
|
"source": "",
|
|
@@ -1130,7 +1130,7 @@ vfb.get_instances('FBbt_00003748', return_dataframe=False)
|
|
|
1130
1130
|
"template": "[JRCFIB2018Fum](VFB_00101384)",
|
|
1131
1131
|
"dataset": "[JRC_FlyEM_Hemibrain painted domains](Xu2020roi)",
|
|
1132
1132
|
"license": "",
|
|
1133
|
-
"thumbnail": "[ on JRC_FlyEM_Hemibrain aligned to JRCFIB2018Fum')](VFB_00101384,VFB_00101385)"
|
|
1134
1134
|
},
|
|
1135
1135
|
{
|
|
1136
1136
|
"id": "VFB_00030810",
|
|
@@ -1152,7 +1152,7 @@ vfb.get_instances('FBbt_00003748', return_dataframe=False)
|
|
|
1152
1152
|
"source": "",
|
|
1153
1153
|
"source_id": "",
|
|
1154
1154
|
"template": "[JFRC2](VFB_00017894)",
|
|
1155
|
-
"dataset": "[BrainName neuropils on adult brain JFRC2
|
|
1155
|
+
"dataset": "[BrainName neuropils on adult brain JFRC2 (Jenett, Shinomya)](JenettShinomya_BrainName)",
|
|
1156
1156
|
"license": "",
|
|
1157
1157
|
"thumbnail": "[](VFB_00017894,VFB_00030624)"
|
|
1158
1158
|
}
|
|
@@ -1234,7 +1234,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1234
1234
|
"name": "[JFRC2](VFB_00017894)",
|
|
1235
1235
|
"tags": "Nervous_system|Adult",
|
|
1236
1236
|
"thumbnail": "[](VFB_00017894)",
|
|
1237
|
-
"dataset": "[FlyLight - GMR GAL4 collection
|
|
1237
|
+
"dataset": "[FlyLight - GMR GAL4 collection (Jenett2012)](Jenett2012)",
|
|
1238
1238
|
"license": "[CC-BY-NC-SA](VFBlicense_CC_BY_NC_SA_4_0)"
|
|
1239
1239
|
},
|
|
1240
1240
|
{
|
|
@@ -1252,7 +1252,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1252
1252
|
"name": "[L1 larval CNS ssTEM - Cardona/Janelia](VFB_00050000)",
|
|
1253
1253
|
"tags": "Nervous_system|Larva",
|
|
1254
1254
|
"thumbnail": "[](VFB_00050000)",
|
|
1255
|
-
"dataset": "[Neurons involved in larval fast escape response - EM
|
|
1255
|
+
"dataset": "[Neurons involved in larval fast escape response - EM (Ohyama2016)](Ohyama2015)",
|
|
1256
1256
|
"license": "[CC_BY_SA](VFBlicense_CC_BY_SA_4_0)"
|
|
1257
1257
|
},
|
|
1258
1258
|
{
|
|
@@ -1261,7 +1261,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1261
1261
|
"name": "[L1 larval CNS ssTEM - Cardona/Janelia](VFB_00050000)",
|
|
1262
1262
|
"tags": "Nervous_system|Larva",
|
|
1263
1263
|
"thumbnail": "[](VFB_00050000)",
|
|
1264
|
-
"dataset": "[larval hugin neurons - EM
|
|
1264
|
+
"dataset": "[larval hugin neurons - EM (Schlegel2016)](Schlegel2016)",
|
|
1265
1265
|
"license": "[CC_BY](VFBlicense_CC_BY_4_0)"
|
|
1266
1266
|
},
|
|
1267
1267
|
{
|
|
@@ -1270,7 +1270,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1270
1270
|
"name": "[L3 CNS template - Wood2018](VFB_00049000)",
|
|
1271
1271
|
"tags": "Nervous_system|Larva",
|
|
1272
1272
|
"thumbnail": "[](VFB_00049000)",
|
|
1273
|
-
"dataset": "[L3 Larval CNS Template
|
|
1273
|
+
"dataset": "[L3 Larval CNS Template (Truman2016)](Truman2016)",
|
|
1274
1274
|
"license": "[CC_BY_SA](VFBlicense_CC_BY_SA_4_0)"
|
|
1275
1275
|
},
|
|
1276
1276
|
{
|
|
@@ -1279,7 +1279,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1279
1279
|
"name": "[COURT2018VNS](VFB_00100000)",
|
|
1280
1280
|
"tags": "Nervous_system|Adult|Ganglion",
|
|
1281
1281
|
"thumbnail": "[](VFB_00100000)",
|
|
1282
|
-
"dataset": "[Adult VNS neuropils
|
|
1282
|
+
"dataset": "[Adult VNS neuropils (Court2017)](Court2017)",
|
|
1283
1283
|
"license": "[CC_BY_SA](VFBlicense_CC_BY_SA_4_0)"
|
|
1284
1284
|
},
|
|
1285
1285
|
{
|
|
@@ -1294,7 +1294,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1294
1294
|
{
|
|
1295
1295
|
"id": "VFB_00110000",
|
|
1296
1296
|
"order": 9,
|
|
1297
|
-
"name": "[Adult Head
|
|
1297
|
+
"name": "[Adult Head (McKellar2020)](VFB_00110000)",
|
|
1298
1298
|
"tags": "Adult|Anatomy",
|
|
1299
1299
|
"thumbnail": "[')](VFB_00110000)",
|
|
1300
1300
|
"dataset": "[GAL4 lines from McKellar et al., 2020](McKellar2020)",
|
|
@@ -1303,7 +1303,7 @@ vfb.get_templates(return_dataframe=False)
|
|
|
1303
1303
|
{
|
|
1304
1304
|
"id": "VFB_00120000",
|
|
1305
1305
|
"order": 10,
|
|
1306
|
-
"name": "[Adult T1 Leg
|
|
1306
|
+
"name": "[Adult T1 Leg (Kuan2020)](VFB_00120000)",
|
|
1307
1307
|
"tags": "Adult|Anatomy",
|
|
1308
1308
|
"thumbnail": "[')](VFB_00120000)",
|
|
1309
1309
|
"dataset": "[Millimeter-scale imaging of a Drosophila leg at single-neuron resolution](Kuan2020)",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
test/readme_parser.py,sha256=I15lzWyYaYRgbFqZ1EZOBrUyzeJJK0VWzpxvH25lAZE,5772
|
|
3
|
+
test/term_info_queries_test.py,sha256=bb3oTnPqpGzegWDkWyIBySVbYEeyMdKB3epgzm0yf40,36963
|
|
4
|
+
test/test_default_caching.py,sha256=-KW2Mkz9x0tjlhXMreqJvjgo3pWOg49G0r22Woa9U5U,6616
|
|
5
|
+
test/test_examples_diff.py,sha256=TPo7gHPcus-24T7kxBXMQiCl0EHcXaEXeVuOG7C3rUo,15853
|
|
6
|
+
vfbquery/__init__.py,sha256=IQ2W2LkrVKThB-00cuaIsW5fwQRN8PgPlKSXGdGY2Q8,3010
|
|
7
|
+
vfbquery/cache_enhancements.py,sha256=-PCM0YZHPjwUJwJODZLgmz91sDyFGuYz_QRph_kTbB8,17341
|
|
8
|
+
vfbquery/cached_functions.py,sha256=5-aIiRP9mfEhVT3mXkLvIPDmdFq6iIExiLZAyir12IQ,10255
|
|
9
|
+
vfbquery/solr_cache_integration.py,sha256=Q87z_pXPdS9zn0r9kp2YBLGpCKOXVvcmzNmkRN7D8MU,7984
|
|
10
|
+
vfbquery/solr_fetcher.py,sha256=1FAyqaLrvZLaAmCW96en9y8lKTcs-ZFjt_UlnohP0jo,5683
|
|
11
|
+
vfbquery/solr_result_cache.py,sha256=qkR13mCiqRHLCv5PWd1pT-rPIboZcGuusn065HgvV-0,30111
|
|
12
|
+
vfbquery/term_info_queries.py,sha256=oE-Ogm7jCPPlKtD3W3EtttYZcHnInwDOpOj-phAEOaI,42009
|
|
13
|
+
vfbquery/test_utils.py,sha256=7wUA3xgaGu3eLnjC98msNYt1wL538nOimVJjkC0ZLjU,5791
|
|
14
|
+
vfbquery/vfb_queries.py,sha256=tLLphDbsjv0MsEnA5JWvKzT1a8OutsCUH6gmrVahZKg,78143
|
|
15
|
+
vfbquery-0.4.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
+
vfbquery-0.4.1.dist-info/METADATA,sha256=d5onNHbLwdQXpfNLNgvvtVNbOV2L920AO8aFf-A3rbY,63049
|
|
17
|
+
vfbquery-0.4.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
18
|
+
vfbquery-0.4.1.dist-info/top_level.txt,sha256=UgaRTTOy4JBdKbkr_gkeknT4eaibm3ztF520G4NTQZs,14
|
|
19
|
+
vfbquery-0.4.1.dist-info/RECORD,,
|
vfbquery-0.3.4.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=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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|