vfbquery 0.5.0__py3-none-any.whl → 0.5.2__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
@@ -340,10 +340,25 @@ def encode_markdown_links(df, columns):
340
340
  return label
341
341
 
342
342
  try:
343
- # Skip linked images (format: [![alt text](image_url "title")](link))
344
- # These should NOT be encoded
343
+ # Handle linked images (format: [![alt text](image_url "title")](link))
345
344
  if label.startswith("[!["):
346
- return label
345
+ # Replace http with https in the image URL
346
+ # Pattern: [![anything](http://... "title")](link)
347
+ def secure_image_url(match):
348
+ alt_text = match.group(1)
349
+ image_url = match.group(2)
350
+ title = match.group(3) if match.group(3) else ""
351
+ link = match.group(4)
352
+ secure_url = image_url.replace("http://", "https://")
353
+ if title:
354
+ return f"[![{alt_text}]({secure_url} \"{title}\")]({link})"
355
+ else:
356
+ return f"[![{alt_text}]({secure_url})]({link})"
357
+
358
+ # Regex to match the entire linked image
359
+ pattern = r'\[\!\[([^\]]+)\]\(([^\'"\s]+)(?:\s+[\'"]([^\'"]*)[\'"])?\)\]\(([^)]+)\)'
360
+ encoded_label = re.sub(pattern, secure_image_url, label)
361
+ return encoded_label
347
362
 
348
363
  # Process regular markdown links - handle multiple links separated by commas
349
364
  # Pattern matches [label](url) format
@@ -356,7 +371,9 @@ def encode_markdown_links(df, columns):
356
371
  url_part = match.group(2) # The URL part (between ( and ))
357
372
  # Encode brackets in the label part only
358
373
  label_part_encoded = encode_brackets(label_part)
359
- return f"[{label_part_encoded}]({url_part})"
374
+ # Ensure URLs use https
375
+ url_part_secure = url_part.replace("http://", "https://")
376
+ return f"[{label_part_encoded}]({url_part_secure})"
360
377
 
361
378
  # Replace all markdown links with their encoded versions
362
379
  encoded_label = re.sub(r'\[([^\]]+)\]\(([^\)]+)\)', encode_single_link, label)
@@ -1268,7 +1285,7 @@ def NeuronRegionConnectivityQuery_to_schema(name, take_default):
1268
1285
  "default": take_default,
1269
1286
  }
1270
1287
  preview = 5
1271
- preview_columns = ["id", "label", "presynaptic_terminals", "postsynaptic_terminals", "tags"]
1288
+ preview_columns = ["id", "region", "presynaptic_terminals", "postsynaptic_terminals", "tags"]
1272
1289
  return Query(query=query, label=label, function=function, takes=takes, preview=preview, preview_columns=preview_columns)
1273
1290
 
1274
1291
 
