Passed
Push — master ( d539f4...a81f8a )
by Ramon
05:14 queued 14s
created

bika.lims.browser.workflow.analysis   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 19
eloc 92
dl 0
loc 148
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A WorkflowActionSubmitAdapter.get_interims_data() 0 15 5
F WorkflowActionSubmitAdapter.__call__() 0 112 14
1
import json
2
from collections import defaultdict
3
4
from DateTime import DateTime
5
from Products.CMFPlone.i18nl10n import ulocalized_time
6
from bika.lims import api
7
from bika.lims import bikaMessageFactory as _
8
from bika.lims import logger
9
from bika.lims.api.analysis import is_out_of_range
10
from bika.lims.browser.referenceanalysis import AnalysesRetractedListReport
11
from bika.lims.browser.workflow import WorkflowActionGenericAdapter
12
from bika.lims.catalog.analysis_catalog import CATALOG_ANALYSIS_LISTING
13
from bika.lims.interfaces import IReferenceAnalysis
14
15
16
class WorkflowActionSubmitAdapter(WorkflowActionGenericAdapter):
17
    """Adapter in charge of submission of analyses
18
    """
19
20
    def __call__(self, action, objects):
21
        # Store invalid instruments-ref.analyses
22
        invalid_instrument_refs = defaultdict(set)
23
24
        # Get interims data
25
        interims_data = self.get_interims_data()
26
27
        for analysis in objects:
28
            uid = api.get_uid(analysis)
29
30
            # Need to save remarks?
31
            remarks = self.get_form_value("Remarks", uid, default="")
32
            analysis.setRemarks(remarks)
33
34
            # Need to save the instrument?
35
            instrument = self.get_form_value("Instrument", uid, None)
36
            if instrument is not None:
37
                # Could be an empty string
38
                instrument = instrument or None
39
                analysis.setInstrument(instrument)
40
                if instrument and IReferenceAnalysis.providedBy(analysis):
41
                    if is_out_of_range(analysis):
42
                        # This reference analysis is out of range, so we have
43
                        # to retract all analyses assigned to this same
44
                        # instrument that are awaiting for verification
45
                        invalid_instrument_refs[uid].add(analysis)
46
                    else:
47
                        # The reference result is valid, so make the instrument
48
                        # available again for further analyses
49
                        instrument.setDisposeUntilNextCalibrationTest(False)
50
51
            # Need to save the method?
52
            method = self.get_form_value("Method", uid, default=None)
53
            if method is not None:
54
                method = method or None
55
                analysis.setMethod(method)
56
57
            # Need to save analyst?
58
            analyst = self.get_form_value("Analyst", uid, default=None)
59
            if analyst is not None:
60
                analysis.setAnalyst(analyst)
61
62
            # Save uncertainty
63
            uncertainty = self.get_form_value("Uncertainty", uid, "")
64
            analysis.setUncertainty(uncertainty)
65
66
            # Save detection limit
67
            dlimit = self.get_form_value("DetectionLimit", uid, "")
68
            analysis.setDetectionLimitOperand(dlimit)
69
70
            # Interim fields
71
            interims = interims_data.get(uid, analysis.getInterimFields())
72
            analysis.setInterimFields(interims)
73
74
            # Save Hidden
75
            hidden = self.get_form_value("Hidden", uid, "")
76
            analysis.setHidden(hidden == "on")
77
78
            # Result
79
            result = self.get_form_value("Result", uid,
80
                                         default=analysis.getResult())
81
            analysis.setResult(result)
82
83
        # Submit all analyses
84
        transitioned = self.do_action(action, objects)
85
        if not transitioned:
86
            return self.redirect(message=_("No changes made"), level="warning")
87
88
        # If a reference analysis with an out-of-range result and instrument
89
        # assigned has been submitted, retract then routine analyses that are
90
        # awaiting for verification and with same instrument associated
91
        retracted = list()
92
        for invalid_instrument_uid in invalid_instrument_refs.keys():
93
            query = dict(getInstrumentUID=invalid_instrument_uid,
94
                         portal_type=['Analysis', 'DuplicateAnalysis'],
95
                         review_state='to_be_verified',
96
                         cancellation_state='active', )
97
            brains = api.search(query, CATALOG_ANALYSIS_LISTING)
98
            for brain in brains:
99
                analysis = api.get_object(brain)
100
                failed_msg = '{0}: {1}'.format(
101
                    ulocalized_time(DateTime(), long_format=1),
102
                    _("Instrument failed reference test"))
103
                an_remarks = analysis.getRemarks()
104
                analysis.setRemarks('. '.join([an_remarks, failed_msg]))
105
                retracted.append(analysis)
106
107
        # If some analyses have been retracted because instrument failed a
108
        # reference test, then generate a pdf report
109
        if self.do_action("retract", retracted):
110
            # Create the Retracted Analyses List
111
            portal_url = api.get_url(api.get_portal())
112
            report = AnalysesRetractedListReport(self.context, self.request,
113
                                                 portal_url,
114
                                                 'Retracted analyses',
115
                                                 retracted)
116
117
            # Attach the pdf to all ReferenceAnalysis that failed (accessible
118
            # from Instrument's Internal Calibration Tests list
119
            pdf = report.toPdf()
120
            for ref in invalid_instrument_refs.values():
121
                ref.setRetractedAnalysesPdfReport(pdf)
122
123
            # Send the email
124
            try:
125
                report.sendEmail()
126
            except Exception as err_msg:
127
                message = "Unable to send email: {}".format(err_msg)
128
                logger.warn(message)
129
130
        # Redirect to success view
131
        return self.success(transitioned)
132
133
    def get_interims_data(self):
134
        """Returns a dictionary with the interims data
135
        """
136
        form = self.request.form
137
        if 'item_data' not in form:
138
            return {}
139
140
        item_data = {}
141
        if type(form['item_data']) == list:
142
            for i_d in form['item_data']:
143
                for i, d in json.loads(i_d).items():
144
                    item_data[i] = d
145
            return item_data
146
147
        return json.loads(form['item_data'])
148