Completed
Branch master (9edffc)
by Jordi
04:36
created

bika.lims.content.sample.Sample.getAnalysts()   A

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 10

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 10
dl 10
loc 10
rs 9.9
c 0
b 0
f 0
cc 4
nop 1
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
8
"""Sample represents a physical sample submitted for testing
9
"""
10
11
from datetime import timedelta
12
from AccessControl import ClassSecurityInfo
13
from bika.lims import bikaMessageFactory as _
14
from bika.lims.api import get_object_by_uid
15
from bika.lims.browser.fields.remarksfield import RemarksField
16
from bika.lims.browser.fields.uidreferencefield import get_backreferences
17
from bika.lims.utils import t, getUsers
18
from Products.ATExtensions.field import RecordsField
19
from bika.lims.browser.widgets.datetimewidget import DateTimeWidget
20
from bika.lims.browser.widgets import RejectionWidget
21
from bika.lims.browser.widgets import RemarksWidget
22
from bika.lims.config import PROJECTNAME
23
from bika.lims.content.bikaschema import BikaSchema
24
from bika.lims.interfaces import ISample
25
from bika.lims.permissions import SampleSample
26
from bika.lims.permissions import ScheduleSampling
27
from Products.Archetypes import atapi
28
from Products.Archetypes.public import *
29
from Products.Archetypes.references import HoldingReference
30
from Products.ATContentTypes.lib.historyaware import HistoryAwareMixin
31
from Products.ATContentTypes.utils import DT2dt, dt2DT
32
from Products.CMFCore import permissions
33
from Products.CMFCore.utils import getToolByName
34
from Products.CMFPlone.utils import safe_unicode
35
from zope.interface import implements
36
37
from bika.lims.browser.fields import DateTimeField
38
from bika.lims.browser.widgets import ReferenceWidget
39
from bika.lims.browser.widgets import SelectionWidget as BikaSelectionWidget
40
41
import sys
42
from bika.lims.utils import to_unicode
43
44
schema = BikaSchema.copy() + Schema((
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable Schema does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'Schema'
Loading history...
45
    # TODO This field is only for v1.3.0 migration purposes
46
    # bika_catalog contains an "isValid" index. We will take advantage of this
47
    # index to keep track of the Samples that have been migrated already in
48
    # order to prevent an unnecessary reimport when v1.3.0 is rerun.
49
    # This field is used by `isValid` function
50
    BooleanField('Migrated',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanField does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanField'
Loading history...
51
        default = False,
52
    ),
53
    StringField('SampleID',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringField does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
54
        required=1,
55
        searchable=True,
56
        mode="rw",
57
        read_permission=permissions.View,
58
        write_permission=permissions.ModifyPortalContent,
59
        widget=StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable StringWidget does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'StringWidget'
Loading history...
60
            label=_("Sample ID"),
61
            description=_("The ID assigned to the client's sample by the lab"),
62
            visible=False,
63
            render_own_label=True,
64
        ),
65
    ),
66
    StringField('ClientReference',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
67
        mode="rw",
68
        read_permission=permissions.View,
69
        write_permission=permissions.ModifyPortalContent,
70
        widget=StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringWidget'
Loading history...
71
            label=_("Client Reference"),
72
            visible=False,
73
            render_own_label=True,
74
        ),
75
    ),
76
    StringField('ClientSampleID',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
77
        mode="rw",
78
        read_permission=permissions.View,
79
        write_permission=permissions.ModifyPortalContent,
80
        widget=StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringWidget'
Loading history...
81
            label=_("Client SID"),
82
            visible=False,
83
            render_own_label=True,
84
        ),
85
    ),
86
    ReferenceField('SampleType',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ReferenceField does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'ReferenceField'
Loading history...
87
        required=1,
88
        vocabulary_display_path_bound=sys.maxsize,
89
        allowed_types=('SampleType',),
90
        relationship='SampleSampleType',
91
        referenceClass=HoldingReference,
92
        mode="rw",
93
        read_permission=permissions.View,
94
        write_permission=permissions.ModifyPortalContent,
95
        widget=ReferenceWidget(
96
            label=_("Sample Type"),
97
            render_own_label=True,
98
            visible=False,
99
            catalog_name='bika_setup_catalog',
100
            base_query={'inactive_state': 'active'},
101
            showOn=True,
102
        ),
103
    ),
