Passed
Push — master ( 3d9c1d...19c3e0 )
by Jordi
03:55
created

bika.lims.content.batch.Batch.getProgress()   A

Complexity

Conditions 3

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 3
nop 1
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2020 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
from AccessControl import ClassSecurityInfo
22
from bika.lims import api
23
from bika.lims import bikaMessageFactory as _
24
from bika.lims import deprecated
25
from bika.lims.browser.fields.remarksfield import RemarksField
26
from bika.lims.browser.widgets import DateTimeWidget
27
from bika.lims.browser.widgets import RecordsWidget as bikaRecordsWidget
28
from bika.lims.browser.widgets import ReferenceWidget
29
from bika.lims.browser.widgets import RemarksWidget
30
from bika.lims.catalog import CATALOG_ANALYSIS_REQUEST_LISTING
31
from bika.lims.config import PROJECTNAME
32
from bika.lims.content.bikaschema import BikaFolderSchema
33
from bika.lims.content.clientawaremixin import ClientAwareMixin
34
from bika.lims.interfaces import IBatch
35
from bika.lims.interfaces import ICancellable
36
from bika.lims.interfaces import IClient
37
from plone.app.folder.folder import ATFolder
38
from plone.indexer import indexer
39
from Products.Archetypes.public import DateTimeField
40
from Products.Archetypes.public import DisplayList
41
from Products.Archetypes.public import LinesField
42
from Products.Archetypes.public import MultiSelectionWidget
43
from Products.Archetypes.public import ReferenceField
44
from Products.Archetypes.public import Schema
45
from Products.Archetypes.public import StringField
46
from Products.Archetypes.public import StringWidget
47
from Products.Archetypes.public import registerType
48
from Products.Archetypes.references import HoldingReference
49
from Products.ATExtensions.ateapi import RecordsField
50
from Products.CMFCore.utils import getToolByName
51
from Products.CMFPlone.utils import safe_unicode
52
from zope.interface import implements
53
54
55
@indexer(IBatch)
56
def BatchDate(instance):
57
    return instance.Schema().getField('BatchDate').get(instance)
58
59
60
schema = BikaFolderSchema.copy() + Schema((
61
62
    StringField(
63
        'BatchID',
64
        required=False,
65
        validators=('uniquefieldvalidator',),
66
        widget=StringWidget(
67
            # XXX This field can never hold a user value, because it is
68
            #     invisible (see custom getBatchID getter method)
69
            # => we should remove that field
70
            visible=False,
71
            label=_("Batch ID"),
72
        )
73
    ),
74
75
    ReferenceField(
76
        'Client',
77
        required=0,
78
        allowed_types=('Client',),
79
        relationship='BatchClient',
80
        widget=ReferenceWidget(
81
            label=_("Client"),
82
            size=30,
83
            visible=True,
84
            base_query={'review_state': 'active'},
85
            showOn=True,
86
            colModel=[
87
                {'columnName': 'UID', 'hidden': True},
88
                {'columnName': 'Title', 'width': '60', 'label': _('Title')},
89
                {'columnName': 'ClientID', 'width': '20', 'label': _('Client ID')}
90
            ],
91
        ),
92
    ),
93
94
    StringField(
95
        'ClientBatchID',
96
        required=0,
97
        widget=StringWidget(
98
            label=_("Client Batch ID")
99
        )
100
    ),
101
102
    DateTimeField(
103
        'BatchDate',
104
        required=False,
105
        widget=DateTimeWidget(
106
            label=_('Date'),
107
        ),
108
    ),
109
110
    LinesField(
111
        'BatchLabels',
112
        vocabulary="BatchLabelVocabulary",
113
        accessor="getLabelNames",
114
        widget=MultiSelectionWidget(
115
            label=_("Batch Labels"),
116
            format="checkbox",
117
        )
118
    ),
119
120
    RemarksField(
121
        'Remarks',
122
        widget=RemarksWidget(
123
            label=_('Remarks'),
124
        )
125
    ),
126
))
127
128
# Remove implicit `uniquefieldvalidator` coming from `BikaFolderSchema`
129
schema['title'].validators = ()
130
schema['title'].widget.description = _("If no value is entered, the Batch ID"
131
                                       " will be auto-generated.")
132
schema['title'].required = False
133
schema['title'].widget.visible = True
134
schema['title'].widget.description = _("If no Title value is entered,"
135
                                       " the Batch ID will be used.")
