From d26aeb3065ff32f1172a300f217b5c1f31bf8c1c Mon Sep 17 00:00:00 2001 From: Jonathan Legrand Date: Thu, 29 Aug 2024 00:03:43 +0200 Subject: [PATCH] Update REST API to be more parsimonious and thus faster! --- src/plantdb/cli/fsdb_rest_api.py | 6 +-- src/plantdb/rest_api.py | 69 +++++++------------------------- src/plantdb/rest_api_client.py | 53 ++++++++++++------------ 3 files changed, 45 insertions(+), 83 deletions(-) diff --git a/src/plantdb/cli/fsdb_rest_api.py b/src/plantdb/cli/fsdb_rest_api.py index 720aa83..18d1adc 100644 --- a/src/plantdb/cli/fsdb_rest_api.py +++ b/src/plantdb/cli/fsdb_rest_api.py @@ -23,7 +23,7 @@ from plantdb.rest_api import PointCloudGroundTruth from plantdb.rest_api import Refresh from plantdb.rest_api import Scan -from plantdb.rest_api import ScanList +from plantdb.rest_api import ScansList from plantdb.rest_api import Sequence from plantdb.test_database import DATASET from plantdb.test_database import test_database @@ -78,8 +78,8 @@ def main(): logger.info(f"Found {len(db.list_scans())} scans dataset to serve in local plant database.") # Initialize RESTful resources to serve: - api.add_resource(ScanList, '/scans', - resource_class_args=tuple([db, logger])) + api.add_resource(ScansList, '/scans', + resource_class_args=tuple([db])) api.add_resource(Scan, '/scans/', resource_class_args=tuple([db, logger])) api.add_resource(File, '/files/', diff --git a/src/plantdb/rest_api.py b/src/plantdb/rest_api.py index c9c30f0..0f1e166 100644 --- a/src/plantdb/rest_api.py +++ b/src/plantdb/rest_api.py @@ -40,6 +40,8 @@ from flask import send_file from flask import send_from_directory from flask_restful import Resource +from jinja2.sandbox import unsafe + from plantdb import webcache from plantdb.io import read_json from plantdb.log import configure_logger @@ -174,53 +176,6 @@ def get_scan_template(scan_id: str, error=False) -> dict: } -def list_scans_info(scans, query=None, **kwargs): - """List scans information. - - Parameters - ---------- - scans : list of plantdb.fsdb.Scan - The list of scan instances to get information from. - query : str, optional - A scan filtering query, to be matched in the scan metadata. - - Other Parameters - ---------------- - logger : logging.Logger - A logger to use with this method, default to a logger created on the fly with a `module.function` name. - - Returns - ------- - list of dict - The list of scans information dictionaries. - - Examples - -------- - >>> from plantdb.rest_api import list_scans_info - >>> from plantdb.test_database import test_database - >>> db = test_database('real_plant_analyzed') - >>> db.connect() - >>> scans_info = list_scans_info(db.get_scans()) - >>> print(scans_info) - [{'id': 'real_plant_analyzed', 'metadata': {'date': '2023-12-15 16:37:15', 'species': 'N/A', 'plant': 'N/A', 'environment': 'Lyon indoor', 'nbPhotos': 60, 'files': {'metadatas': None, 'archive': None}}, 'thumbnailUri': '', 'hasTriangleMesh': True, 'hasPointCloud': True, 'hasPcdGroundTruth': False, 'hasCurveSkeleton': True, 'hasAnglesAndInternodes': True, 'hasSegmentation2D': False, 'hasSegmentedPcdEvaluation': False, 'hasPointCloudEvaluation': False, 'hasManualMeasures': False, 'hasAutomatedMeasures': True, 'hasSegmentedPointCloud': False, 'error': False, 'hasTreeGraph': True}] - >>> db.disconnect() - """ - logger = kwargs.get("logger", configure_logger(__name__)) - - res = [] - for scan in scans: - metadata = scan.get_metadata() - if query is not None and not (query.lower() in json.dumps(metadata).lower()): - continue # filter scans info list by matching the query with metadata keys - try: - scan_info = get_scan_info(scan) - except: - logger.error(f"Could not obtain information from scan dataset '{scan.id}'...") - scan_info = get_scan_template(scan.id, error=True) - res.append(scan_info) - return res - - def get_scan_info(scan, **kwargs): """Get the information related to a single scan dataset. @@ -501,12 +456,11 @@ def get_scan_data(scan, **kwargs): return scan_data -class ScanList(Resource): +class ScansList(Resource): """Concrete RESTful resource to serve the list of scan datasets and some info upon request (GET method).""" - def __init__(self, db, logger): + def __init__(self, db): self.db = db - self.logger = logger def get(self): """Returns a list of scan dataset information. @@ -523,13 +477,19 @@ def get(self): >>> import json >>> # Get an info dict about all dataset: >>> res = requests.get("http://127.0.0.1:5000/scans") - >>> scans = json.loads(res.content) + >>> scans_list = json.loads(res.content) >>> # List the known dataset id: - >>> print([scan['id'] for scan in scans]) + >>> print(scans_list) ['arabidopsis000', 'virtual_plant_analyzed', 'real_plant_analyzed', 'real_plant', 'virtual_plant', 'models'] + >>> res = requests.get('http://127.0.0.1:5000/scans?filterQuery={"object":{"species":"Arabidopsis.*"}}&fuzzy="true"') + >>> res.content.decode() """ - return list_scans_info(self.db.get_scans(), query=request.args.get('filterQuery'), logger=self.logger) + query = request.args.get('filterQuery', None) + fuzzy = request.args.get('fuzzy', False, type=bool) + if query is not None: + query = json.loads(query) + return self.db.list_scans(query=query, fuzzy=fuzzy) class Scan(Resource): @@ -565,7 +525,8 @@ def get(self, scan_id): 2024-08-19 11:12:25 """ - return get_scan_data(self.db.get_scan(scan_id), logger=self.logger) + #return get_scan_data(self.db.get_scan(scan_id), logger=self.logger) + return get_scan_info(self.db.get_scan(scan_id), logger=self.logger) class File(Resource): diff --git a/src/plantdb/rest_api_client.py b/src/plantdb/rest_api_client.py index ae656e6..0014463 100644 --- a/src/plantdb/rest_api_client.py +++ b/src/plantdb/rest_api_client.py @@ -95,6 +95,31 @@ def test_db_availability(host=REST_API_URL, port=REST_API_PORT): return True +def list_scan_names(host=REST_API_URL, port=REST_API_PORT): + """List the names of the scan datasets served by the PlantDB REST API. + + Parameters + ---------- + host : str, optional + The IP address of the PlantDB REST API. Defaults to ``"127.0.0.1"``. + port : str or int, optional + The port of the PlantDB REST API. Defaults to ``5000``. + + Returns + ------- + list + The list of scan dataset names served by the PlantDB REST API. + + Examples + -------- + >>> from plantdb.rest_api_client import list_scan_names + >>> # This example requires the PlantDB REST API to be active (`fsdb_rest_api --test` from plantdb library) + >>> print(list_scan_names()) + ['arabidopsis000', 'real_plant', 'real_plant_analyzed', 'virtual_plant', 'virtual_plant_analyzed'] + """ + return sorted(requests.get(url=f"{base_url(host, port)}/scans").json()) + + def get_scans_info(host=REST_API_URL, port=REST_API_PORT): """Retrieve the information dictionary for all scans from the PlantDB REST API. @@ -116,7 +141,8 @@ def get_scans_info(host=REST_API_URL, port=REST_API_PORT): >>> # This example requires the PlantDB REST API to be active (`fsdb_rest_api --test` from plantdb library) >>> get_scans_info() """ - return requests.get(url=f"{base_url(host, port)}/scans").json() + scan_list = list_scan_names(host, port) + return [requests.get(url=f"{base_url(host, port)}/scans/{scan}").json() for scan in scan_list] def parse_scans_info(host=REST_API_URL, port=REST_API_PORT): @@ -180,31 +206,6 @@ def get_scan_data(scan_id, host=REST_API_URL, port=REST_API_PORT): return requests.get(url=f"{base_url(host, port)}/scans/{scan_id}").json() -def list_scan_names(host=REST_API_URL, port=REST_API_PORT): - """List the names of the scan datasets served by the PlantDB REST API. - - Parameters - ---------- - host : str, optional - The IP address of the PlantDB REST API. Defaults to ``"127.0.0.1"``. - port : str or int, optional - The port of the PlantDB REST API. Defaults to ``5000``. - - Returns - ------- - list - The list of scan dataset names served by the PlantDB REST API. - - Examples - -------- - >>> from plantdb.rest_api_client import list_scan_names - >>> # This example requires the PlantDB REST API to be active (`fsdb_rest_api --test` from plantdb library) - >>> print(list_scan_names()) - ['arabidopsis000', 'real_plant', 'real_plant_analyzed', 'virtual_plant', 'virtual_plant_analyzed'] - """ - return sorted(parse_scans_info(host, port).keys()) - - def scan_preview_image_url(scan_id, host=REST_API_URL, port=REST_API_PORT, size="thumb"): """Get the URL to the preview image for a scan dataset served by the PlantDB REST API.