From 919b170ca286c560823fb2ed72763cc4b27a1304 Mon Sep 17 00:00:00 2001 From: Yuma Uematsu Date: Sat, 4 Oct 2025 17:47:44 +0200 Subject: [PATCH] OB Loaded Module Cell test linked to Module test --- .../itkpd_interface/recursiveUploader.py | 2 +- viewer/pages/qc.py | 163 +++++++++++------- .../component/latest_test_index.html | 14 +- .../templates/displayResults/qc_others.html | 2 + 4 files changed, 110 insertions(+), 71 deletions(-) diff --git a/viewer/functions/itkpd_interface/recursiveUploader.py b/viewer/functions/itkpd_interface/recursiveUploader.py index 871d3384c..825817072 100755 --- a/viewer/functions/itkpd_interface/recursiveUploader.py +++ b/viewer/functions/itkpd_interface/recursiveUploader.py @@ -212,7 +212,7 @@ class RecursiveUploader(PDInterface.PDInterface): for key in ["properties", "property", "Metadata", "metadata", "DCSdata"]: properties.update(test["results"].pop(key, {})) - out["properties"].update(properties) + out.update({"properties": properties}) # in general, just copy the LocalDB results field... out.update({"results": test.get("results")}) diff --git a/viewer/pages/qc.py b/viewer/pages/qc.py index d5705f7c7..74f7fa72d 100644 --- a/viewer/pages/qc.py +++ b/viewer/pages/qc.py @@ -17,6 +17,7 @@ import traceback import unicodedata import zipfile from datetime import datetime, timezone +from itertools import islice from pathlib import Path from typing import Any @@ -79,6 +80,7 @@ from module_qc_data_tools.utils import ( ) from module_qc_database_tools.chip_config_api import ChipConfigAPI from module_qc_database_tools.core import DPPort, Module +from module_qc_database_tools.db.local import get_children from module_qc_database_tools.iv import fetch_reference_ivs from module_qc_database_tools.sync_component_stages import sync_component_stages from pymongo import ( @@ -2085,7 +2087,12 @@ def processCreateSummaryDefault(pageDocs): resultCandidates = {} # List of supported test types - processFunctions = {"E_SUMMARY": process_E_SUMMARY} + processFunctions = { + "E_SUMMARY": process_E_SUMMARY, + "LOADED_CELL_E_SUMMARY": register_module_result, + "LOADED_CELL_IV_MEASURE": register_module_result, + "LOADED_CELL_THERMAL_PERFORMANCE": register_module_result, + } # Loop over parameters for parameter in testFormat["parameters"]: @@ -2112,7 +2119,12 @@ def processCreateSummaryInput(pageDocs): resultCandidates = {} # List of supported test types - processFunctions = {"E_SUMMARY": process_E_SUMMARY} + processFunctions = { + "E_SUMMARY": process_E_SUMMARY, + "LOADED_CELL_E_SUMMARY": register_module_result, + "LOADED_CELL_IV_MEASURE": register_module_result, + "LOADED_CELL_THERMAL_PERFORMANCE": register_module_result, + } # Loop-1: gather all inputs for parameter in testFormat["parameters"]: @@ -2326,6 +2338,21 @@ def process_E_SUMMARY(component, stage, code, resultCandidates, _mode): ) +def register_module_result(component, stage, code, resultCandidates, _mode): + # code format: {PARENTTYPE}_{TEST_NAME}_MODULE_LINK + code_format = re.compile(r"^(?P[^_]+)_(?P.+)_MODULE_LINK$") + match = code_format.fullmatch(code) + if not match: + return + + children_iter = get_children(localdb, component, component_type="module") + children = list(islice(children_iter, 2)) + if len(children) == 1: + resultCandidates[code] = getCandidateResults( + str(children[0]["_id"]), stage, match["TEST_NAME"] + ) + + def processCreateSummaryDone(pageDocs): component = pageDocs["component"] stage = pageDocs["stage"] @@ -2408,74 +2435,90 @@ def processCreateSummaryDone(pageDocs): if out["results"][code] is None: logger.warning(f"parameter { code } was not filled (None)") - # Fill the rest parameters - summary_keys = { - "MODULE_BAD_PIXEL_NUMBER": "PIXEL_FAILURE_FAILING_PIXELS", - "MODULE_ELECTRICALLY_BAD_PIXEL_NUMBER": "PIXEL_FAILURE_ELECTRICALLY_FAILED", - "MODULE_DISCONNECTED_PIXEL_NUMBER": "PIXEL_FAILURE_DISCONNECTED_PIXELS", - "MODULE_DISABLED_COLUMNS_NUMBER": "PIXEL_FAILURE_COLUMN_DISABLED", - } + if testType == "E_SUMMARY": + # Fill the rest parameters + summary_keys = { + "MODULE_BAD_PIXEL_NUMBER": "PIXEL_FAILURE_FAILING_PIXELS", + "MODULE_ELECTRICALLY_BAD_PIXEL_NUMBER": "PIXEL_FAILURE_ELECTRICALLY_FAILED", + "MODULE_DISCONNECTED_PIXEL_NUMBER": "PIXEL_FAILURE_DISCONNECTED_PIXELS", + "MODULE_DISABLED_COLUMNS_NUMBER": "PIXEL_FAILURE_COLUMN_DISABLED", + } - # Parameter Loop-2: collect bad pixels - for parameter, key in summary_keys.items(): - # print( f'######## loop2: {parameter}' ) - for feIndex in range(1, 5): - referenceResultId = None - referenceResult = None - try: - referenceResultId = out["results"][ - f"MODULE_PIXEL_FAILURE_ANALYSIS_FE_LINK_{feIndex}" - ] - except KeyError: - msg = f"Unable to find the MODULE_PIXEL_FAILURE_ANALYSIS_FE_LINK_{feIndex} entry" - logger.exception(msg) - continue + # Parameter Loop-2: collect bad pixels + for parameter, key in summary_keys.items(): + # print( f'######## loop2: {parameter}' ) + for feIndex in range(1, 5): + referenceResultId = None + referenceResult = None + try: + referenceResultId = out["results"][ + f"MODULE_PIXEL_FAILURE_ANALYSIS_FE_LINK_{feIndex}" + ] + except KeyError: + msg = f"Unable to find the MODULE_PIXEL_FAILURE_ANALYSIS_FE_LINK_{feIndex} entry" + logger.exception(msg) + continue - if referenceResultId: - referenceResult = localdb.QC.result.find_one( - {"_id": ObjectId(referenceResultId)} - ) + if referenceResultId: + referenceResult = localdb.QC.result.find_one( + {"_id": ObjectId(referenceResultId)} + ) - if referenceResult: - value = referenceResult.get("results", {}).get(key) + if referenceResult: + value = referenceResult.get("results", {}).get(key) - if value is not None: - if value >= 0: - out["results"][f"{parameter}_FE_{feIndex}"] = value - logger.info(f"inserted {value} to {parameter}_FE_{feIndex}") + if value is not None: + if value >= 0: + out["results"][f"{parameter}_FE_{feIndex}"] = value + logger.info(f"inserted {value} to {parameter}_FE_{feIndex}") + else: + out["results"][f"{parameter}_FE_{feIndex}"] = None else: - out["results"][f"{parameter}_FE_{feIndex}"] = None - else: - logger.info( - f"reference result for {parameter}_FE_{feIndex} is missing, skipped to evaluate" - ) + logger.info( + f"reference result for {parameter}_FE_{feIndex} is missing, skipped to evaluate" + ) - # Sum up all FEs - for var in summary_keys: - elems = [ - out["results"][f"{var}_FE_{feIndex}"] - for feIndex in range(1, 5) - if out["results"].get(f"{var}_FE_{feIndex}") is not None - ] - if len(elems) == 0: - out["results"][var] = None - else: - out["results"][var] = sum(elems) + # Sum up all FEs + for var in summary_keys: + elems = [ + out["results"][f"{var}_FE_{feIndex}"] + for feIndex in range(1, 5) + if out["results"].get(f"{var}_FE_{feIndex}") is not None + ] + if len(elems) == 0: + out["results"][var] = None + else: + out["results"][var] = sum(elems) - for prop in outFormat["properties"]: - if prop["code"] == "ANALYSIS_VERSION": - out["results"]["properties"][prop["code"]] = qcAnalysisVersion + for prop in outFormat["properties"]: + if prop["code"] == "ANALYSIS_VERSION": + out["results"]["properties"][prop["code"]] = qcAnalysisVersion + + if prop["code"] == "MODULE_TEMPERATURE": + if stage.lower().find("warm") >= 0: + out["results"]["properties"][prop["code"]] = 20 + elif stage.lower().find("cold") >= 0: + out["results"]["properties"][prop["code"]] = -15 + else: + out["results"]["properties"][prop["code"]] = -99 + + else: + for parameter in outFormat["parameters"]: + if not parameter["required"]: + continue + code = parameter["code"] + # here, pageDocs['input'] is ImmutableMultiDict, use get(key) + candidateId = pageDocs["input"].get(code) + result = None + if candidateId and candidateId != "skipped": + result = localdb.QC.result.find_one({"_id": ObjectId(candidateId)}) - if prop["code"] == "MODULE_TEMPERATURE": - if stage.lower().find("warm") >= 0: - out["results"]["properties"][prop["code"]] = 20 - elif stage.lower().find("cold") >= 0: - out["results"]["properties"][prop["code"]] = -15 + if result: + summaryEvals.append(result.get("passed", False)) else: - out["results"]["properties"][prop["code"]] = -99 + summaryEvals.append(False) - qcPassed = all(summaryEvals) - out["passed"] = qcPassed + out["passed"] = all(summaryEvals) # logger.info( pprint.pformat( out ) ) diff --git a/viewer/templates/component/latest_test_index.html b/viewer/templates/component/latest_test_index.html index f946d16ab..3c58a41c8 100644 --- a/viewer/templates/component/latest_test_index.html +++ b/viewer/templates/component/latest_test_index.html @@ -106,7 +106,7 @@ {% elif test['name'] == "VISUAL_INSPECTION" %}  (Submit TestRun via module-qc-nonelec-gui) - {% elif test['name'] in ["IV_MEASURE", "LONG_TERM_STABILITY_DCS", "WIREBOND_PULL_TEST"] %} + {% elif test['name'] in ["IV_MEASURE", "LONG_TERM_STABILITY_DCS", "WIREBOND_PULL_TEST", "THERMAL_PERFORMANCE"] %} @@ -124,20 +124,14 @@ > - {% elif test['name'] == "E_SUMMARY" %} + {% elif test['name'].startswith("LOADED_CELL") %} - + - {% elif "VISUAL_INSPECTION" in test['name'] or "FLATNESS" in test['name'] or "METROLOGY" in test['name'] or "WIREBOND_PULL_TEST" in test['name'] %} + {% elif "VISUAL_INSPECTION" in test['name'] or "FLATNESS" in test['name'] or "METROLOGY" in test['name'] %}  (Submit TestRun via module-qc-nonelec-gui) - {% elif "IV_MEASURE" in test['name'] %} - - - - {% elif test['name'] == "LONG_TERM_STABILITY_DCS" %} -  (Submit TestRun via module-qc-tools) {% else %} (Server's version: {{ server_version }})

+ {% elif component['qctest']['results']['testType'].startswith("LOADED_CELL_") %} + {# No recycle API provided #} {% else %} {# Simple analysis - use smart logic #} {% if is_up_to_date %} -- GitLab