136
schema['description'].required = False
137
schema['description'].widget.visible = True
138
139
schema.moveField('ClientBatchID', before='description')
140
schema.moveField('BatchID', before='description')
141
schema.moveField('title', before='description')
142
schema.moveField('Client', after='title')
143
144
145
class Batch(ATFolder, ClientAwareMixin):
146
    """A Batch combines multiple ARs into a logical unit
147
    """
148
    implements(IBatch, ICancellable)
149
150
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
151
    displayContentsTab = False
152
    security = ClassSecurityInfo()
153
    _at_rename_after_creation = True
154
155
    def _renameAfterCreation(self, check_auto_id=False):
156
        from bika.lims.idserver import renameAfterCreation
157
        renameAfterCreation(self)
158
159
    def getClient(self):
160
        """Retrieves the Client the current Batch is assigned to
161
        """
162
        # We override here getClient from ClientAwareMixin because te schema's
163
        # field Client is only used to allow the user to assign the batch to a
164
        # client in edit form. The entered value is used in
165
        # ObjectModifiedEventHandler to move the batch to the Client's folder,
166
        # so the value stored in the Schema's is not used anymore
167
        # See https://github.com/senaite/senaite.core/pull/1450
168
        client = self.aq_parent
169
        if IClient.providedBy(client):
170
            return client
171
        return None
172
173
    def getContactTitle(self):
174
        return ""
175
176
    def getProfilesTitle(self):
177
        return ""
178
179
    def getAnalysisService(self):
180
        analyses = set()
181
        for ar in self.getAnalysisRequests():
182
            for an in ar.getAnalyses():
183
                analyses.add(an)
184
        value = []
185
        for analysis in analyses:
186
            val = analysis.Title
187
            if val not in value:
188
                value.append(val)
189
        return list(value)
190
191
    def getAnalysts(self):
192
        analyses = []
193
        for ar in self.getAnalysisRequests():
194
            analyses += list(ar.getAnalyses(full_objects=True))
195
        value = []
196
        for analysis in analyses:
197
            val = analysis.getAnalyst()
198
            if val not in value:
199
                value.append(val)
200
        return value
201
202
    security.declarePublic('getBatchID')
203
204
    @deprecated("Please use getId instead")
205
    def getBatchID(self):
206
        # NOTE This method is a custom getter of the invisible field "BatchID".
207
        #      Therefore, it is unlikely that it returns anything else than `getId`.
208
        if self.BatchID:
209
            return self.BatchID
210
        if self.checkCreationFlag():
211
            return self.BatchID
212
        return self.getId()
213
214
    def BatchLabelVocabulary(self):
215
        """Return all batch labels as a display list
216
        """
217
        bsc = getToolByName(self, 'bika_setup_catalog')
218
        ret = []
219
        for p in bsc(portal_type='BatchLabel',
220
                     is_active=True,
221
                     sort_on='sortable_title'):
222
            ret.append((p.UID, p.Title))
223
        return DisplayList(ret)
224
225
    def getAnalysisRequestsBrains(self, **kwargs):
226
        """Return all the Analysis Requests brains linked to the Batch
227
        kargs are passed directly to the catalog.
228
        """
229
        kwargs['getBatchUID'] = self.UID()
230
        catalog = getToolByName(self, CATALOG_ANALYSIS_REQUEST_LISTING)
231
        brains = catalog(kwargs)
232
        return brains
233
234
    def getAnalysisRequests(self, **kwargs):
235
        """Return all the Analysis Requests objects linked to the Batch kargs
236
        are passed directly to the catalog.
237
        """
238
        brains = self.getAnalysisRequestsBrains(**kwargs)
239
        return [b.getObject() for b in brains]
240
241
    def isOpen(self):
242
        """Returns true if the Batch is in 'open' state
243
        """
244
        return api.get_workflow_status_of(self) not in ["cancelled", "closed"]
245
246
    def getLabelNames(self):
247
        uc = getToolByName(self, 'uid_catalog')
248
        uids = [uid for uid in self.Schema().getField('BatchLabels').get(self)]
249
        labels = [label.getObject().title for label in uc(UID=uids)]
250
        return labels
251
252
    def getProgress(self):
253
        """Returns the progress in percent of all samples
254
        """
255
        total_progress = 0
256
        samples = self.getAnalysisRequests()
257
        total = len(samples)
258
        if total > 0:
259
            sample_progresses = map(lambda s: s.getProgress(), samples)
260
            total_progress = sum(sample_progresses) / total
261
        return total_progress
262
263
264
registerType(Batch, PROJECTNAME)
265