def prepublish(obj): """Returns True if 'prepublish' transition can be applied to the Analysis Request passed in. Returns true if the Analysis Request is active (not in a cancelled/inactive state), the 'publish' transition cannot be performed yet, and at least one of its analysis is under to_be_verified state or has been already verified. As per default DC workflow definition in bika_ar_workflow, note that prepublish does not transitions the Analysis Request to any other state different from the actual one, neither its children. This 'fake' transition is only used for the prepublish action to be displayed when the Analysis Request' status is other than verified, so the labman can generate a provisional report, also if results are not yet definitive. :returns: true or false """ if not isBasicTransitionAllowed(obj): return False if isTransitionAllowed(obj, 'publish'): return False analyses = obj.getAnalyses(full_objects=True) for an in analyses: # If the analysis is not active, omit if not isActive(an): continue # Check if the current state is 'verified' status = getCurrentState(an) if status in ['verified', 'to_be_verified']: return True # This analysis request has no single result ready to be verified or # verified yet. In this situation, it doesn't make sense to publish a # provisional results reports without a single result to display return False
def prepublish(obj): """Returns True if 'prepublish' transition can be applied to the Analysis Request passed in. Returns true if the Analysis Request is active (not in a cancelled/inactive state), the 'publish' transition cannot be performed yet, and at least one of its analysis is under to_be_verified state or has been already verified. As per default DC workflow definition in bika_ar_workflow, note that prepublish does not transitions the Analysis Request to any other state different from the actual one, neither its children. This 'fake' transition is only used for the prepublish action to be displayed when the Analysis Request' status is other than verified, so the labman can generate a provisional report, also if results are not yet definitive. :returns: true or false """ if not isBasicTransitionAllowed(obj): return False if isTransitionAllowed(obj, 'publish'): return False analyses = obj.getAnalyses(full_objects=True) for an in analyses: # If the analysis is not active, omit if not isActive(an): continue # Check if the current state is 'verified' status = getCurrentState(an) if status in ['verified', 'to_be_verified']: return True # This analysis request has no single result ready to be verified or # verified yet. In this situation, it doesn't make sense to publish a # provisional results reports without a single result to display return False
def isItemAllowed(self, obj): """ Only valid reference samples (neither expired nor disposed) are allowed """ if not obj.isValid() or not isActive(obj): return False return super(ReferenceSamplesView, self).isItemAllowed(obj)
def isItemAllowed(self, obj): """ Only valid reference samples (neither expired nor disposed) are allowed """ if not obj.isValid() or not isActive(obj): return False return super(ReferenceSamplesView, self).isItemAllowed(obj)
def fix_service_status_inconsistences(): catalog = api.get_tool('bika_setup_catalog') brains = catalog(portal_type='AnalysisService') for brain in brains: obj = api.get_object(brain) if not isActive(obj): continue # If this service is active, then all the services this service # depends on must be active too, as well as the calculation calculation = obj.getCalculation() if not calculation: continue dependencies = calculation.getDependentServices() for dependency in dependencies: dependency = api.get_object(dependency) if not isActive(dependency): _change_inactive_state(dependency, 'active')
def fix_service_status_inconsistences(): catalog = api.get_tool('bika_setup_catalog') brains = catalog(portal_type='AnalysisService') for brain in brains: obj = api.get_object(brain) if not isActive(obj): continue # If this service is active, then all the services this service # depends on must be active too, as well as the calculation calculation = obj.getCalculation() if not calculation: continue dependencies = calculation.getDependentServices() for dependency in dependencies: dependency = api.get_object(dependency) if not isActive(dependency): _change_inactive_state(dependency, 'active')
def fix_service_profile_template_inconsistences(): catalog = api.get_tool('bika_setup_catalog') brains = catalog(portal_type='AnalysisService') for brain in brains: obj = api.get_object(brain) if isActive(obj): continue # If this service is inactive, be sure is not used neither in Profiles # nor in AR Templates obj.after_deactivate_transition_event()
def fix_service_profile_template_inconsistences(): catalog = api.get_tool('bika_setup_catalog') brains = catalog(portal_type='AnalysisService') for brain in brains: obj = api.get_object(brain) if isActive(obj): continue # If this service is inactive, be sure is not used neither in Profiles # nor in AR Templates obj.after_deactivate_transition_event()
def verify(obj): """Returns True if 'verify' transition can be applied to the Analysis Request passed in. This is, returns true if all the analyses that contains have already been verified. Those analyses that are in an inactive state (cancelled, inactive) are dismissed, but at least one analysis must be in an active state (and verified), otherwise always return False. If the Analysis Request is in inactive state (cancelled/inactive), returns False Note this guard depends entirely on the current status of the children :returns: true or false """ if not isBasicTransitionAllowed(obj): return False analyses = obj.getAnalyses(full_objects=True) invalid = 0 for an in analyses: # The analysis has already been verified? if wasTransitionPerformed(an, 'verify'): continue # Maybe the analysis is in an 'inactive' state? if not isActive(an): invalid += 1 continue # Maybe the analysis has been rejected or retracted? dettached = ['rejected', 'retracted', 'attachments_due'] status = getCurrentState(an) if status in dettached: invalid += 1 continue # At this point we can assume this analysis is an a valid state and # could potentially be verified, but the Analysis Request can only be # verified if all the analyses have been transitioned to verified return False # Be sure that at least there is one analysis in an active state, it # doesn't make sense to verify an Analysis Request if all the analyses that # contains are rejected or cancelled! return len(analyses) - invalid > 0
def verify(obj): """Returns True if 'verify' transition can be applied to the Analysis Request passed in. This is, returns true if all the analyses that contains have already been verified. Those analyses that are in an inactive state (cancelled, inactive) are dismissed, but at least one analysis must be in an active state (and verified), otherwise always return False. If the Analysis Request is in inactive state (cancelled/inactive), returns False Note this guard depends entirely on the current status of the children :returns: true or false """ if not isBasicTransitionAllowed(obj): return False analyses = obj.getAnalyses(full_objects=True) invalid = 0 for an in analyses: # The analysis has already been verified? if wasTransitionPerformed(an, 'verify'): continue # Maybe the analysis is in an 'inactive' state? if not isActive(an): invalid += 1 continue # Maybe the analysis has been rejected or retracted? dettached = ['rejected', 'retracted', 'attachments_due'] status = getCurrentState(an) if status in dettached: invalid += 1 continue # At this point we can assume this analysis is an a valid state and # could potentially be verified, but the Analysis Request can only be # verified if all the analyses have been transitioned to verified return False # Be sure that at least there is one analysis in an active state, it # doesn't make sense to verify an Analysis Request if all the analyses that # contains are rejected or cancelled! return len(analyses) - invalid > 0
def guard_submit(context): if not IAnalysisRequest.providedBy(context): # Note that this guard is only used for bika_ar_workflow! return True logger.info("*** Custom Guard: submit **") if not isBasicTransitionAllowed(context): return False invalid = 0 analyses = context.getAnalyses() for an in analyses: if an.review_state == 'to_be_verified': continue # The analysis has already been verified? an = api.get_object(an) if wasTransitionPerformed(an, 'submit'): continue # Maybe the analysis is in an 'inactive' state? if not isActive(an): invalid += 1 continue # Maybe the analysis has been rejected or retracted? dettached = ['rejected', 'retracted', 'attachments_due'] status = getCurrentState(an) if status in dettached: invalid += 1 continue # At this point we can assume this analysis is an a valid state and # the AR could potentially be submitted, but the Analysis Request can # only be submitted if all the analyses have been submitted already return False # Be sure that at least there is one analysis in an active state, it # doesn't make sense to submit an Analysis Request if all the analyses that # contains are rejected or cancelled! return len(analyses) - invalid > 0
def _children_are_ready(obj, transition_id, dettached_states=None): """Returns true if the children of the object passed in (worksheet) have been all transitioned in accordance with the 'transition_id' passed in. If dettached_states is provided, children with those states are dismissed, so they will not be taken into account in the evaluation. Nevertheless, at least one child with for which the transition_id performed is required for this function to return true (if all children are in dettached states, it always return False). """ analyses = obj.getAnalyses() invalid = 0 for an in analyses: # The analysis has already been transitioned? if wasTransitionPerformed(an, transition_id): continue # Maybe the analysis is in an 'inactive' state? if not isActive(an): invalid += 1 continue # Maybe the analysis is in a dettached state? if dettached_states: status = getCurrentState(an) if status in dettached_states: invalid += 1 continue # At this point we can assume this analysis is an a valid state and # could potentially be transitioned, but the Worksheet can only be # transitioned if all the analyses have been transitioned previously return False # Be sure that at least there is one analysis in an active state, it # doesn't make sense to transition a Worksheet if all the analyses that # contains are not valid return len(analyses) - invalid > 0
def __init__(self, context, request, **kwargs): BikaListingView.__init__( self, context, request, show_categories=context.bika_setup.getCategoriseAnalysisServices(), expand_all_categories=True) # prepare the content filter of this listing self.contentFilter = dict(kwargs) self.contentFilter.update({ "portal_type": "Analysis", "sort_on": "sortable_title", "sort_order": "ascending", }) # set the listing view config self.catalog = CATALOG_ANALYSIS_LISTING self.sort_order = 'ascending' self.context_actions = {} self.show_sort_column = False self.show_select_row = False self.show_select_column = False self.show_column_toggles = False self.pagesize = 999999 self.form_id = 'analyses_form' self.context_active = isActive(context) self.interim_fields = {} self.interim_columns = {} self.specs = {} self.bsc = getToolByName(context, 'bika_setup_catalog') self.portal = getToolByName(context, 'portal_url').getPortalObject() self.portal_url = self.portal.absolute_url() self.rc = getToolByName(context, REFERENCE_CATALOG) self.dmk = context.bika_setup.getResultsDecimalMark() self.scinot = context.bika_setup.getScientificNotationResults() self.categories = [] request.set('disable_plone.rightcolumn', 1) # each editable item needs it's own allow_edit # which is a list of field names. self.allow_edit = False self.columns = { # Although 'created' column is not displayed in the list (see # review_states to check the columns that will be rendered), this # column is needed to sort the list by create date 'created': { 'title': _('Date Created'), 'toggle': False}, 'Service': { 'title': _('Analysis'), 'attr': 'Title', 'index': 'sortable_title', 'sortable': False}, 'Partition': { 'title': _("Partition"), 'attr': 'getSamplePartitionID', 'sortable': False}, 'Method': { 'title': _('Method'), 'sortable': False, 'toggle': True}, 'Instrument': { 'title': _('Instrument'), 'sortable': False, 'toggle': True}, 'Analyst': { 'title': _('Analyst'), 'sortable': False, 'toggle': True}, 'state_title': { 'title': _('Status'), 'sortable': False}, 'DetectionLimit': { 'title': _('DL'), 'sortable': False, 'toggle': False}, 'Result': { 'title': _('Result'), 'input_width': '6', 'input_class': 'ajax_calculate numeric', 'sortable': False}, 'Specification': { 'title': _('Specification'), 'sortable': False}, 'Uncertainty': { 'title': _('+-'), 'sortable': False}, 'retested': { 'title': "<img title='%s' " "src='%s/++resource++bika.lims.images/retested.png" "'/>" % \ (t(_('Retested')), self.portal_url), 'type': 'boolean', 'sortable': False}, 'Attachments': { 'title': _('Attachments'), 'sortable': False}, 'CaptureDate': { 'title': _('Captured'), 'index': 'getResultCaptureDate', 'sortable': False}, 'DueDate': { 'title': _('Due Date'), 'index': 'getDueDate', 'sortable': False}, 'Hidden': { 'title': _('Hidden'), 'toggle': True, 'sortable': False, 'input_class': 'autosave', 'type': 'boolean'}, } self.review_states = [ { 'id': 'default', 'title': _('All'), 'contentFilter': {}, 'columns': [ 'Service', 'Partition', 'DetectionLimit', 'Result', 'Specification', 'Method', 'Instrument', 'Analyst', 'Uncertainty', 'CaptureDate', 'DueDate', 'state_title', 'Hidden' ] }, ] if not context.bika_setup.getShowPartitions(): self.review_states[0]['columns'].remove('Partition') # This is used to cache analysis keywords with Point of Capture to # reduce the number objects that need to be woken up # Is managed by `is_analysis_edition_allowed` function self._keywords_poc_map = dict() # This is used to display method and instrument columns if there is at # least one analysis to be rendered that allows the assignment of method # and/or instrument self.show_methodinstr_columns = False
def __init__(self, context, request, **kwargs): super(AnalysesView, self).__init__(context, request, **kwargs) # prepare the content filter of this listing self.contentFilter = dict(kwargs) self.contentFilter.update({ "portal_type": "Analysis", "sort_on": "sortable_title", "sort_order": "ascending", }) # set the listing view config self.catalog = CATALOG_ANALYSIS_LISTING self.sort_order = "ascending" self.context_actions = {} self.show_select_row = False self.show_select_column = False self.show_column_toggles = False self.pagesize = 9999999 self.form_id = "analyses_form" self.context_active = isActive(context) self.interim_fields = {} self.interim_columns = OrderedDict() self.specs = {} self.bsc = api.get_tool("bika_setup_catalog") self.portal = api.get_portal() self.portal_url = api.get_url(self.portal) self.rc = api.get_tool(REFERENCE_CATALOG) self.dmk = context.bika_setup.getResultsDecimalMark() self.scinot = context.bika_setup.getScientificNotationResults() self.categories = [] # each editable item needs it's own allow_edit # which is a list of field names. self.allow_edit = False self.columns = OrderedDict(( # Although 'created' column is not displayed in the list (see # review_states to check the columns that will be rendered), this # column is needed to sort the list by create date ("created", { "title": _("Date Created"), "toggle": False}), ("Service", { "title": _("Analysis"), "attr": "Title", "index": "sortable_title", "sortable": False}), ("Partition", { "title": _("Partition"), "attr": "getSamplePartitionID", "sortable": False}), ("Method", { "title": _("Method"), "sortable": False, "ajax": True, "toggle": True}), ("Instrument", { "title": _("Instrument"), "ajax": True, "sortable": False, "toggle": True}), ("Analyst", { "title": _("Analyst"), "sortable": False, "ajax": True, "toggle": True}), ("state_title", { "title": _("Status"), "sortable": False}), ("DetectionLimit", { "title": _("DL"), "sortable": False, "toggle": False}), ("Result", { "title": _("Result"), "input_width": "6", "input_class": "ajax_calculate numeric", "ajax": True, "sortable": False}), ("Specification", { "title": _("Specification"), "sortable": False}), ("Uncertainty", { "title": _("+-"), "sortable": False}), ("retested", { "title": _("Retested"), "type": "boolean", "sortable": False}), ("Attachments", { "title": _("Attachments"), "sortable": False}), ("CaptureDate", { "title": _("Captured"), "index": "getResultCaptureDate", "sortable": False}), ("DueDate", { "title": _("Due Date"), "index": "getDueDate", "sortable": False}), ("Hidden", { "title": _("Hidden"), "toggle": True, "sortable": False, "ajax": True, "type": "boolean"}), )) # Inject Remarks column for listing if self.analysis_remarks_enabled(): self.columns["Remarks"] = { "title": "Remarks", "toggle": False, "sortable": False, "type": "remarks", "ajax": True, } self.review_states = [ { "id": "default", "title": _("All"), "contentFilter": {}, "columns": self.columns.keys() }, ] # This is used to display method and instrument columns if there is at # least one analysis to be rendered that allows the assignment of # method and/or instrument self.show_methodinstr_columns = False