1515Endpoints (mirrors v3-cached.virtualflybrain.org):
1616 GET /get_term_info?id=<short_form>
1717 GET /run_query?id=<short_form>&query_type=<QueryType>
18+ GET /resolve_entity?query=<name_or_symbol> # IDs rewritten to names
19+ GET /find_stocks?id=<resolved_flybase_feature_id>
20+ GET /resolve_combination?query=<name_or_synonym> # IDs rewritten to names
21+ GET /find_combo_publications?id=<resolved_fbco_id>
22+ GET /list_connectome_datasets
23+ GET /query_connectivity?upstream_type=<name>&downstream_type=<name>
1824 GET /health
1925 GET /status — queue depth, cache stats & worker utilisation
2026
2834import json
2935import logging
3036import os
37+ import re
3138import sys
3239import asyncio
3340import time
5360# or --workers CLI flag.
5461DEFAULT_WORKERS = 10
5562DEFAULT_MAX_QUEUE_DEPTH = 200
63+ _FLYBASE_FEATURE_ID_RE = re .compile (r"^FB(?:gn|al|ti|st|co)\d+$" , re .IGNORECASE )
64+ _FBCO_ID_RE = re .compile (r"^FBco\d+$" , re .IGNORECASE )
5665
5766
5867# ---------------------------------------------------------------------------
@@ -317,7 +326,10 @@ def _run_query(short_form, func_name):
317326
318327def _run_resolve_entity (name_or_id ):
319328 """Execute resolve_entity in a worker process."""
320- return _vfb .resolve_entity (name_or_id )
329+ query = _rewrite_resolve_entity_query (name_or_id )
330+ if query is None :
331+ return {"match_type" : "NOT_FOUND" , "results" : []}
332+ return _vfb .resolve_entity (query )
321333
322334
323335def _run_find_stocks (feature_id , collection_filter ):
@@ -327,7 +339,10 @@ def _run_find_stocks(feature_id, collection_filter):
327339
328340def _run_resolve_combination (name_or_id ):
329341 """Execute resolve_combination in a worker process."""
330- return _vfb .resolve_combination (name_or_id )
342+ query = _rewrite_resolve_combination_query (name_or_id )
343+ if query is None :
344+ return {"match_type" : "NOT_FOUND" , "results" : []}
345+ return _vfb .resolve_combination (query )
331346
332347
333348def _run_find_combo_publications (fbco_id ):
@@ -368,6 +383,72 @@ def _convert_numpy_types(obj):
368383 return obj
369384
370385
386+ def _parse_resolver_query (query ):
387+ """Normalise a resolver query parameter."""
388+ normalized = (query or "" ).strip ()
389+ if not normalized :
390+ raise ValueError ("Missing required parameter: query" )
391+ return normalized
392+
393+
394+ def _canonicalize_flybase_feature_id (feature_id ):
395+ """Return canonical FlyBase casing for a supported feature ID."""
396+ match = re .match (r"^FB(gn|al|ti|st|co)(\d+)$" , feature_id or "" , re .IGNORECASE )
397+ if not match :
398+ return feature_id
399+ return f"FB{ match .group (1 ).lower ()} { match .group (2 )} "
400+
401+
402+ def _canonicalize_fbco_id (fbco_id ):
403+ """Return canonical FlyBase casing for an FBco ID."""
404+ match = re .match (r"^FBco(\d+)$" , fbco_id or "" , re .IGNORECASE )
405+ if not match :
406+ return fbco_id
407+ return f"FBco{ match .group (1 )} "
408+
409+
410+ def _preferred_term_info_query (term_info ):
411+ """Pick a stable name/label from VFB term_info."""
412+ if not isinstance (term_info , dict ):
413+ return None
414+
415+ candidate = (term_info .get ("Name" ) or "" ).strip ()
416+ if candidate :
417+ return candidate
418+
419+ meta = term_info .get ("Meta" )
420+ if isinstance (meta , dict ):
421+ for key in ("Symbol" , "Name" ):
422+ candidate = (meta .get (key ) or "" ).strip ()
423+ if candidate :
424+ match = re .match (r"^\[(.+)\]\([^)]+\)$" , candidate )
425+ return match .group (1 ) if match else candidate
426+
427+ return None
428+
429+
430+ def _rewrite_resolve_entity_query (name_or_id ):
431+ """Rewrite a FlyBase feature ID to a preferred VFB term name."""
432+ query = _parse_resolver_query (name_or_id )
433+ if not _FLYBASE_FEATURE_ID_RE .match (query ):
434+ return query
435+
436+ canonical_id = _canonicalize_flybase_feature_id (query )
437+ term_info = _vfb .get_term_info (canonical_id , preview = False )
438+ return _preferred_term_info_query (term_info )
439+
440+
441+ def _rewrite_resolve_combination_query (name_or_id ):
442+ """Rewrite an FBco ID to a preferred VFB term name."""
443+ query = _parse_resolver_query (name_or_id )
444+ if not _FBCO_ID_RE .match (query ):
445+ return query
446+
447+ canonical_id = _canonicalize_fbco_id (query )
448+ term_info = _vfb .get_term_info (canonical_id , preview = False )
449+ return _preferred_term_info_query (term_info )
450+
451+
371452# ---------------------------------------------------------------------------
372453# HTTP handlers
373454# ---------------------------------------------------------------------------
@@ -667,10 +748,12 @@ async def _dispatch_to_pool(request, cache_key, worker_fn, *args):
667748
668749
669750async def handle_resolve_entity (request ):
670- """GET /resolve_entity?query=<name_or_id>"""
671- query = request .query .get ("query" )
672- if not query :
673- return web .json_response ({"error" : "Missing required parameter: query" }, status = 400 )
751+ """GET /resolve_entity?query=<name_or_symbol>"""
752+ try :
753+ query = _parse_resolver_query (request .query .get ("query" ))
754+ except ValueError as exc :
755+ return web .json_response ({"error" : str (exc )}, status = 400 )
756+
674757 return await _dispatch_to_pool (
675758 request , f"resolve_entity:{ query } " , _run_resolve_entity , query
676759 )
@@ -689,10 +772,12 @@ async def handle_find_stocks(request):
689772
690773
691774async def handle_resolve_combination (request ):
692- """GET /resolve_combination?query=<name_or_id>"""
693- query = request .query .get ("query" )
694- if not query :
695- return web .json_response ({"error" : "Missing required parameter: query" }, status = 400 )
775+ """GET /resolve_combination?query=<name_or_synonym>"""
776+ try :
777+ query = _parse_resolver_query (request .query .get ("query" ))
778+ except ValueError as exc :
779+ return web .json_response ({"error" : str (exc )}, status = 400 )
780+
696781 return await _dispatch_to_pool (
697782 request , f"resolve_combination:{ query } " , _run_resolve_combination , query
698783 )
0 commit comments