From f62dad20c09ac1337e8619720c34b1539a94a0b8 Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Tue, 19 Aug 2025 09:35:00 -0700 Subject: [PATCH 1/2] process testruns of children first --- .../recursiveComponentsSynchronizer.py | 219 ++++++++++++++++-- 1 file changed, 203 insertions(+), 16 deletions(-) diff --git a/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py b/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py index 2e8b3a5ac..80b2a877d 100755 --- a/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py +++ b/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py @@ -652,9 +652,9 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): logger.exception(msg) self.error_list.append(msg) - def __bookComponent(self, cpt_doc): + def __bookComponentWithoutTestRuns(self, cpt_doc): logger.info( - f"Registering {cpt_doc['componentType']['code']} component {cpt_doc['serialNumber']}" + f"Registering {cpt_doc['componentType']['code']} component {cpt_doc['serialNumber']} (without test runs)" ) # Component skeleton @@ -705,9 +705,11 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): cpt_qc_doc["stage"] = currStage - # Synchronize the QC status to ProdDB - futures = self.__processTestRuns(cpt_doc, cpt_localdb_doc, cpt_qc_doc) + return cpt_localdb_doc, cpt_qc_doc + def __bookComponent(self, cpt_doc): + cpt_localdb_doc, cpt_qc_doc = self.__bookComponentWithoutTestRuns(cpt_doc) + futures = self.__processTestRuns(cpt_doc, cpt_localdb_doc, cpt_qc_doc) return cpt_localdb_doc, futures def __processTestRuns(self, cpt_doc, cpt_localdb_doc, cpt_qc_doc): @@ -950,6 +952,183 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): # wait for recursive calls to finish (meaning inside spawned subthread) return self.as_completed(futures) + def __loopSubComponentsWithoutTestRuns( + self, cpt_doc, cpt_localdb_doc, parent_module_localdb_doc=None, depth=0 + ): + """Register all subcomponents without processing their test runs""" + if cpt_doc["children"] is None: + return + + cpt_type = cpt_doc.get("componentType").get("code") + if parent_module_localdb_doc is None and cpt_type == "MODULE": + parent_module_localdb_doc = cpt_localdb_doc + + for child_cpt_metadata in cpt_doc["children"]: + # Check which child component type + child_type = child_cpt_metadata["componentType"]["code"] + + # Skip carrier and children of OB Bare Module Cell + if child_type in ["MODULE_CARRIER", "OB_COOLING_BLOACK", "OB_PG_TYLE"]: + continue + + logger.debug(pprint.pformat(child_cpt_metadata)) + + try: + child_id = child_cpt_metadata["component"]["code"] + except Exception: + logger.warning("child component ID was not found: skipping") + continue + + # Acquire the child component from ProdDB + child_doc = self.getCompFromProdDB(child_id) + + child_cpt_sn = child_doc["serialNumber"] + + self.cpt_sn.append(child_cpt_sn) + + child_cpt_localdb_doc, child_cpt_qc_doc = ( + self.__bookComponentWithoutTestRuns(child_doc) + ) + + # Create a new child-parent relation + + # For FE chips, make an additional direct link between the chip and the parent module + if child_type == "FE_CHIP" and parent_module_localdb_doc is not None: + try: + cpr_info = self.__createCprDoc( + parent_module_localdb_doc, child_cpt_localdb_doc + ) + + logger.info( + f"checking to see if parent={parent_module_localdb_doc.get('serialNumber')} and child={child_cpt_sn} are currently related (parent={parent_module_localdb_doc['_id']}, child[FE_CHIP]={child_cpt_localdb_doc['_id']})." + ) + + self.cpr_list.append(cpr_info) + msg = f"{child_type} {child_cpt_sn} cpr relation was created for parent={parent_module_localdb_doc.get('serialNumber')} (parent={cpr_info['parent']}, child={cpr_info['child']})." + logger.info(msg) + + except Exception as e: + logger.warning(str(e)) + + # The following generic linking applies to all sub-components + cpr_info = self.__createCprDoc(cpt_localdb_doc, child_cpt_localdb_doc) + + logger.info( + f"checking to see if parent={cpt_localdb_doc['serialNumber']} and child={child_cpt_sn} are currently related (parent={cpt_localdb_doc['_id']}, child[{child_type}]={child_cpt_localdb_doc['_id']})." + ) + + self.cpr_list.append(cpr_info) + msg = f"{child_type} {child_cpt_sn} cpr relation was created for parent={cpt_localdb_doc.get('serialNumber')} (parent={cpr_info['parent']}, child={cpr_info['child']})." + logger.info(msg) + + # Recursively register subcomponents (without test runs) + self.__loopSubComponentsWithoutTestRuns( + child_doc, + child_cpt_localdb_doc, + parent_module_localdb_doc, + depth=depth + 1, + ) + + def __processTestRunsRecursively( + self, cpt_doc, cpt_localdb_doc, parent_module_localdb_doc=None, depth=0 + ): + """Process test runs depth-first (children before parents)""" + futures = {} + + # Process children first (depth-first) + if cpt_doc["children"] is not None: + for child_cpt_metadata in cpt_doc["children"]: + child_type = child_cpt_metadata["componentType"]["code"] + + # Skip carrier and children of OB Bare Module Cell + if child_type in ["MODULE_CARRIER", "OB_COOLING_BLOACK", "OB_PG_TYLE"]: + continue + + try: + child_id = child_cpt_metadata["component"]["code"] + except Exception: + logger.warning("child component ID was not found: skipping") + continue + + # Acquire the child component from ProdDB + child_doc = self.getCompFromProdDB(child_id) + + # Find the child component in our component_list to get the localdb doc + child_cpt_localdb_doc = None + for cpt in self.component_list: + if str(cpt["_id"]) == child_doc["id"]: + child_cpt_localdb_doc = cpt + break + + if child_cpt_localdb_doc is None: + logger.warning( + f"Could not find child component {child_doc['serialNumber']} in component list" + ) + continue + + # Recursively process child test runs first + child_futures = self.__processTestRunsRecursively( + child_doc, + child_cpt_localdb_doc, + parent_module_localdb_doc, + depth=depth + 1, + ) + futures.update(child_futures) + + # Wait for all child test runs to complete before processing this component's test runs + if futures: + logger.info( + f"Waiting for {len(futures)} child test run futures to complete before processing parent" + ) + self.as_completed(futures) + futures = {} # Clear futures since we've waited for them + + # Now process this component's test runs + cpt_qc_doc = self.__createComponentQCStatusDoc(cpt_doc) + + # Apply the same QC status logic from __bookComponentWithoutTestRuns + stageFlow = self.stageFlows[cpt_doc["componentType"]["code"]] + stage_history = {stage["code"] for stage in cpt_doc["stages"]} + + resetUpToStage = cpt_qc_doc["stage"] + if self.resetStage and self.resetStage in stageFlow["all"]: + resetUpToStageIndex = max(0, stageFlow["all"].index(self.resetStage) - 1) + resetUpToStage = stageFlow["all"][resetUpToStageIndex] + + upload_status = {} + for stage in stageFlow["all"]: + if ( + stageFlow["all"].index(stage) <= stageFlow["all"].index(resetUpToStage) + and stage in stage_history + ): + status = "1" + else: + status = "-1" if stage in stageFlow["required"] else "alternative" + upload_status[stage] = status + + cpt_qc_doc.setdefault("upload_status", upload_status) + + # Set the current stage to the next one (if not at the end) + currStage = cpt_qc_doc["stage"] + while True: + nextStageIndex = min( + len(stageFlow["all"]) - 1, + stageFlow["all"].index(currStage) + 1, + ) + currStage = stageFlow["all"][nextStageIndex] + if ( + currStage in stageFlow["required"] + or nextStageIndex == len(stageFlow["all"]) - 1 + ): + break + + cpt_qc_doc["stage"] = currStage + + logger.info( + f"Processing test runs for {cpt_doc['componentType']['code']} {cpt_doc['serialNumber']} (depth={depth})" + ) + return self.__processTestRuns(cpt_doc, cpt_localdb_doc, cpt_qc_doc) + def recursiveReplace(self, j, a, b, nest=0): # TODO: understand this function flag = False @@ -1149,24 +1328,32 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): if self.resetStage: logger.warning(f"==> Switching to stage {self.resetStage}") - this_cpt_localdb_doc, component_futures = self.__bookComponent(this_cpt_doc) + # Phase 1: Register parent component without test runs + this_cpt_localdb_doc, this_cpt_qc_doc = self.__bookComponentWithoutTestRuns( + this_cpt_doc + ) + + # Phase 2: Register all sub-components without test runs + logger.info( + "Phase 1-2: Registering all components without processing test runs" + ) + self.__loopSubComponentsWithoutTestRuns(this_cpt_doc, this_cpt_localdb_doc) + + # Phase 3: Process test runs depth-first (children before parents) + logger.info( + "Phase 3: Processing test runs depth-first (children before parents)" + ) + subcomponent_futures = self.__processTestRunsRecursively( + this_cpt_doc, this_cpt_localdb_doc + ) except Exception as e: logger.warning(str(e)) logger.warning("error in acquiring component {this_cpt_sn}") raise - # Loop over sub-components - # Note: you may be tempted to submit this in the loopSubComponents - # executor, but we need to have all children threads made first before - # waiting below - subcomponent_futures = self.__loopSubComponents( - this_cpt_doc, this_cpt_localdb_doc - ) - - futures = {**component_futures, **subcomponent_futures} - logger.info(f"Waiting for {len(futures)} futures to finish") - self.as_completed(futures) + logger.info(f"Waiting for {len(subcomponent_futures)} futures to finish") + self.as_completed(subcomponent_futures) self.shutdown_executors(wait=False) ###################################### -- GitLab From 78b69eb53b6cea785723f3c6d85d8b2bc5ff547d Mon Sep 17 00:00:00 2001 From: Giordon Stark Date: Tue, 19 Aug 2025 09:41:36 -0700 Subject: [PATCH 2/2] final fixes --- .../recursiveComponentsSynchronizer.py | 50 ++++--------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py b/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py index 80b2a877d..d018774a5 100755 --- a/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py +++ b/viewer/functions/itkpd_interface/recursiveComponentsSynchronizer.py @@ -705,6 +705,11 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): cpt_qc_doc["stage"] = currStage + # Store the QC status doc in the database + localdb.QC.module.status.replace_one( + {"component": cpt_doc["id"]}, cpt_qc_doc, upsert=True + ) + return cpt_localdb_doc, cpt_qc_doc def __bookComponent(self, cpt_doc): @@ -1083,46 +1088,13 @@ class RecursiveComponentsSynchronizer(PDInterface.PDInterface): self.as_completed(futures) futures = {} # Clear futures since we've waited for them - # Now process this component's test runs - cpt_qc_doc = self.__createComponentQCStatusDoc(cpt_doc) - - # Apply the same QC status logic from __bookComponentWithoutTestRuns - stageFlow = self.stageFlows[cpt_doc["componentType"]["code"]] - stage_history = {stage["code"] for stage in cpt_doc["stages"]} - - resetUpToStage = cpt_qc_doc["stage"] - if self.resetStage and self.resetStage in stageFlow["all"]: - resetUpToStageIndex = max(0, stageFlow["all"].index(self.resetStage) - 1) - resetUpToStage = stageFlow["all"][resetUpToStageIndex] - - upload_status = {} - for stage in stageFlow["all"]: - if ( - stageFlow["all"].index(stage) <= stageFlow["all"].index(resetUpToStage) - and stage in stage_history - ): - status = "1" - else: - status = "-1" if stage in stageFlow["required"] else "alternative" - upload_status[stage] = status - - cpt_qc_doc.setdefault("upload_status", upload_status) - - # Set the current stage to the next one (if not at the end) - currStage = cpt_qc_doc["stage"] - while True: - nextStageIndex = min( - len(stageFlow["all"]) - 1, - stageFlow["all"].index(currStage) + 1, + # Find the QC status doc that was already created and stored for this component + cpt_qc_doc = localdb.QC.module.status.find_one({"component": cpt_doc["id"]}) + if cpt_qc_doc is None: + logger.error( + f"QC status doc not found for component {cpt_doc['serialNumber']} (id={cpt_doc['id']})" ) - currStage = stageFlow["all"][nextStageIndex] - if ( - currStage in stageFlow["required"] - or nextStageIndex == len(stageFlow["all"]) - 1 - ): - break - - cpt_qc_doc["stage"] = currStage + return {} logger.info( f"Processing test runs for {cpt_doc['componentType']['code']} {cpt_doc['serialNumber']} (depth={depth})" -- GitLab