@@ -2713,7 +2730,7 @@ def get_neuron_region_connectivity(short_form: str, return_dataframe=True, limit
2713
2730
  primary
2714
2731
  RETURN
2715
2732
  target.short_form AS id,
2716
- target.label AS label,
2733
+ target.label AS region,
2717
2734
  synapse_counts.`pre` AS presynaptic_terminals,
2718
2735
  synapse_counts.`post` AS postsynaptic_terminals,
2719
2736
  target.uniqueFacets AS tags
@@ -2732,7 +2749,7 @@ def get_neuron_region_connectivity(short_form: str, return_dataframe=True, limit
2732
2749
 
2733
2750
  headers = {
2734
2751
  'id': {'title': 'Region ID', 'type': 'selection_id', 'order': -1},
2735
- 'label': {'title': 'Brain Region', 'type': 'markdown', 'order': 0},
2752
+ 'region': {'title': 'Brain Region', 'type': 'markdown', 'order': 0},
2736
2753
  'presynaptic_terminals': {'title': 'Presynaptic Terminals', 'type': 'number', 'order': 1},
2737
2754
  'postsynaptic_terminals': {'title': 'Postsynaptic Terminals', 'type': 'number', 'order': 2},
2738
2755
  'tags': {'title': 'Region Types', 'type': 'list', 'order': 3},
@@ -3070,19 +3087,37 @@ def _owlery_query_to_results(owl_query_string: str, short_form: str, return_data
3070
3087
  owlery_url = f"{owlery_base}{endpoint}?{urlencode(params)}"
3071
3088
 
3072
3089
  import sys
3073
- print(f"ERROR: Owlery {'instances' if query_instances else 'subclasses'} query failed: {e}", file=sys.stderr)
3074
- print(f" Full URL: {owlery_url}", file=sys.stderr)
3075
- print(f" Query string: {owl_query_string}", file=sys.stderr)
3076
- import traceback
3077
- traceback.print_exc()
3078
- # Return error indication with count=-1
3079
- if return_dataframe:
3080
- return pd.DataFrame()
3081
- return {
3082
- "headers": _get_standard_query_headers(),
3083
- "rows": [],
3084
- "count": -1
3085
- }
3090
+ import requests
3091
+
3092
+ # Check if this is a 400 Bad Request (invalid query) vs other errors
3093
+ is_bad_request = isinstance(e, requests.exceptions.HTTPError) and hasattr(e, 'response') and e.response.status_code == 400
3094
+
3095
+ if is_bad_request:
3096
+ # 400 Bad Request means the term isn't valid for this type of query (e.g., anatomical query on expression pattern)
3097
+ # Return 0 results instead of error
3098
+ print(f"INFO: Owlery query returned 400 Bad Request (invalid for this term type): {owl_query_string}", file=sys.stderr)
3099
+ if return_dataframe:
3100
+ return pd.DataFrame()
3101
+ return {
3102
+ "headers": _get_standard_query_headers(),
3103
+ "rows": [],
3104
+ "count": 0
3105
+ }
3106
+ else:
3107
+ # Other errors (500, network issues, etc.) - return error indication
3108
+ print(f"ERROR: Owlery {'instances' if query_instances else 'subclasses'} query failed: {e}", file=sys.stderr)
3109
+ print(f" Full URL: {owlery_url}", file=sys.stderr)
3110
+ print(f" Query string: {owl_query_string}", file=sys.stderr)
3111
+ import traceback
3112
+ traceback.print_exc()
3113
+ # Return error indication with count=-1
3114
+ if return_dataframe:
3115
+ return pd.DataFrame()
3116
+ return {
3117
+ "headers": _get_standard_query_headers(),
3118
+ "rows": [],
3119
+ "count": -1
3120
+ }
3086
3121
 
3087
3122
 
3088
3123
  def get_anatomy_scrnaseq(anatomy_short_form: str, return_dataframe=True, limit: int = -1):
@@ -3915,6 +3950,20 @@ def fill_query_results(term_info):
3915
3950
  result_count = 0
3916
3951
 
3917
3952
  # Store preview results (count is stored at query level, not in preview_results)
3953
+ # Sort rows based on the sort field in headers, default to ID descending if none
3954
+ sort_column = None
3955
+ sort_direction = None
3956
+ for col, info in filtered_headers.items():
3957
+ if 'sort' in info and isinstance(info['sort'], dict):
3958
+ sort_column = col
3959
+ sort_direction = list(info['sort'].values())[0] # e.g., 'Asc' or 'Desc'
3960
+ break
3961
+ if sort_column:
3962
+ reverse = sort_direction == 'Desc'
3963
+ filtered_result.sort(key=lambda x: x.get(sort_column, ''), reverse=reverse)
3964
+ else:
3965
+ # Default to ID descending if no sort specified
3966
+ filtered_result.sort(key=lambda x: x.get('id', ''), reverse=True)
3918
3967
  query['preview_results'] = {'headers': filtered_headers, 'rows': filtered_result}
3919
3968
  query['count'] = result_count
3920
3969
  # print(f"Filtered result: {filtered_result}")