Passed
Push — master ( 0e02ba...4def5b )
by Jordi
04:46
created

bika.lims.browser.sample.view   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 585
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 39
eloc 499
dl 0
loc 585
rs 9.28
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A SamplesView._schedule_sampling_permissions() 0 10 1
F SamplesView.folderitem() 0 135 19
D SamplesView.folderitems() 0 32 12
A SampleView.__call__() 0 3 1
A SamplesView.before_render() 0 13 2
A SamplesView.isItemAllowed() 0 7 1
B SamplesView.__init__() 0 347 3
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
from DateTime import DateTime
9
from Products.CMFCore.utils import getToolByName
10
from bika.lims import PMF
11
from bika.lims import api
12
from bika.lims import bikaMessageFactory as _
13
from bika.lims.browser.bika_listing import BikaListingView
14
from bika.lims.permissions import *
15
from bika.lims.utils import getUsers
16
from bika.lims.utils import t
17
18
from . import SampleEdit
19
20
21
class SampleView(SampleEdit):
22
    """
23
    The view of a single sample
24
    """
25
    def __call__(self):
26
        self.allow_edit = False
27
        return SampleEdit.__call__(self)
28
29
30
class SamplesView(BikaListingView):
31
    """
32
    A list of samples view (folder view)
33
    """
34
35
    def __init__(self, context, request):
36
        super(SamplesView, self).__init__(context, request)
37
38
        request.set('disable_plone.rightcolumn', 1)
39
40
        self.catalog = 'bika_catalog'
41
        self.contentFilter = {'portal_type': 'Sample',
42
                              'sort_on':'created',
43
                              'sort_order': 'reverse',
44
                              'path': {'query': "/",
45
                                       'level': 0 }
46
                              }
47
        # So far we will only print if the sampling workflow is activated
48
        if self.context.bika_setup.getSamplingWorkflowEnabled():
49
            self.context_actions = {
50
                _('Print sample sheets'): {
51
                    'url': 'print_sampling_sheets',
52
                    'icon': '++resource++bika.lims.images/print_32.png'}
53
                    }
54
        else:
55
                self.context_actions = {}
56
57
        self.show_select_row = False
58
        self.show_select_column = True
59
        self.allow_edit = True
60
        self.form_id = "samples"
61
62
        if self.view_url.find("/samples") > -1:
63
            self.request.set('disable_border', 1)
64
        else:
65
            self.view_url = self.view_url + "/samples"
66
67
        self.icon = self.portal_url + "/++resource++bika.lims.images/sample_big.png"
68
        self.title = self.context.translate(_("Samples"))
69
        self.description = ""
70
        SamplingWorkflowEnabled = self.context.bika_setup.getSamplingWorkflowEnabled()
71
        mtool = getToolByName(self.context, 'portal_membership')
72
        member = mtool.getAuthenticatedMember()
73
        user_is_preserver = 'Preserver' in member.getRoles()
74
        # Defined in the __init__.py