104
    ComputedField('SampleTypeTitle',
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedField does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
105
        expression="here.getSampleType() and here.getSampleType().Title() or ''",
106
        widget=ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ComputedWidget does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedWidget'
Loading history...
107
            visible=False,
108
        ),
109
    ),
110
    ReferenceField('SamplePoint',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ReferenceField'
Loading history...
111
        vocabulary_display_path_bound=sys.maxsize,
112
        allowed_types=('SamplePoint',),
113
        relationship = 'SampleSamplePoint',
114
        referenceClass = HoldingReference,
115
        mode="rw",
116
        read_permission=permissions.View,
117
        write_permission=permissions.ModifyPortalContent,
118
        widget=ReferenceWidget(
119
            label=_("Sample Point"),
120
            render_own_label=True,
121
            visible=False,
122
            catalog_name='bika_setup_catalog',
123
            base_query={'inactive_state': 'active'},
124
            showOn=True,
125
        ),
126
    ),
127
    ComputedField('SamplePointTitle',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
128
        expression = "here.getSamplePoint() and here.getSamplePoint().Title() or ''",
129
        widget = ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedWidget'
Loading history...
130
            visible=False,
131
        ),
132
    ),
133
    ReferenceField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ReferenceField'
Loading history...
134
        'StorageLocation',
135
        allowed_types='StorageLocation',
136
        relationship='AnalysisRequestStorageLocation',
137
        mode="rw",
138
        read_permission=permissions.View,
139
        write_permission=permissions.ModifyPortalContent,
140
        widget=ReferenceWidget(
141
            label=_("Storage Location"),
142
            description=_("Location where sample is kept"),
143
            size=20,
144
            render_own_label=True,
145
            visible=False,
146
            catalog_name='bika_setup_catalog',
147
            base_query={'inactive_state': 'active'},
148
            showOn=True,
149
        ),
150
    ),
151
    BooleanField('SamplingWorkflowEnabled',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanField'
Loading history...
152
                 default_method='getSamplingWorkflowEnabledDefault'
153
    ),
154
    DateTimeField('DateSampled',
155
        mode="rw",
156
        read_permission=permissions.View,
157
        write_permission=SampleSample,
158
        widget = DateTimeWidget(
159
            label=_("Date Sampled"),
160
            show_time=True,
161
            size=20,
162
            visible=False,
163
            render_own_label=True,
164
        ),
165
    ),
166
    StringField('Sampler',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
167
        mode="rw",
168
        read_permission=permissions.View,
169
        write_permission=SampleSample,
170
        vocabulary='getSamplers',
171
        widget=BikaSelectionWidget(
172
            format='select',
173
            label=_("Sampler"),
174
            visible=False,
175
            render_own_label=True,
176
        ),
177
    ),
178
    StringField('ScheduledSamplingSampler',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
179
        mode="rw",
180
        read_permission=permissions.View,
181
        write_permission=ScheduleSampling,
182
        vocabulary='getSamplers',
183
        widget=BikaSelectionWidget(
184
            description=_("Define the sampler supposed to do the sample in "
185
                          "the scheduled date"),
186
            format='select',
187
            label=_("Sampler for scheduled sampling"),
188
            visible=False,
189
            render_own_label=True,
190
        ),
191
    ),
192
    DateTimeField('SamplingDate',
193
        mode="rw",
194
        read_permission=permissions.View,
195
        write_permission=permissions.ModifyPortalContent,
196
        widget = DateTimeWidget(
197
            label=_("Expected Sampling Date"),
198
            description=_("Define when the sampler has to take the samples"),
199
            show_time=True,
200
            visible=False,
201
            render_own_label=True,
202
        ),
203
    ),
204
    ReferenceField('SamplingDeviation',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ReferenceField'
Loading history...
205
        vocabulary_display_path_bound = sys.maxsize,
206
        allowed_types = ('SamplingDeviation',),
207
        relationship = 'SampleSamplingDeviation',
208
        referenceClass = HoldingReference,
209
        mode="rw",
210
        read_permission=permissions.View,
211
        write_permission=permissions.ModifyPortalContent,
212
        widget=ReferenceWidget(
213
            label=_("Sampling Deviation"),
214
            render_own_label=True,
215
            visible=False,
216
            catalog_name='bika_setup_catalog',
217
            base_query={'inactive_state': 'active'},
218
            showOn=True,
219
        ),
220
    ),
