Example #1
0
 def workflow_script_cancel(self):
     if skip(self, "cancel"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=["cancellation_state", ])
     sample_c_state = workflow.getInfoFor(sample, 'cancellation_state')
     # if all sibling partitions are cancelled, cancel sample
     if not skip(sample, "cancel", peek=True):
         active = [sp for sp in sample.objectValues("SamplePartition")
                   if workflow.getInfoFor(sp, 'cancellation_state') == 'active']
         if sample_c_state == 'active' and not active:
             workflow.doActionFor(sample, 'cancel')
Example #2
0
 def workflow_script_sample_due(self):
     if skip(self, "sample_due"):
         return
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "sample_due")
         ar.reindexObject()
Example #3
0
 def workflow_script_sample_due(self):
     if skip(self, "sample_due"):
         return
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "sample_due")
         ar.reindexObject()
Example #4
0
 def workflow_script_cancel(self):
     if skip(self, "cancel"):
         return
     workflow = getToolByName(self, "portal_workflow")
     parts = self.objectValues("SamplePartition")
     self.reindexObject(idxs=["cancellation_state"])
     # Cancel all partitions
     for sp in [sp for sp in parts if workflow.getInfoFor(sp, "cancellation_state") == "active"]:
         workflow.doActionFor(sp, "cancel")
     # cancel all ARs for this self.
     ars = self.getAnalysisRequests()
     for ar in ars:
         if not skip(ar, "cancel", peek=True):
             ar_state = workflow.getInfoFor(ar, "cancellation_state")
             if ar_state == "active":
                 workflow.doActionFor(ar, "cancel")
Example #5
0
 def workflow_script_cancel(self):
     if skip(self, "cancel"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=[
         "cancellation_state",
     ])
     sample_c_state = workflow.getInfoFor(sample, 'cancellation_state')
     # if all sibling partitions are cancelled, cancel sample
     if not skip(sample, "cancel", peek=True):
         active = [
             sp for sp in sample.objectValues("SamplePartition")
             if workflow.getInfoFor(sp, 'cancellation_state') == 'active'
         ]
         if sample_c_state == 'active' and not active:
             workflow.doActionFor(sample, 'cancel')
Example #6
0
 def workflow_script_attach(self):
     if skip(self, "attach"):
         return
     self.reindexObject(idxs=[
         "review_state",
     ])
     # Don't cascade. Shouldn't be attaching WSs for now (if ever).
     return
Example #7
0
 def workflow_script_cancel(self):
     if skip(self, "cancel"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     parts = self.objectValues('SamplePartition')
     self.reindexObject(idxs=["cancellation_state", ])
     # Cancel all partitions
     for sp in [sp for sp in parts
                if workflow.getInfoFor(sp, 'cancellation_state') == 'active']:
         workflow.doActionFor(sp, 'cancel')
     # cancel all ARs for this self.
     ars = self.getAnalysisRequests()
     for ar in ars:
         if not skip(ar, "cancel", peek=True):
             ar_state = workflow.getInfoFor(ar, 'cancellation_state')
             if ar_state == 'active':
                 workflow.doActionFor(ar, 'cancel')
Example #8
0
 def workflow_script_receive(self):
     if skip(self, "receive"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     sample_state = workflow.getInfoFor(sample, 'review_state')
     self.setDateReceived(DateTime())
     self.reindexObject(idxs=["getDateReceived", ])
     # Transition our analyses
     analyses = self.getBackReferences('AnalysisSamplePartition')
     for analysis in analyses:
         doActionFor(analysis, "receive")
     # if all sibling partitions are received, promote sample
     if not skip(sample, "receive", peek=True):
         due = [sp for sp in sample.objectValues("SamplePartition")
                if workflow.getInfoFor(sp, 'review_state') == 'sample_due']
         if sample_state == 'sample_due' and not due:
             doActionFor(sample, 'receive')
Example #9
0
 def workflow_script_to_be_preserved(self):
     if skip(self, "to_be_preserved"):
         return
     workflow = getToolByName(self, "portal_workflow")
     parts = self.objectValues("SamplePartition")
     # Transition our children
     tbs = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "to_be_preserved"]
     for sp in tbs:
         doActionFor(sp, "to_be_preserved")
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "to_be_preserved")
         ar.reindexObject()