75
        self.columns = {
76
            'getSampleID': {
77
                'title': _('Sample ID'),
78
                'index': 'getSampleID'},
79
            'Client': {
80
                'title': _("Client"),
81
                'index': 'getClientTitle',
82
                'toggle': True, },
83
            'Creator': {
84
                'title': PMF('Creator'),
85
                'index': 'Creator',
86
                'toggle': True},
87
            'Created': {
88
                'title': PMF('Date Created'),
89
                'index': 'created',
90
                'toggle': False},
91
            'Requests': {
92
                'title': _('Requests'),
93
                'sortable': False,
94
                'toggle': False},
95
            'getClientReference': {
96
                'title': _('Client Ref'),
97
                'index': 'getClientReference',
98
                'toggle': True},
99
            'getClientSampleID': {
100
                'title': _('Client SID'),
101
                'index': 'getClientSampleID',
102
                'toggle': True},
103
            'getSampleTypeTitle': {
104
                'title': _('Sample Type'),
105
                'index': 'getSampleTypeTitle'},
106
            'getSamplePointTitle': {
107
                'title': _('Sample Point'),
108
                'index': 'getSamplePointTitle',
109
                'toggle': False},
110
            'getStorageLocation': {
111
                'sortable': False,
112
                'title': _('Storage Location'),
113
                'toggle': False},
114
            'SamplingDeviation': {
115
                'title': _('Sampling Deviation'),
116
                'sortable': False,
117
                'toggle': False},
118
            'AdHoc': {
119
                'title': _('Ad-Hoc'),
120
                'sortable': False,
121
                'toggle': False},
122
            'SamplingDate': {
123
                'title': _('Sampling Date'),
124
                'index': 'getSamplingDate',
125
                'input_class': 'datetimepicker_nofuture autosave',
126
                'input_width': '10',
127
                'toggle': SamplingWorkflowEnabled},
128
            'DateSampled': {
129
                'title': _('Date Sampled'),
130
                'index': 'getDateSampled',
131
                'toggle': True,
132
                'input_class': 'datetimepicker_nofuture autosave',
133
                'input_width': '10'},
134
            'getSampler': {
135
                'title': _('Sampler'),
136
                'toggle': SamplingWorkflowEnabled},
137
            'getScheduledSamplingSampler': {
138
                'title': _('Sampler for scheduled sampling'),
139
                'input_class': 'autosave',
140
                'sortable': False,
141
                'toggle': self.context.bika_setup.getScheduleSamplingEnabled()
142
                },
143
            'getDatePreserved': {
144
                'title': _('Date Preserved'),
145
                'toggle': user_is_preserver,
146
                'input_class': 'datepicker_nofuture',
147
                'input_width': '10'},
148
            'getPreserver': {
149
                'title': _('Preserver'),
150
                'toggle': user_is_preserver},
151
            'DateReceived': {
152
                'title': _('Date Received'),
153
                'index': 'getDateReceived',
154
                'toggle': False},
155
            'state_title': {
156
                'title': _('State'),
157
                'sortable': False,
158
                'index': 'review_state'},
159
        }