221
    ReferenceField('SampleCondition',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ReferenceField'
Loading history...
222
        vocabulary_display_path_bound = sys.maxsize,
223
        allowed_types = ('SampleCondition',),
224
        relationship = 'SampleSampleCondition',
225
        referenceClass = HoldingReference,
226
        mode="rw",
227
        read_permission=permissions.View,
228
        write_permission=permissions.ModifyPortalContent,
229
        widget=ReferenceWidget(
230
            label=_("Sample Condition"),
231
            render_own_label=True,
232
            visible=False,
233
            catalog_name='bika_setup_catalog',
234
            base_query={'inactive_state': 'active'},
235
            showOn=True,
236
        ),
237
    ),
238
    StringField(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringField'
Loading history...
239
        'EnvironmentalConditions',
240
        mode="rw",
241
        read_permission=permissions.View,
242
        write_permission=permissions.ModifyPortalContent,
243
        widget=StringWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'StringWidget'
Loading history...
244
            label=_("Environmental Conditions"),
245
            visible=False,
246
            render_own_label=True,
247
            size=20,
248
        ),
249
    ),
250
    # Another way to obtain a transition date is using getTransitionDate
251
    # function. We are using a DateTimeField/Widget here because in some
252
    # cases the user may want to change the Received Date.
253
    # AnalysisRequest and Sample's DateReceived fields needn't to have
254
    # the same value.
255
    # This field is updated in workflow_script_receive method.
256
    DateTimeField('DateReceived',
257
        mode="rw",
258
        read_permission=permissions.View,
259
        write_permission=permissions.ModifyPortalContent,
260
        widget = DateTimeWidget(
261
            label=_("Date Received"),
262
            show_time=True,
263
            datepicker_nofuture=1,
264
            visible=False,
265
            render_own_label=True,
266
        ),
267
    ),
268
    ComputedField('ClientUID',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
269
        expression = 'context.aq_parent.UID()',
270
        widget = ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedWidget'
Loading history...
271
            visible=False,
272
        ),
273
    ),
274
    ComputedField('SampleTypeUID',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
275
                  expression='context.getSampleType() and \
276
                             context.getSampleType().UID() or None',
277
                  widget=ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedWidget'
Loading history...
278
                    visible=False,
279
                  ),
280
    ),
281
    ComputedField('SamplePointUID',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
282
        expression = 'context.getSamplePoint() and context.getSamplePoint().UID() or None',
283
        widget = ComputedWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedWidget'
Loading history...
284
            visible=False,
285
        ),
286
    ),
287
    BooleanField('Composite',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanField'
Loading history...
288
        default = False,
289
        mode="rw",
290
        read_permission=permissions.View,
291
        write_permission=permissions.ModifyPortalContent,
292
        widget = BooleanWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BooleanWidget does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanWidget'
Loading history...
293
            label=_("Composite"),
294
            visible=False,
295
            render_own_label=True,
296
        ),
297
    ),
298
    DateTimeField('DateExpired',
299
        mode="rw",
300
        read_permission=permissions.View,
301
        write_permission=permissions.ModifyPortalContent,
302
        widget = DateTimeWidget(
303
            label=_("Date Expired"),
304
            visible=False,
305
            render_own_label=True,
306
        ),
307
    ),
308
    ComputedField('DisposalDate',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ComputedField'
Loading history...
309
        expression = 'context.disposal_date()',
310
        widget=DateTimeWidget(
311
            visible=False,
312
            render_own_label=True,
313
        ),
314
    ),
315
    DateTimeField('DateDisposed',
316
        mode="rw",
317
        read_permission=permissions.View,
318
        write_permission=permissions.ModifyPortalContent,
319
        widget = DateTimeWidget(
320
            label=_("Date Disposed"),
321
            visible=False,
322
            render_own_label=True,
323
        ),
324
    ),
325
    BooleanField('AdHoc',
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanField'
Loading history...
326
        default=False,
327
        mode="rw",
328
        read_permission=permissions.View,
329
        write_permission=permissions.ModifyPortalContent,
330
        widget=BooleanWidget(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'BooleanWidget'
Loading history...
331
            label=_("Ad-Hoc"),
332
            visible=False,
333
           render_own_label=True,
334
        ),
335
    ),
336
    RemarksField(
337
        'Remarks',
338
        searchable=True,
339
        widget=RemarksWidget(
340
            label=_("Remarks"),
341
        ),
342
    ),
343
    RecordsField(
344
        'RejectionReasons',
345
        widget = RejectionWidget(
346
            label=_("Sample Rejection"),
347
            description = _("Set the Sample Rejection workflow and the reasons"),
348
            render_own_label=False,
349
            visible=False,
350
        ),
351
    ),