Example #10
0
 def workflow_script_receive(self):
     if skip(self, "receive"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     sample_state = workflow.getInfoFor(sample, 'review_state')
     self.setDateReceived(DateTime())
     self.reindexObject(idxs=[
         "getDateReceived",
     ])
     # Transition our analyses
     analyses = self.getBackReferences('AnalysisSamplePartition')
     for analysis in analyses:
         doActionFor(analysis, "receive")
     # if all sibling partitions are received, promote sample
     if not skip(sample, "receive", peek=True):
         due = [
             sp for sp in sample.objectValues("SamplePartition")
             if workflow.getInfoFor(sp, 'review_state') == 'sample_due'
         ]
         if sample_state == 'sample_due' and not due:
             doActionFor(sample, 'receive')
Example #11
0
 def workflow_script_verify(self):
     if skip(self, "verify"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=["review_state", ])
     if not "verify all analyses" in self.REQUEST['workflow_skiplist']:
         # verify all analyses in this self.
         analyses = self.getAnalyses()
         for analysis in analyses:
             state = workflow.getInfoFor(analysis, 'review_state', '')
             if state != 'to_be_verified':
                 continue
             doActionFor(analysis, "verify")
Example #12
0
 def workflow_script_to_be_preserved(self):
     if skip(self, "to_be_preserved"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     parts = self.objectValues('SamplePartition')
     # Transition our children
     tbs = [sp for sp in parts
            if workflow.getInfoFor(sp, 'review_state') == 'to_be_preserved']
     for sp in tbs:
         doActionFor(sp, "to_be_preserved")
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "to_be_preserved")
         ar.reindexObject()
Example #13
0
 def workflow_script_retract(self):
     if skip(self, "retract"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=["review_state", ])
     if not "retract all analyses" in self.REQUEST['workflow_skiplist']:
         # retract all analyses in this self.
         # (NB: don't retract if it's verified)
         analyses = self.getAnalyses()
         for analysis in analyses:
             state = workflow.getInfoFor(analysis, 'review_state', '')
             if state not in ('attachment_due', 'to_be_verified',):
                 continue
             doActionFor(analysis, 'retract')
Example #14
0
 def workflow_script_sample(self):
     if skip(self, "sample"):
         return
     workflow = getToolByName(self, "portal_workflow")
     parts = self.objectValues("SamplePartition")
     # This action can happen in the Sample UI.  So we transition all
     # partitions that are still 'to_be_sampled'
     tbs = [sp for sp in parts if workflow.getInfoFor(sp, "review_state") == "to_be_sampled"]
     for sp in tbs:
         doActionFor(sp, "sample")
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "sample")
         ar.reindexObject()