160
161
        self.review_states = [
162
            {'id': 'default',
163
             'title': _('Active'),
164
             'contentFilter': {'cancellation_state': 'active',
165
                               'sort_on': 'created'},
166
             'columns': ['getSampleID',
167
                         'Client',
168
                         'Creator',
169
                         'Created',
170
                         'Requests',
171
                         'getClientReference',
172
                         'getClientSampleID',
173
                         'getSampleTypeTitle',
174
                         'getSamplePointTitle',
175
                         'getStorageLocation',
176
                         'SamplingDeviation',
177
                         'AdHoc',
178
                         'SamplingDate',
179
                         'getScheduledSamplingSampler',
180
                         'DateSampled',
181
                         'getSampler',
182
                         'getDatePreserved',
183
                         'getPreserver',
184
                         'DateReceived',
185
                         'state_title'],
186
             'custom_actions': [{
187
                 'id': 'print_stickers',
188
                 'title': _('Print stickers'),
189
                 'url': 'workflow_action?action=print_stickers'}],},
190
            {'id': 'to_be_sampled',
191
             'title': _('To be sampled'),
192
             'contentFilter': {'review_state': ('to_be_sampled',
193
                                                'scheduled_sampling'),
194
                               'cancellation_state': 'active',
195
                               'sort_on': 'created',
196
                               'sort_order': 'reverse'},
197
             'columns': ['getSampleID',
198
                         'Client',
199
                         'Requests',
200
                         'getClientReference',
201
                         'getClientSampleID',
202
                         'SamplingDate',
203
                         'getScheduledSamplingSampler',
204
                         'DateSampled',
205
                         'getSampler',
206
                         'getPreserver',
207
                         'getSampleTypeTitle',
208
                         'getSamplePointTitle',
209
                         'state_title'],
210
             'transitions': [
211
                {'id': 'schedule_sampling'}, {'id': 'sample'}],
212
             'custom_actions': [{
213
                 'id': 'print_stickers',
214
                 'title': _('Print stickers'),
215
                 'url': 'workflow_action?action=print_stickers'}],
216
             },
217
            {'id': 'sample_due',
218
             'title': _('Due'),
219
             'contentFilter': {'review_state': ('to_be_preserved',
220
                                                'sample_due'),
221
                               'sort_on': 'created',
222
                               'sort_order': 'reverse'},
223
             'columns': ['getSampleID',
224
                         'Client',
225
                         'Creator',
226
                         'Created',
227
                         'Requests',
228
                         'getClientReference',
229
                         'getClientSampleID',
230
                         'SamplingDate',
231
                         'getScheduledSamplingSampler',
232
                         'getScheduledSamplingSampler',
233
                         'DateSampled',
234
                         'getSampler',
235
                         'getDatePreserved',
236
                         'getPreserver',
237
                         'getSampleTypeTitle',
238
                         'getSamplePointTitle',
239
                         'getStorageLocation',
240
                         'SamplingDeviation',
241
                         'AdHoc',
242
                         'state_title']},
243
            {'id': 'sample_received',
244
             'title': _('Received'),
245
             'contentFilter': {'review_state':'sample_received',
246
                              'sort_order': 'reverse',
247
                              'sort_on':'created'},
248
             'columns': ['getSampleID',
249
                         'Client',
250
                         'Creator',
251
                         'Created',
252
                         'Requests',
253
                         'getClientReference',
254
                         'getClientSampleID',
255
                         'getSampleTypeTitle',
256
                         'getSamplePointTitle',
257
                         'getStorageLocation',
258
                         'SamplingDeviation',
259
                         'AdHoc',
260
                         'SamplingDate',
261
                         'getScheduledSamplingSampler',
262
                         'DateSampled',
263
                         'getSampler',
264
                         'getDatePreserved',
265
                         'getPreserver',
266
                         'DateReceived'],
267
             'custom_actions': [{
268
                 'id': 'print_stickers',
269
                 'title': _('Print stickers'),
270
                 'url': 'workflow_action?action=print_stickers'}],},
271
            {'id':'expired',
272
             'title': _('Expired'),
273
             'contentFilter':{'review_state':'expired',
274
                              'sort_order': 'reverse',
275
                              'sort_on':'created'},
276
             'columns': ['getSampleID',
277
                         'Client',
278
                         'Creator',
279
                         'Created',
280
                         'Requests',
281
                         'getClientReference',
282
                         'getClientSampleID',
283
                         'getSampleTypeTitle',
284
                         'getSamplePointTitle',
285
                         'getStorageLocation',
286
                         'SamplingDeviation',
287
                         'AdHoc',
288
                         'SamplingDate',
289
                         'getScheduledSamplingSampler',
290
                         'DateSampled',
291
                         'getSampler',
292
                         'getDatePreserved',
293
                         'getPreserver',
294
                         'DateReceived'],
295
             'custom_actions': [{
296
                 'id': 'print_stickers',
297
                 'title': _('Print stickers'),
298
                 'url': 'workflow_action?action=print_stickers'}],},
299
            {'id':'disposed',
300
             'title': _('Disposed'),
301
             'contentFilter':{'review_state':'disposed',
302
                              'sort_order': 'reverse',
303
                              'sort_on':'created'},
304
             'columns': ['getSampleID',
305
                         'Client',
306
                         'Creator',
307
                         'Created',
308
                         'Requests',
309
                         'getClientReference',
310
                         'getClientSampleID',
311
                         'getSampleTypeTitle',
312
                         'getSamplePointTitle',
313
                         'getStorageLocation',
314
                         'SamplingDeviation',
315
                         'AdHoc',
316
                         'SamplingDate',
317
                         'getScheduledSamplingSampler',
318
                         'DateSampled',
319
                         'getSampler',
320
                         'getDatePreserved',
321
                         'getPreserver',
322
                         'DateReceived'],
323
             'custom_actions': [{
324
                 'id': 'print_stickers',
325
                 'title': _('Print stickers'),
326
                 'url': 'workflow_action?action=print_stickers'}],},
327
            {'id':'cancelled',
328
             'title': _('Cancelled'),
329
             'contentFilter': {'cancellation_state': 'cancelled',
330
                               'sort_order': 'reverse',
331
                               'sort_on':'created'},
332
             'transitions': [{'id':'reinstate'}, ],
333
             'columns': ['getSampleID',
334
                         'Client',
335
                         'Creator',
336
                         'Created',
337
                         'Requests',
338
                         'getClientReference',
339
                         'getClientSampleID',
340
                         'getSampleTypeTitle',
341
                         'getSamplePointTitle',
342
                         'getStorageLocation',
343
                         'SamplingDeviation',
344
                         'AdHoc',
345
                         'SamplingDate',
346
                         'getScheduledSamplingSampler',
347
                         'DateReceived',
348
                         'DateSampled',
349
                         'getSampler',
350
                         'getDatePreserved',
351
                         'getPreserver',
352
                         'state_title']},
353
            {'id': 'rejected',
354
             'title': _('Rejected'),
355
             'contentFilter': {'review_state': 'rejected',
356
                               'sort_order': 'reverse',
357
                               'sort_on': 'created'},
358
             'transitions': [],
359
             'custom_actions': [{
360
                 'id': 'print_stickers',
361
                 'title': _('Print stickers'),
362
                 'url': 'workflow_action?action=print_stickers'}],
363
             'columns': ['getSampleID',
364
                         'Client',
365
                         'Creator',
366
                         'Created',
367
                         'Requests',
368
                         'getClientReference',
369
                         'getClientSampleID',
370
                         'getSampleTypeTitle',
371
                         'getSamplePointTitle',
372
                         'getStorageLocation',
373
                         'SamplingDeviation',
374
                         'AdHoc',
375
                         'SamplingDate',
376
                         'DateReceived',
377
                         'DateSampled',
378
                         'getSampler',
379
                         'getDatePreserved',
380
                         'getPreserver',
381
                         'state_title']},
382
        ]
