From c7f3983baae698e3ef6e7ff2ff85d473f65f4a6b Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:11:51 -0700 Subject: [PATCH 01/16] get the mqt version added in version endpoint --- viewer/functions/common.py | 2 ++ viewer/pages/site.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/viewer/functions/common.py b/viewer/functions/common.py index 23c588826..816f9d5f6 100755 --- a/viewer/functions/common.py +++ b/viewer/functions/common.py @@ -35,6 +35,7 @@ import gridfs import itkdb import module_qc_analysis_tools import module_qc_database_tools +import module_qc_tools import pytz import requests import urllib3 @@ -330,6 +331,7 @@ if "set_variables" not in globals(): qcAnalysisVersion = "2.6.7" itkdb_version = itkdb.__version__ + mqt_version = module_qc_tools.__version__ mqat_version = module_qc_analysis_tools.__version__ mqdbt_version = module_qc_database_tools.__version__ diff --git a/viewer/pages/site.py b/viewer/pages/site.py index d0ade75b8..1f909ced1 100644 --- a/viewer/pages/site.py +++ b/viewer/pages/site.py @@ -7,6 +7,7 @@ from functions.common import ( itkdb_version, mqat_version, mqdbt_version, + mqt_version, qcAnalysisVersion, ) @@ -25,6 +26,7 @@ def version(): "python": sys.version, "localdb": qcAnalysisVersion, "itkdb": itkdb_version, + "mqt": mqt_version, "mqat": mqat_version, "mqdbt": mqdbt_version, } -- GitLab From af4aab48895c2219c855e3f95a7423bdc706522e Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:30:40 -0700 Subject: [PATCH 02/16] add api to extract LVPS current for serial number --- viewer/pages/api.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 613d51318..fd2b58fd1 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -4,8 +4,20 @@ from __future__ import annotations import logging import types +import module_qc_tools from flask import Blueprint, jsonify, request from functions.api import * +from module_qc_data_tools.utils import ( + get_chip_type_from_serial_number, + get_layer_from_sn, +) +from module_qc_database_tools.db.local import get_bom_info +from module_qc_database_tools.utils import ( + get_cutFile_suffix, +) +from module_qc_tools.utils.misc import ( + load_meas_config, +) def json_route(self, rule, **options): @@ -125,3 +137,22 @@ def get_tags_for_site(site): except Exception as e: logger.error(f"Error for site {site}: {e}") return {"error": str(e)}, 500 + + +@rest_api.json_route( + "/components//properties/module-current", methods=["GET"] +) +def get_module_current(serial_number): + """ """ + layer = get_layer_from_sn(serial_number) + chip_type = get_chip_type_from_serial_number(serial_number) + bom = get_bom_info(localdb, serial_number) + bom_suffix = get_cutFile_suffix(bom) + config = load_meas_config( + module_qc_tools.data / "configs" / "meas_config.json", + test_type=None, + chip_type=chip_type, + bom_type=bom_suffix, + layer=layer, + ) + return config["i_config"] -- GitLab From b2d787e8276928beecc39406fc69c1aaad97d195 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:40:43 -0700 Subject: [PATCH 03/16] improve and put under /api --- viewer/app.py | 2 +- viewer/pages/api.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/viewer/app.py b/viewer/app.py index 99a5e54d4..0aa2b3e1d 100755 --- a/viewer/app.py +++ b/viewer/app.py @@ -150,7 +150,7 @@ with app.app_context(): app.register_blueprint(site_api) ## API - app.register_blueprint(rest_api) + app.register_blueprint(rest_api, url_prefix="/api") logging.getLogger("werkzeug").setLevel(logging.ERROR) logging.getLogger("module_qc_database_tools").setLevel(logging.WARNING) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index fd2b58fd1..f4a4db4d4 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -13,6 +13,7 @@ from module_qc_data_tools.utils import ( ) from module_qc_database_tools.db.local import get_bom_info from module_qc_database_tools.utils import ( + default_BOMCode_from_layer, get_cutFile_suffix, ) from module_qc_tools.utils.misc import ( @@ -147,7 +148,8 @@ def get_module_current(serial_number): layer = get_layer_from_sn(serial_number) chip_type = get_chip_type_from_serial_number(serial_number) bom = get_bom_info(localdb, serial_number) - bom_suffix = get_cutFile_suffix(bom) + default_bom = default_BOMCode_from_layer(layer) + bom_suffix = get_cutFile_suffix(bom.get("code", default_bom)) config = load_meas_config( module_qc_tools.data / "configs" / "meas_config.json", test_type=None, -- GitLab From 0ab38a8bec028b5cbdb47617f5b3e640bbf9560e Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:49:31 -0700 Subject: [PATCH 04/16] get_nominal_current is useful instead --- viewer/pages/api.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index f4a4db4d4..f787e2afe 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -7,6 +7,7 @@ import types import module_qc_tools from flask import Blueprint, jsonify, request from functions.api import * +from module_qc_data_tools import get_n_chips, get_nominal_current from module_qc_data_tools.utils import ( get_chip_type_from_serial_number, get_layer_from_sn, @@ -16,9 +17,6 @@ from module_qc_database_tools.utils import ( default_BOMCode_from_layer, get_cutFile_suffix, ) -from module_qc_tools.utils.misc import ( - load_meas_config, -) def json_route(self, rule, **options): @@ -141,20 +139,17 @@ def get_tags_for_site(site): @rest_api.json_route( - "/components//properties/module-current", methods=["GET"] + "/components//properties/nominal-current", methods=["GET"] ) -def get_module_current(serial_number): +def get_module_nominal_current(serial_number): """ """ layer = get_layer_from_sn(serial_number) chip_type = get_chip_type_from_serial_number(serial_number) + n_chips = get_n_chips(layer) bom = get_bom_info(localdb, serial_number) default_bom = default_BOMCode_from_layer(layer) - bom_suffix = get_cutFile_suffix(bom.get("code", default_bom)) - config = load_meas_config( - module_qc_tools.data / "configs" / "meas_config.json", - test_type=None, - chip_type=chip_type, - bom_type=bom_suffix, - layer=layer, - ) - return config["i_config"] + bom_type = get_cutFile_suffix(bom.get("code", default_bom)) + + config_path = module_qc_tools.data / "configs" / "meas_config.json" + config = json.loads(config_path.read_text(encoding="utf-8")) + return get_nominal_current(config, layer, chip_type, bom_type, n_chips) -- GitLab From be31ebc12df214ddcccbb5f196ef0dd8adf0613e Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:50:09 -0700 Subject: [PATCH 05/16] output current and version as dict --- viewer/pages/api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index f787e2afe..5a98c6aab 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -152,4 +152,7 @@ def get_module_nominal_current(serial_number): config_path = module_qc_tools.data / "configs" / "meas_config.json" config = json.loads(config_path.read_text(encoding="utf-8")) - return get_nominal_current(config, layer, chip_type, bom_type, n_chips) + return { + "current": get_nominal_current(config, layer, chip_type, bom_type, n_chips), + "module-qc-tools": module_qc_tools.__version__, + } -- GitLab From d8b30f3170e9fc5fdab8dd9d06b10c4b33507cdf Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:52:39 -0700 Subject: [PATCH 06/16] missed import --- viewer/pages/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 5a98c6aab..2f0df0738 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -1,6 +1,7 @@ # imports from __future__ import annotations +import json import logging import types -- GitLab From 640d9c5f9ee1bb94d90c2667e6a60b130a6d7b05 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 10:57:33 -0700 Subject: [PATCH 07/16] provide customizable properties query --- viewer/pages/api.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 2f0df0738..8c92b77f4 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -139,10 +139,11 @@ def get_tags_for_site(site): return {"error": str(e)}, 500 +@rest_api.json_route("/components//properties", methods=["GET"]) @rest_api.json_route( - "/components//properties/nominal-current", methods=["GET"] + "/components//properties/", methods=["GET"] ) -def get_module_nominal_current(serial_number): +def get_module_nominal_current(serial_number, key=None): """ """ layer = get_layer_from_sn(serial_number) chip_type = get_chip_type_from_serial_number(serial_number) @@ -153,7 +154,17 @@ def get_module_nominal_current(serial_number): config_path = module_qc_tools.data / "configs" / "meas_config.json" config = json.loads(config_path.read_text(encoding="utf-8")) - return { - "current": get_nominal_current(config, layer, chip_type, bom_type, n_chips), + properties = { + "layer": layer, + "chip_type": chip_type, + "n_chips": n_chips, + "bom": bom, + "nominal_current": get_nominal_current( + config, layer, chip_type, bom_type, n_chips + ), "module-qc-tools": module_qc_tools.__version__, } + if not key: + return properties + + return properties[key] -- GitLab From ab7070a90a73e184d9f7fe01e9c6ea9c6a44078a Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:07:18 -0700 Subject: [PATCH 08/16] fix imports --- viewer/pages/api.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 8c92b77f4..aba4a13dc 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -7,7 +7,13 @@ import types import module_qc_tools from flask import Blueprint, jsonify, request -from functions.api import * +from functions.api import ( + get_list_of_sites, + get_serial_numbers_for_site, + get_serial_numbers_for_tag, + get_tags, +) +from functions.common import localdb from module_qc_data_tools import get_n_chips, get_nominal_current from module_qc_data_tools.utils import ( get_chip_type_from_serial_number, -- GitLab From bb0eb931e1cd8a2842405d969391e2491f61265a Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:12:30 -0700 Subject: [PATCH 09/16] handle missing keys --- viewer/pages/api.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index aba4a13dc..5d5ba7ced 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -173,4 +173,7 @@ def get_module_nominal_current(serial_number, key=None): if not key: return properties - return properties[key] + try: + return properties[key] + except KeyError as exc: + return {"error": exc, "key": key, "valid_keys": list(properties)} -- GitLab From e2b8ff3544308a68493563362203d47952721ba8 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:13:12 -0700 Subject: [PATCH 10/16] need trailing slash --- viewer/pages/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 5d5ba7ced..c846dbbef 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -145,7 +145,7 @@ def get_tags_for_site(site): return {"error": str(e)}, 500 -@rest_api.json_route("/components//properties", methods=["GET"]) +@rest_api.json_route("/components//properties/", methods=["GET"]) @rest_api.json_route( "/components//properties/", methods=["GET"] ) -- GitLab From 22152468a7436abfe697593d99fc922c3ea53643 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:17:02 -0700 Subject: [PATCH 11/16] fix exception handling --- viewer/pages/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index c846dbbef..0b134efa6 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -175,5 +175,5 @@ def get_module_nominal_current(serial_number, key=None): try: return properties[key] - except KeyError as exc: - return {"error": exc, "key": key, "valid_keys": list(properties)} + except KeyError: + return {"error": "Missing Key", "key": key, "valid_keys": list(properties)} -- GitLab From cdb48ce98e31e354f3eb4ddec26537d553a4b19c Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:18:20 -0700 Subject: [PATCH 12/16] debugging information --- viewer/pages/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 0b134efa6..24cc22abd 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -170,6 +170,8 @@ def get_module_nominal_current(serial_number, key=None): ), "module-qc-tools": module_qc_tools.__version__, } + print(key) + print(properties) if not key: return properties -- GitLab From eeead0843ffb7b2382b69b30b6fab21e696b29d1 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:25:22 -0700 Subject: [PATCH 13/16] more debug --- viewer/pages/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 24cc22abd..241bd6cd4 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -173,8 +173,10 @@ def get_module_nominal_current(serial_number, key=None): print(key) print(properties) if not key: + print("not key, so returning properties") return properties + print("returning properties[key] instead") try: return properties[key] except KeyError: -- GitLab From bee152e1289a1c8e96560309473986c20af44afb Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:31:57 -0700 Subject: [PATCH 14/16] more debug --- viewer/pages/api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 241bd6cd4..064e9cb44 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -172,6 +172,9 @@ def get_module_nominal_current(serial_number, key=None): } print(key) print(properties) + print("Checking types:") + for k, v in properties.items(): + print(f"{k}: {type(v)} = {v}") if not key: print("not key, so returning properties") return properties -- GitLab From 2ff066a7deee5fd9c318a198b3717cfb4adba631 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:40:33 -0700 Subject: [PATCH 15/16] fix up --- viewer/pages/api.py | 59 +++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/viewer/pages/api.py b/viewer/pages/api.py index 064e9cb44..42fd5ba14 100644 --- a/viewer/pages/api.py +++ b/viewer/pages/api.py @@ -3,7 +3,6 @@ from __future__ import annotations import json import logging -import types import module_qc_tools from flask import Blueprint, jsonify, request @@ -25,21 +24,6 @@ from module_qc_database_tools.utils import ( get_cutFile_suffix, ) - -def json_route(self, rule, **options): - def decorator(f): - endpoint = options.pop("endpoint", f.__name__) - - def new_f(*args, **kwargs): - return jsonify(f(*args, **kwargs)) - - self.add_url_rule(rule, endpoint, new_f, **options) - - return new_f - - return decorator - - ####################### ### route functions ### ####################### @@ -47,10 +31,9 @@ def json_route(self, rule, **options): logger = logging.getLogger("localdb") rest_api = Blueprint("api", __name__) -rest_api.json_route = types.MethodType(json_route, rest_api) -@rest_api.json_route("/tags//serial_numbers", methods=["GET"]) +@rest_api.route("/tags//serial_numbers", methods=["GET"]) def get_serial_numbers_for_tag_endpoint(tag): """ Endpoint to retrieve the module serialNumbers of the latest entry associated to one or more tags. @@ -71,15 +54,15 @@ def get_serial_numbers_for_tag_endpoint(tag): verbose = request.args.get("verbose") == "true" try: - return get_serial_numbers_for_tag(tag, verbose) + return jsonify(get_serial_numbers_for_tag(tag, verbose)) except Exception as e: # Catch any other unexpected errors and return a generic error message logger.error(f"Error for tag {tag}: {e}") - return {"error": str(e)}, 500 + return jsonify({"error": str(e)}), 500 -@rest_api.json_route("/sites//serial_numbers", methods=["GET"]) +@rest_api.route("/sites//serial_numbers", methods=["GET"]) def get_serial_numbers_for_site_endpoint(site): """ Endpoint to retrieve all modules at the specified site and return a list including for each, serial numbers, chip type and current stage @@ -99,14 +82,14 @@ def get_serial_numbers_for_site_endpoint(site): try: result = get_serial_numbers_for_site(site, verbose) if result: - return result - return {"error": "no modules found for the site provided"}, 404 + return jsonify(result) + return jsonify({"error": "no modules found for the site provided"}), 404 except Exception as e: logger.error(f"Error for site {site}: {e}") - return {"error": str(e)}, 500 # Handle unexpected errors + return jsonify({"error": str(e)}), 500 # Handle unexpected errors -@rest_api.json_route("/sites/", methods=["GET"]) +@rest_api.route("/sites/", methods=["GET"]) def get_list_of_sites_endpoint(): """ Endpoint to retrieve the list of all sites @@ -114,13 +97,13 @@ def get_list_of_sites_endpoint(): List of site codes (institutions) """ try: - return get_list_of_sites() + return jsonify(get_list_of_sites()) except Exception as e: logger.error(f"Error: {e}") - return {"error": str(e)}, 500 + return jsonify({"error": str(e)}), 500 -@rest_api.json_route("/sites//tags", methods=["GET"]) +@rest_api.route("/sites//tags", methods=["GET"]) def get_tags_for_site(site): """ Endpoint to retrieve the list of all tags for a given site. @@ -145,8 +128,8 @@ def get_tags_for_site(site): return {"error": str(e)}, 500 -@rest_api.json_route("/components//properties/", methods=["GET"]) -@rest_api.json_route( +@rest_api.route("/components//properties/", methods=["GET"]) +@rest_api.route( "/components//properties/", methods=["GET"] ) def get_module_nominal_current(serial_number, key=None): @@ -170,17 +153,13 @@ def get_module_nominal_current(serial_number, key=None): ), "module-qc-tools": module_qc_tools.__version__, } - print(key) - print(properties) - print("Checking types:") - for k, v in properties.items(): - print(f"{k}: {type(v)} = {v}") + if not key: - print("not key, so returning properties") - return properties + return jsonify(properties) - print("returning properties[key] instead") try: - return properties[key] + return jsonify(properties[key]) except KeyError: - return {"error": "Missing Key", "key": key, "valid_keys": list(properties)} + return jsonify( + {"error": "Missing Key", "key": key, "valid_keys": list(properties)} + ), 400 -- GitLab From b4baf3866587ba2eef8cfa35d2cda74446a340ff Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Fri, 25 Jul 2025 11:48:35 -0700 Subject: [PATCH 16/16] update changelog --- docs/release-note.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/release-note.md b/docs/release-note.md index d6d929cff..8a1b65ebd 100644 --- a/docs/release-note.md +++ b/docs/release-note.md @@ -4,6 +4,11 @@ ### Release: +- add new api endpoint `/api/components//properties/` and + `/api/components//properties/` for querying information + about the component (!301) +- move the JSON API under an `/api` prefix + ## [v2.6.7](https://gitlab.cern.ch/YARR/localdb-tools/-/tree/v2.6.7) ### Release: 23 Jul 2025 -- GitLab