Example #15
0
 def workflow_script_sample(self):
     if skip(self, "sample"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     parts = self.objectValues('SamplePartition')
     # This action can happen in the Sample UI.  So we transition all
     # partitions that are still 'to_be_sampled'
     tbs = [sp for sp in parts
            if workflow.getInfoFor(sp, 'review_state') == 'to_be_sampled']
     for sp in tbs:
         doActionFor(sp, "sample")
     # All associated AnalysisRequests are also transitioned
     for ar in self.getAnalysisRequests():
         doActionFor(ar, "sample")
         ar.reindexObject()
Example #16
0
 def workflow_script_verify(self):
     if skip(self, "verify"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=[
         "review_state",
     ])
     if not "verify all analyses" in self.REQUEST['workflow_skiplist']:
         # verify all analyses in this self.
         analyses = self.getAnalyses()
         for analysis in analyses:
             state = workflow.getInfoFor(analysis, 'review_state', '')
             if state != 'to_be_verified':
                 continue
             doActionFor(analysis, "verify")
Example #17
0
 def workflow_script_retract(self):
     if skip(self, "retract"):
         return
     workflow = getToolByName(self, 'portal_workflow')
     self.reindexObject(idxs=[
         "review_state",
     ])
     if not "retract all analyses" in self.REQUEST['workflow_skiplist']:
         # retract all analyses in this self.
         # (NB: don't retract if it's verified)
         analyses = self.getAnalyses()
         for analysis in analyses:
             state = workflow.getInfoFor(analysis, 'review_state', '')
             if state not in (
                     'attachment_due',
                     'to_be_verified',
             ):
                 continue
             doActionFor(analysis, 'retract')
Example #18
0
 def workflow_script_to_be_preserved(self):
     if skip(self, "to_be_preserved"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     # Transition our analyses
     analyses = self.getBackReferences('AnalysisSamplePartition')
     for analysis in analyses:
         doActionFor(analysis, "to_be_preserved")
     # if all our siblings are now up to date, promote sample and ARs.
     parts = sample.objectValues("SamplePartition")
     if parts:
         lower_states = ['to_be_sampled', 'to_be_preserved', ]
         escalate = True
         for part in parts:
             if workflow.getInfoFor(part, 'review_state') in lower_states:
                 escalate = False
         if escalate:
             doActionFor(sample, "to_be_preserved")
             for ar in sample.getAnalysisRequests():
                 doActionFor(ar, "to_be_preserved")
Example #19
0
 def workflow_script_sample_due(self):
     if skip(self, "sample_due"):
         return
     sample = self.aq_parent
     workflow = getToolByName(self, 'portal_workflow')
     # Transition our analyses
     analyses = self.getBackReferences('AnalysisSamplePartition')
     for analysis in analyses:
         doActionFor(analysis, "sample_due")
     # if all our siblings are now up to date, promote sample and ARs.
     parts = sample.objectValues("SamplePartition")
     if parts:
         lower_states = [
             'to_be_preserved',
         ]
         escalate = True
         for part in parts:
             pstate = workflow.getInfoFor(part, 'review_state')
             if pstate in lower_states:
                 escalate = False
         if escalate:
             doActionFor(sample, "sample_due")
             for ar in sample.getAnalysisRequests():
                 doActionFor(ar, "sample_due")
Example #20
0
    def workflow_script_reject(self):
        """Copy real analyses to RejectAnalysis, with link to real
           create a new worksheet, with the original analyses, and new
           duplicates and references to match the rejected
           worksheet.
        """
        if skip(self, "reject"):
            return
        utils = getToolByName(self, 'plone_utils')
        workflow = self.portal_workflow

        def copy_src_fields_to_dst(src, dst):
            # These will be ignored when copying field values between analyses
            ignore_fields = [
                'UID',
                'id',
                'title',
                'allowDiscussion',
                'subject',
                'description',
                'location',
                'contributors',
                'creators',
                'effectiveDate',
                'expirationDate',
                'language',
                'rights',
                'creation_date',
                'modification_date',
                'Layout',  # ws
                'Analyses',  # ws
            ]
            fields = src.Schema().fields()
            for field in fields:
                fieldname = field.getName()
                if fieldname in ignore_fields:
                    continue
                getter = getattr(
                    src, 'get' + fieldname,
                    src.Schema().getField(fieldname).getAccessor(src))
                setter = getattr(
                    dst, 'set' + fieldname,
                    dst.Schema().getField(fieldname).getMutator(dst))
                if getter is None or setter is None:
                    # ComputedField
                    continue
                setter(getter())

        analysis_positions = {}
        for item in self.getLayout():
            analysis_positions[item['analysis_uid']] = item['position']
        old_layout = []
        new_layout = []

        # New worksheet
        worksheets = self.aq_parent
        new_ws = _createObjectByType('Worksheet', worksheets, tmpID())
        new_ws.unmarkCreationFlag()
        new_ws_id = renameAfterCreation(new_ws)
        copy_src_fields_to_dst(self, new_ws)
        new_ws.edit(Number=new_ws_id, Remarks=self.getRemarks())

        # Objects are being created inside other contexts, but we want their
        # workflow handlers to be aware of which worksheet this is occurring in.
        # We save the worksheet in request['context_uid'].
        # We reset it again below....  be very sure that this is set to the
        # UID of the containing worksheet before invoking any transitions on
        # analyses.
        self.REQUEST['context_uid'] = new_ws.UID()

        # loop all analyses
        analyses = self.getAnalyses()
        new_ws_analyses = []
        old_ws_analyses = []
        for analysis in analyses:
            # Skip published or verified analyses
            review_state = workflow.getInfoFor(analysis, 'review_state', '')
            if review_state in ['published', 'verified', 'retracted']:
                old_ws_analyses.append(analysis.UID())
                old_layout.append({
                    'position': position,
                    'type': 'a',
                    'analysis_uid': analysis.UID(),
                    'container_uid': analysis.aq_parent.UID()
                })
                continue
            # Normal analyses:
            # - Create matching RejectAnalysis inside old WS
            # - Link analysis to new WS in same position
            # - Copy all field values
            # - Clear analysis result, and set Retested flag
            if analysis.portal_type == 'Analysis':
                reject = _createObjectByType('RejectAnalysis', self, tmpID())
                reject.unmarkCreationFlag()
                reject_id = renameAfterCreation(reject)
                copy_src_fields_to_dst(analysis, reject)
                reject.setAnalysis(analysis)
                reject.reindexObject()
                analysis.edit(
                    Result=None,
                    Retested=True,
                )
                analysis.reindexObject()
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(reject.UID())
                old_layout.append({
                    'position': position,
                    'type': 'r',
                    'analysis_uid': reject.UID(),
                    'container_uid': self.UID()
                })
                new_ws_analyses.append(analysis.UID())
                new_layout.append({
                    'position': position,
                    'type': 'a',
                    'analysis_uid': analysis.UID(),
                    'container_uid': analysis.aq_parent.UID()
                })
            # Reference analyses
            # - Create a new reference analysis in the new worksheet
            # - Transition the original analysis to 'rejected' state
            if analysis.portal_type == 'ReferenceAnalysis':
                service_uid = analysis.getService().UID()
                reference = analysis.aq_parent
                reference_type = analysis.getReferenceType()
                new_analysis_uid = reference.addReferenceAnalysis(
                    service_uid, reference_type)
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(analysis.UID())
                old_layout.append({
                    'position': position,
                    'type': reference_type,
                    'analysis_uid': analysis.UID(),
                    'container_uid': reference.UID()
                })
                new_ws_analyses.append(new_analysis_uid)
                new_layout.append({
                    'position': position,
                    'type': reference_type,
                    'analysis_uid': new_analysis_uid,
                    'container_uid': reference.UID()
                })
                workflow.doActionFor(analysis, 'reject')
                new_reference = reference.uid_catalog(
                    UID=new_analysis_uid)[0].getObject()
                workflow.doActionFor(new_reference, 'assign')
                analysis.reindexObject()
            # Duplicate analyses
            # - Create a new duplicate inside the new worksheet
            # - Transition the original analysis to 'rejected' state
            if analysis.portal_type == 'DuplicateAnalysis':
                src_analysis = analysis.getAnalysis()
                ar = src_analysis.aq_parent
                service = src_analysis.getService()
                duplicate_id = new_ws.generateUniqueId('DuplicateAnalysis')
                new_duplicate = _createObjectByType('DuplicateAnalysis',
                                                    new_ws, duplicate_id)
                new_duplicate.unmarkCreationFlag()
                copy_src_fields_to_dst(analysis, new_duplicate)
                workflow.doActionFor(new_duplicate, 'assign')
                new_duplicate.reindexObject()
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(analysis.UID())
                old_layout.append({
                    'position': position,
                    'type': 'd',
                    'analysis_uid': analysis.UID(),
                    'container_uid': self.UID()
                })
                new_ws_analyses.append(new_duplicate.UID())
                new_layout.append({
                    'position': position,
                    'type': 'd',
                    'analysis_uid': new_duplicate.UID(),
                    'container_uid': new_ws.UID()
                })
                workflow.doActionFor(analysis, 'reject')
                analysis.reindexObject()

        new_ws.setAnalyses(new_ws_analyses)
        new_ws.setLayout(new_layout)
        new_ws.replaces_rejected_worksheet = self.UID()
        for analysis in new_ws.getAnalyses():
            review_state = workflow.getInfoFor(analysis, 'review_state', '')
            if review_state == 'to_be_verified':
                changeWorkflowState(analysis, "bika_analysis_workflow",
                                    "sample_received")
        self.REQUEST['context_uid'] = self.UID()
        self.setLayout(old_layout)
        self.setAnalyses(old_ws_analyses)
        self.replaced_by = new_ws.UID()
Example #21
0
 def workflow_script_attach(self):
     if skip(self, "attach"):
         return
     self.reindexObject(idxs=["review_state", ])
     # Don't cascade. Shouldn't be attaching WSs for now (if ever).
     return
Example #22
0
    def workflow_script_reject(self):
        """Copy real analyses to RejectAnalysis, with link to real
           create a new worksheet, with the original analyses, and new
           duplicates and references to match the rejected
           worksheet.
        """
        if skip(self, "reject"):
            return
        utils = getToolByName(self, 'plone_utils')
        workflow = self.portal_workflow

        def copy_src_fields_to_dst(src, dst):
            # These will be ignored when copying field values between analyses
            ignore_fields = ['UID',
                             'id',
                             'title',
                             'allowDiscussion',
                             'subject',
                             'description',
                             'location',
                             'contributors',
                             'creators',
                             'effectiveDate',
                             'expirationDate',
                             'language',
                             'rights',
                             'creation_date',
                             'modification_date',
                             'Layout',    # ws
                             'Analyses',  # ws
            ]
            fields = src.Schema().fields()
            for field in fields:
                fieldname = field.getName()
                if fieldname in ignore_fields:
                    continue
                getter = getattr(src, 'get'+fieldname,
                                 src.Schema().getField(fieldname).getAccessor(src))
                setter = getattr(dst, 'set'+fieldname,
                                 dst.Schema().getField(fieldname).getMutator(dst))
                if getter is None or setter is None:
                    # ComputedField
                    continue
                setter(getter())

        analysis_positions = {}
        for item in self.getLayout():
            analysis_positions[item['analysis_uid']] = item['position']
        old_layout = []
        new_layout = []

        # New worksheet
        worksheets = self.aq_parent
        new_ws = _createObjectByType('Worksheet', worksheets, tmpID())
        new_ws.unmarkCreationFlag()
        new_ws_id = renameAfterCreation(new_ws)
        copy_src_fields_to_dst(self, new_ws)
        new_ws.edit(
            Number = new_ws_id,
            Remarks = self.getRemarks()
        )

        # Objects are being created inside other contexts, but we want their
        # workflow handlers to be aware of which worksheet this is occurring in.
        # We save the worksheet in request['context_uid'].
        # We reset it again below....  be very sure that this is set to the
        # UID of the containing worksheet before invoking any transitions on
        # analyses.
        self.REQUEST['context_uid'] = new_ws.UID()

        # loop all analyses
        analyses = self.getAnalyses()
        new_ws_analyses = []
        old_ws_analyses = []
        for analysis in analyses:
            # Skip published or verified analyses
            review_state = workflow.getInfoFor(analysis, 'review_state', '')
            if review_state in ['published', 'verified', 'retracted']:
                old_ws_analyses.append(analysis.UID())
                old_layout.append({'position': position,
                                   'type':'a',
                                   'analysis_uid':analysis.UID(),
                                   'container_uid':analysis.aq_parent.UID()})
                continue
            # Normal analyses:
            # - Create matching RejectAnalysis inside old WS
            # - Link analysis to new WS in same position
            # - Copy all field values
            # - Clear analysis result, and set Retested flag
            if analysis.portal_type == 'Analysis':
                reject = _createObjectByType('RejectAnalysis', self, tmpID())
                reject.unmarkCreationFlag()
                reject_id = renameAfterCreation(reject)
                copy_src_fields_to_dst(analysis, reject)
                reject.setAnalysis(analysis)
                reject.reindexObject()
                analysis.edit(
                    Result = None,
                    Retested = True,
                )
                analysis.reindexObject()
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(reject.UID())
                old_layout.append({'position': position,
                                   'type':'r',
                                   'analysis_uid':reject.UID(),
                                   'container_uid':self.UID()})
                new_ws_analyses.append(analysis.UID())
                new_layout.append({'position': position,
                                   'type':'a',
                                   'analysis_uid':analysis.UID(),
                                   'container_uid':analysis.aq_parent.UID()})
            # Reference analyses
            # - Create a new reference analysis in the new worksheet
            # - Transition the original analysis to 'rejected' state
            if analysis.portal_type == 'ReferenceAnalysis':
                service_uid = analysis.getService().UID()
                reference = analysis.aq_parent
                reference_type = analysis.getReferenceType()
                new_analysis_uid = reference.addReferenceAnalysis(service_uid,
                                                                  reference_type)
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(analysis.UID())
                old_layout.append({'position': position,
                                   'type':reference_type,
                                   'analysis_uid':analysis.UID(),
                                   'container_uid':reference.UID()})
                new_ws_analyses.append(new_analysis_uid)
                new_layout.append({'position': position,
                                   'type':reference_type,
                                   'analysis_uid':new_analysis_uid,
                                   'container_uid':reference.UID()})
                workflow.doActionFor(analysis, 'reject')
                new_reference = reference.uid_catalog(UID=new_analysis_uid)[0].getObject()
                workflow.doActionFor(new_reference, 'assign')
                analysis.reindexObject()
            # Duplicate analyses
            # - Create a new duplicate inside the new worksheet
            # - Transition the original analysis to 'rejected' state
            if analysis.portal_type == 'DuplicateAnalysis':
                src_analysis = analysis.getAnalysis()
                ar = src_analysis.aq_parent
                service = src_analysis.getService()
                duplicate_id = new_ws.generateUniqueId('DuplicateAnalysis')
                new_duplicate = _createObjectByType('DuplicateAnalysis',
                                                    new_ws, duplicate_id)
                new_duplicate.unmarkCreationFlag()
                copy_src_fields_to_dst(analysis, new_duplicate)
                workflow.doActionFor(new_duplicate, 'assign')
                new_duplicate.reindexObject()
                position = analysis_positions[analysis.UID()]
                old_ws_analyses.append(analysis.UID())
                old_layout.append({'position': position,
                                   'type':'d',
                                   'analysis_uid':analysis.UID(),
                                   'container_uid':self.UID()})
                new_ws_analyses.append(new_duplicate.UID())
                new_layout.append({'position': position,
                                   'type':'d',
                                   'analysis_uid':new_duplicate.UID(),
                                   'container_uid':new_ws.UID()})
                workflow.doActionFor(analysis, 'reject')
                analysis.reindexObject()

        new_ws.setAnalyses(new_ws_analyses)
        new_ws.setLayout(new_layout)
        new_ws.replaces_rejected_worksheet = self.UID()
        for analysis in new_ws.getAnalyses():
            review_state = workflow.getInfoFor(analysis, 'review_state', '')
            if review_state == 'to_be_verified':
                changeWorkflowState(analysis, "bika_analysis_workflow", "sample_received")
        self.REQUEST['context_uid'] = self.UID()
        self.setLayout(old_layout)
        self.setAnalyses(old_ws_analyses)
        self.replaced_by = new_ws.UID()