383
384
    def before_render(self):
385
        """Before template render hook
386
        """
387
        # If the current user is a client contact, display those samples that
388
        # belong to same client only
389
        super(SamplesView, self).before_render()
390
        client = api.get_current_client()
391
        if client:
392
            self.contentFilter['path'] = {
393
                "query": "/".join(client.getPhysicalPath()),
394
                "level": 0 }
395
            # No need to display the Client column
396
            self.remove_column('Client')
397
398
    def folderitem(self, obj, item, index):
399
        workflow = getToolByName(self.context, "portal_workflow")
400
        mtool = getToolByName(self.context, 'portal_membership')
401
        member = mtool.getAuthenticatedMember()
402
        translate = self.context.translate
403
        roles = member.getRoles()
404
        hideclientlink = 'RegulatoryInspector' in roles \
405
            and 'Manager' not in roles \
406
            and 'LabManager' not in roles \
407
            and 'LabClerk' not in roles
408
        if not item.has_key('obj'):
409
            return item
410
        obj = item['obj']
411
412
        item['replace']['getSampleID'] = "<a href='%s'>%s</a>" % \
413
            (item['url'], obj.getSampleID())
414
        item['replace']['Requests'] = ",".join(
415
            ["<a href='%s'>%s</a>" % (o.absolute_url(), o.Title())
416
             for o in obj.getAnalysisRequests()])
417
        item['Client'] = obj.aq_parent.Title()
418
        if hideclientlink == False:
419
            item['replace']['Client'] = "<a href='%s'>%s</a>" % \
