From 9efd3bdee354aa4b45f7b1088f201501ad1b6325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Thu, 23 Nov 2017 20:12:46 +0100 Subject: [PATCH 1/5] Port DependentServices (HistoryAwareReferenceField) to UIDReferenceField --- bika/lims/upgrade/v01_01_006.py | 54 ++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/bika/lims/upgrade/v01_01_006.py b/bika/lims/upgrade/v01_01_006.py index e3afb801c0..06717f56d2 100644 --- a/bika/lims/upgrade/v01_01_006.py +++ b/bika/lims/upgrade/v01_01_006.py @@ -28,8 +28,17 @@ def upgrade(tool): logger.info("Upgrading {0}: {1} -> {2}".format(product, ver_from, version)) + # Convert ReferenceField's values into UIDReferenceFields. UpgradeReferenceFields() + # Calculations not triggered in manage results view + # https://github.com/senaite/bika.lims/issues/355 + # Since we've already migrated the ReferenceField DependentServices from + # Calculation (with relation name 'CalculationAnalysisService' above, this + # wouldn't be strictly necessary, but who knows... maybe we've lost the + # at_references too, so just do it. + fix_broken_calculations() + # Indexes and colums were changed as per # https://github.com/senaite/bika.lims/pull/353 ut.delIndex(CATALOG_ANALYSIS_LISTING, 'getAnalysisRequestUID') @@ -44,6 +53,48 @@ def upgrade(tool): return True +def fix_broken_calculations(): + """Walks-through calculations associated to undergoing analyses and + resets the value for DependentServices field""" + + # Fetch only the subset of analyses that are undergoing. + # Analyses that have been verified or published cannot be updated, so there + # is no sense to check their calculations + review_states = [ + 'attachment_due', + 'not_requested', + 'rejected', + 'retracted', + 'sample_due', + 'sample_prep', + 'sample_received', + 'sample_received', + 'sample_registered', + 'sampled', + 'to_be_preserved', + 'to_be_sampled', + ] + catalog = get_tool(CATALOG_ANALYSIS_LISTING) + brains = catalog(portal_type='Analysis', review_state=review_states) + for brain in brains: + analysis = brain.getObject() + calculation = analysis.getCalculation() + if not calculation: + continue + + dependents = calculation.getDependentServices() + # We don't want eventualities such as [None,] + dependents = filter(None, dependents) + if not dependents: + # Assign the formula again to the calculation. Note the function + # setFormula inferes the dependent services (and stores them) by + # inspecting the keywords set in the formula itself. + # So, instead of doing this job here, we just let setFormula to work + # for us. + formula = calculation.getFormula() + calculation.setFormula(formula) + + refs_to_remove = [] objs_to_reindex = [] @@ -142,7 +193,8 @@ def UpgradeReferenceFields(): ]], ['Calculation', [ - ('DependentServices', 'CalculationDependentServices') + ('DependentServices', 'CalculationDependentServices'), + ('DependentServices', 'CalculationAnalysisService') ]], ['Instrument', [ From 039d05867d0c95117382184b096242eaa6026d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Thu, 23 Nov 2017 20:19:12 +0100 Subject: [PATCH 2/5] Changelog --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 36661bc572..9ab7ad2ab8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,7 @@ Changelog **Fixed** +- #403 Calculations not triggered in manage results view - #399 PR-2318 AR Add fails silently if e.g. the ID of the AR was already taken - #400 PR-2319 AR Add fails if an Analysis Category was disabled - #401 PR-2321 AR Add Copy of multiple ARs from different clients raises a Traceback in the background From 21a2ebc6d19cf771e1092cd056129397ba46e19d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 24 Nov 2017 13:01:33 +0100 Subject: [PATCH 3/5] Remove duplicate UIDs during assignment of value in HistoryAwareReferenceField --- bika/lims/browser/fields/historyawarereferencefield.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bika/lims/browser/fields/historyawarereferencefield.py b/bika/lims/browser/fields/historyawarereferencefield.py index 9c866b4d32..28340e4f07 100644 --- a/bika/lims/browser/fields/historyawarereferencefield.py +++ b/bika/lims/browser/fields/historyawarereferencefield.py @@ -72,6 +72,7 @@ def set(self, instance, value, **kwargs): add = [v for v in uids if v and v not in targetUIDs] newuids = [t for t in list(targetUIDs) + list(uids) if t not in sub] + newuids = list(set(newuids)) for uid in newuids: # update version_id of all existing references that aren't # about to be removed anyway (contents of sub) From 3336ae0abf8e378496889dcbabfc5878e3c7c953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 24 Nov 2017 13:02:14 +0100 Subject: [PATCH 4/5] Small redux of complexity in calculation --- bika/lims/content/calculation.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/bika/lims/content/calculation.py b/bika/lims/content/calculation.py index 728a09ade7..d6aca3890d 100644 --- a/bika/lims/content/calculation.py +++ b/bika/lims/content/calculation.py @@ -189,15 +189,11 @@ def setFormula(self, Formula=None): self.setDependentServices(None) self.getField('Formula').set(self, Formula) else: - DependentServices = [] keywords = re.compile(r"\[([^.^\]]+)\]").findall(Formula) - for keyword in keywords: - service = bsc(portal_type="AnalysisService", - getKeyword=keyword) - if service: - DependentServices.append(service[0].getObject()) - - self.getField('DependentServices').set(self, DependentServices) + brains = bsc(portal_type='AnalysisService', + getKeyword=keywords) + services = [brain.getObject() for brain in brains] + self.getField('DependentServices').set(self, services) self.getField('Formula').set(self, Formula) def getMinifiedFormula(self): From 9c8e21a9607d374ef30d0ae7805ac6600ed95311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20Puiggen=C3=A9?= Date: Fri, 24 Nov 2017 13:02:58 +0100 Subject: [PATCH 5/5] Take into account versioned calculations in fix_calculations (upgrade 1.1.6) --- bika/lims/upgrade/v01_01_006.py | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/bika/lims/upgrade/v01_01_006.py b/bika/lims/upgrade/v01_01_006.py index 06717f56d2..dbc5d44105 100644 --- a/bika/lims/upgrade/v01_01_006.py +++ b/bika/lims/upgrade/v01_01_006.py @@ -57,6 +57,8 @@ def fix_broken_calculations(): """Walks-through calculations associated to undergoing analyses and resets the value for DependentServices field""" + logger.info("Fixing broken calculations (re-assignment of dependents)...") + # Fetch only the subset of analyses that are undergoing. # Analyses that have been verified or published cannot be updated, so there # is no sense to check their calculations @@ -74,6 +76,7 @@ def fix_broken_calculations(): 'to_be_preserved', 'to_be_sampled', ] + uc = api.get_tool('uid_catalog') catalog = get_tool(CATALOG_ANALYSIS_LISTING) brains = catalog(portal_type='Analysis', review_state=review_states) for brain in brains: @@ -93,6 +96,47 @@ def fix_broken_calculations(): # for us. formula = calculation.getFormula() calculation.setFormula(formula) + deps = calculation.getDependentServices() + if not deps: + # Ok, this calculation does not depend on the result of other + # analyses, so we can omit this one, he is already ok + continue + + deps = [dep.getKeyword() for dep in deps] + deps = ', '.join(deps) + arid = analysis.getRequestID() + logger.info("Dependents for {}.{}.{}: {}".format(arid, + analysis.getKeyword(), + calculation.Title(), + deps)) + + # Set the calculation to the analysis again (field Calculation is an + # HistoryAwareReferenceField in Analyses that inherits from + # AbstractRoutineAnalysis + analysis.setCalculation(calculation) + + # Check if all is ok + an_deps = analysis.getCalculation().getDependentServices() + if not an_deps: + # Maybe the version of the calculation is an old one. If so, we + # need to use the last version, cause HistoryAwareReferenceField + # will always point to the version assigned to the calculation + # that was associated to the analysis. + uid = calculation.UID() + target_version = analysis.reference_versions[uid] + last_calc = uc(UID=uid) + if not last_calc: + # This should not happen + logger.warn("No calculation found for %s " % calculation.UID()) + continue + last_calc = last_calc[0].getObject() + if last_calc.version_id != target_version: + # Ok, this is another version. We have no choice here... we + # need to assign the latest version... + analysis.reference_versions[uid]=last_calc.version_id + + # Just in case + analysis.reindexObject() refs_to_remove = []