352
))
353
354
355
schema['title'].required = False
356
357
358
class Sample(BaseFolder, HistoryAwareMixin):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable BaseFolder does not seem to be defined.
Loading history...
Comprehensibility Best Practice introduced by
Undefined variable 'BaseFolder'
Loading history...
359
    implements(ISample)
360
    security = ClassSecurityInfo()
361
    displayContentsTab = False
362
    schema = schema
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable schema does not seem to be defined.
Loading history...
363
364
    _at_rename_after_creation = True
365
366
    def _renameAfterCreation(self, check_auto_id=False):
367
        from bika.lims.idserver import renameAfterCreation
368
        renameAfterCreation(self)
369
370
    def _getCatalogTool(self):
371
        from bika.lims.catalog import getCatalog
372
        return getCatalog(self)
373
374
    def getSampleID(self):
375
        """ Return the Sample ID as title """
376
        return safe_unicode(self.getId()).encode('utf-8')
377
378
    def Title(self):
379
        """ Return the Sample ID as title """
380
        return self.getSampleID()
381
382
    def getSamplingWorkflowEnabledDefault(self):
383
        return self.bika_setup.getSamplingWorkflowEnabled()
384
385
    def getContactTitle(self):
386
        return ""
387
388
    def getClientTitle(self):
389
        proxies = self.getAnalysisRequests()
390
        if not proxies:
391
            return ""
392
        value = proxies[0].aq_parent.Title()
393
        return value
394
395
    def getProfilesTitle(self):
396
        return ""
397
398
    def getAnalysisService(self):
399
        analyses = []
400
        for ar in self.getAnalysisRequests():
401
            analyses += list(ar.getAnalyses(full_objects=True))
402
        value = []
403
        for analysis in analyses:
404
            val = analysis.Title()
405
            if val not in value:
406
                value.append(val)
407
        return value
408
409 View Code Duplication
    def getAnalysts(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
410
        analyses = []
411
        for ar in self.getAnalysisRequests():
412
            analyses += list(ar.getAnalyses(full_objects=True))
413
        value = []
414
        for analysis in analyses:
415
            val = analysis.getAnalyst()
416
            if val not in value:
417
                value.append(val)
418
        return value
419
420
    security.declarePublic('getAnalysisRequests')
421
422
    def getAnalysisRequests(self):
423
        backrefs = get_backreferences(self, 'AnalysisRequestSample')
424
        ars = map(get_object_by_uid, backrefs)
425
        return ars
426
427
    security.declarePublic('getAnalyses')
428
429
    def getAnalyses(self, contentFilter=None, **kwargs):
430
        """ return list of all analyses against this sample
431
        """
432
        # contentFilter and kwargs are combined.  They both exist for
433
        # compatibility between the two signatures; kwargs has been added
434
        # to be compatible with how getAnalyses() is used everywhere else.
435
        cf = contentFilter if contentFilter else {}
436
        cf.update(kwargs)
437
        analyses = []
438
        for ar in self.getAnalysisRequests():
439
            analyses.extend(ar.getAnalyses(**cf))
440
        return analyses
441
442
    def getSamplers(self):
443
        return getUsers(self, ['Sampler', ])
444
445
    def disposal_date(self):
446
        """Returns the date the retention period ends for this sample based on
447
        the retention period from the Sample Type. If the sample hasn't been
448
        collected yet, returns None
449
        """
450
        date_sampled = self.getDateSampled()
451
        if not date_sampled:
452
            return None
453
454
        # TODO Preservation - preservation's retention period has priority over
455
        # sample type's preservation period
456
457
        retention_period = self.getSampleType().getRetentionPeriod() or {}
458
        retention_period_delta = timedelta(
459
            days=int(retention_period.get("days", 0)),
460
            hours=int(retention_period.get("hours", 0)),
461
            minutes=int(retention_period.get("minutes", 0))
462
        )
463
        return dt2DT(DT2dt(date_sampled) + retention_period_delta)
464
465
466
    # TODO This method is only for v1.3.0 migration purposes
467
    # bika_catalog contains an "isValid" index. We will take advantage of this
468
    # index to keep track of the Samples that have been migrated already in
469
    # order to prevent an unnecessary reimport when v1.3.0 is rerun.
470
    def isValid(self):
471
        return self.getMigrated()
472
473
474
atapi.registerType(Sample, PROJECTNAME)
475