420
                (obj.aq_parent.absolute_url(), obj.aq_parent.Title())
421
        item['Creator'] = self.user_fullname(obj.Creator())
422
423
        item['DateReceived'] = self.ulocalized_time(obj.getDateReceived())
424
425
        deviation = obj.getSamplingDeviation()
426
        item['SamplingDeviation'] = deviation and deviation.Title() or ''
427
428
        item['getStorageLocation'] = obj.getStorageLocation() and obj.getStorageLocation().Title() or ''
429
        item['AdHoc'] = obj.getAdHoc() and True or ''
430
431
        item['Created'] = self.ulocalized_time(obj.created(), long_format=1)
432
433
        sd = obj.getSamplingDate()
434
        item['SamplingDate'] = \
435
            self.ulocalized_time(sd, long_format=1) if sd else ''
436
437
        after_icons = ''
438
        if obj.getSampleType().getHazardous():
439
            after_icons += "<img title='%s' " \
440
                "src='%s/++resource++bika.lims.images/hazardous.png'>" % \
441
                (t(_("Hazardous")),
442
                 self.portal_url)
443
        if sd and sd > DateTime():
444
            after_icons += "<img title='%s' " \
445
                "src='%s/++resource++bika.lims.images/calendar.png' >" % \
446
                (t(_("Future dated sample")),
447
                 self.portal_url)
448
        if after_icons:
449
            item['after']['getSampleID'] = after_icons
450
451
        if obj.getSamplingWorkflowEnabled():
452
            datesampled = self.ulocalized_time(
453
                obj.getDateSampled(), long_format=True)
454
            if not datesampled:
455
                datesampled = self.ulocalized_time(
456
                    DateTime(), long_format=True)
457
                item['class']['DateSampled'] = 'provisional'
458
            sampler = obj.getSampler().strip()
459
            if sampler:
460
                item['replace']['getSampler'] = self.user_fullname(sampler)
461
            if 'Sampler' in member.getRoles() and not sampler:
462
                sampler = member.id
463
                item['class']['getSampler'] = 'provisional'
464
        else:
465
            datesampled = self.ulocalized_time(obj.getDateSampled(), long_format=True)
466
            sampler = ''
467
468
        item['DateSampled'] = datesampled
469
        item['getSampler'] = sampler
470
        # sampling workflow - inline edits for Sampler, Date Sampled and
471
        # Scheduled Sampling Sampler
472
        checkPermission = self.context.portal_membership.checkPermission
473
        state = workflow.getInfoFor(obj, 'review_state')
474
        if state in ['to_be_sampled', 'scheduled_sampling']:
475
            item['required'] = []
476
            item['allow_edit'] = []
477
            item['choices'] = {}
478
            samplers = getUsers(obj, ['Sampler', 'LabManager', 'Manager'])
479
            users = [(
480
                {'ResultValue': u, 'ResultText': samplers.getValue(u)})
481
                for u in samplers]
482
            # both situations
483
            if checkPermission(SampleSample, obj) or\
484
                    self._schedule_sampling_permissions():
485
                item['required'].append('getSampler')
486
                item['allow_edit'].append('getSampler')
487
                item['choices']['getSampler'] = users
488
            # sampling permissions
489
            if checkPermission(SampleSample, obj):
490
                getAuthenticatedMember = self.context.\
491
                    portal_membership.getAuthenticatedMember
492
                username = getAuthenticatedMember().getUserName()
493
                Sampler = sampler and sampler or \
494
                    (username in samplers.keys() and username) or ''
495
                item['required'].append('DateSampled')
496
                item['allow_edit'].append('DateSampled')
497
                item['getSampler'] = Sampler
498
            # coordinator permissions
499
            if self._schedule_sampling_permissions():
500
                item['required'].append('SamplingDate')
501
                item['allow_edit'].append('SamplingDate')
502
                item['required'].append('getScheduledSamplingSampler')
503
                item['allow_edit'].append(
504
                    'getScheduledSamplingSampler')
505
                item['choices']['getScheduledSamplingSampler'] = users
506
        # These don't exist on samples
507
        # the columns exist just to set "preserve" transition from lists.
508
        # XXX This should be a list of preservers...
509
        item['getPreserver'] = ''
510
        item['getDatePreserved'] = ''
511
        # Here we are defining the name of the content field represented by
512
        # the column
513
        item['field']['getSampler'] = 'Sampler'
514
        item['field']['getScheduledSamplingSampler'] =\
515
            'ScheduledSamplingSampler'
516
        # inline edits for Preserver and Date Preserved
517
        checkPermission = self.context.portal_membership.checkPermission
518
        if checkPermission(PreserveSample, obj):
519
            item['required'] = ['getPreserver', 'getDatePreserved']
520
            item['allow_edit'] = ['getPreserver', 'getDatePreserved']
521
            preservers = getUsers(obj, ['Preserver', 'LabManager', 'Manager'])
522
            getAuthenticatedMember = self.context.portal_membership.getAuthenticatedMember
523
            username = getAuthenticatedMember().getUserName()
524
            users = [({'ResultValue': u, 'ResultText': preservers.getValue(u)})
525
                     for u in preservers]
526
            item['choices'] = {'getPreserver': users}
527
            preserver = username in preservers.keys() and username or ''
528
            item['getPreserver'] = preserver
529
            item['getDatePreserved'] = self.ulocalized_time(DateTime())
530
            item['class']['getPreserver'] = 'provisional'
531
            item['class']['getDatePreserved'] = 'provisional'
532
        return item
533
534
    def folderitems(self, full_objects=False):
535
        items = BikaListingView.folderitems(self, full_objects=False)
536
        # Hide Preservation/Sampling workflow actions if the edit columns
537
        # are not displayed.
538
        # Hide schedule_sampling if user has no rights
539
        toggle_cols = self.get_toggle_cols()
540
        new_states = []
541
        for i,state in enumerate(self.review_states):
542
            if state and self.review_state and state['id'] == self.review_state.get('id', ''):
543
                if 'getSampler' not in toggle_cols \
544
                   or 'DateSampled' not in toggle_cols:
545
                    if 'hide_transitions' in state:
546
                        state['hide_transitions'].append('sample')
547
                    else:
548
                        state['hide_transitions'] = ['sample',]
549
                if 'getPreserver' not in toggle_cols \
550
                   or 'getDatePreserved' not in toggle_cols:
551
                    if 'hide_transitions' in state:
552
                        state['hide_transitions'].append('preserve')
553
                    else:
554
                        state['hide_transitions'] = ['preserve',]
555
                # Check if the user has the rights to schedule samplings and
556
                # the check-box 'ScheduleSamplingEnabled' in bikasetup is set
557
                if self._schedule_sampling_permissions():
558
                    # Show the workflow transition button 'schedule_sampling'
559
                    pass
560
                else:
561
                    # Hiddes the button
562
                    state['hide_transitions'] = ['schedule_sampling', ]
563
            new_states.append(state)
564
        self.review_states = new_states
565
        return items
566
567
    def _schedule_sampling_permissions(self):
568
        """
569
        This function checks if all the 'schedule a sampling' conditions
570
        are met
571
        """
572
        mtool = getToolByName(self.context, 'portal_membership')
573
        member = mtool.getAuthenticatedMember()
574
        roles = member.getRoles()
575
        return self.context.bika_setup.getScheduleSamplingEnabled() and\
576
            ('SamplingCoordinator' in roles or 'Manager' in roles)
577
578
    def isItemAllowed(self, obj):
579
        """
580
        Checks the BikaLIMS conditions and also checks filter bar conditions
581
        @Obj: it is a sample object.
582
        @return: boolean
583
        """
584
        return super(SamplesView, self).isItemAllowed(obj)
585