Passed
Push — master ( b85412...b5795d )
by Ramon
06:07
created

  A

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 2
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
import collections
9
import json
10
import traceback
11
12
from bika.lims import api
13
from bika.lims import bikaMessageFactory as _
14
from bika.lims import logger
15
from bika.lims.browser.analysisrequest.analysisrequests_filter_bar import \
16
    AnalysisRequestsBikaListingFilterBar
17
from bika.lims.browser.bika_listing import BikaListingView
18
from bika.lims.catalog import CATALOG_ANALYSIS_REQUEST_LISTING
19
from bika.lims.config import PRIORITIES
20
from bika.lims.permissions import AddAnalysisRequest
21
from bika.lims.permissions import ManageAnalysisRequests
22
from bika.lims.permissions import SampleSample
23
from bika.lims.permissions import Verify as VerifyPermission
24
from bika.lims.utils import get_image
25
from bika.lims.utils import getUsers
26
from bika.lims.utils import t
27
from DateTime import DateTime
28
from plone.api import user
29
from plone.protect import CheckAuthenticator
30
from plone.protect import PostOnly
31
from Products.CMFCore.permissions import ModifyPortalContent
32
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
33
from zope.component import queryUtility
34
35
36
class AnalysisRequestsView(BikaListingView):
37
    """Listing View for all Analysis Requests in the System
38
    """
39
40
    template = ViewPageTemplateFile("templates/analysisrequests.pt")
41
42
    def __init__(self, context, request):
43
        super(AnalysisRequestsView, self).__init__(context, request)
44
45
        # hide the right column
46
        request.set("disable_plone.rightcolumn", 1)
47
48
        # hide the editable border
49
        if self.context.portal_type == "AnalysisRequestsFolder":
50
            self.request.set("disable_border", 1)
51
52
        # catalog used for the query
53
        self.catalog = CATALOG_ANALYSIS_REQUEST_LISTING
54
55
        # https://docs.plone.org/develop/plone/searching_and_indexing/query.html#searching-for-content-within-a-folder
56
        self.contentFilter = {
57
            "sort_on": "created",
58
            "sort_order": "descending",
59
            "cancellation_state": "active",
60
        }
61
62
        # Filter by Department
63
        if self.context.bika_setup.getAllowDepartmentFiltering():
64
            deps = self.request.get('filter_by_department_info', '')
65
            dep_uids = deps.split(",")
66
            dep_query = {"query": dep_uids, "operator": "or"}
67
            self.contentFilter['getDepartmentUIDs'] = dep_query
68
69
        self.context_actions = {}
70
71
        if self.view_url.find("analysisrequests") == -1:
72
            self.view_url = self.view_url + "/analysisrequests"
73
74
        self.allow_edit = True
75
        self.show_sort_column = False
76
        self.show_select_row = False
77
        self.show_select_column = True
78
        self.form_id = "analysisrequests"
79
80
        ar_image_path = "/++resource++bika.lims.images/analysisrequest_big.png"
81
        self.icon = "{}{}".format(self.portal_url, ar_image_path)
82
        self.title = self.context.translate(_("Analysis Requests"))
83
        self.description = ""
84
85
        SamplingWorkflowEnabled = \
86
            self.context.bika_setup.getSamplingWorkflowEnabled()
87
88
        # Check if the filter bar functionality is activated or not
89
        self.filter_bar_enabled =\
90
            self.context.bika_setup.\
91
            getDisplayAdvancedFilterBarForAnalysisRequests()
92
93
        self.columns = collections.OrderedDict((
94
            ("Priority", {
95
                "title": "",
96
                "index": "getPrioritySortkey",
97
                "sortable": True, }),
98
            ("Progress", {
99
                "title": "Progress",
100
                "sortable": False,
101
                "toggle": True}),
102
            ("getId", {
103
                "title": _("Request ID"),
104
                "attr": "getId",
105
                "replace_url": "getURL",
106
                "index": "getId"}),
107
            ("getClientOrderNumber", {
108
                "title": _("Client Order"),
109
                "sortable": True,
110
                "toggle": False}),
111
            ("Creator", {
112
                "title": _("Creator"),
113
                "index": "getCreatorFullName",
114
                "sortable": True,
115
                "toggle": True}),
116
            ("Created", {
117
                "title": _("Date Registered"),
118
                "index": "created",
119
                "toggle": False}),
120
            ("SamplingDate", {
121
                "title": _("Expected Sampling Date"),
122
                "index": "getSamplingDate",
123
                "toggle": SamplingWorkflowEnabled}),
124
            ("getDateSampled", {
125
                "title": _("Date Sampled"),
126
                "toggle": True,
127
                "input_class": "datetimepicker_nofuture",
128
                "input_width": "10"}),
129
            ("getDatePreserved", {
130
                "title": _("Date Preserved"),
131
                "toggle": False,
132
                "input_class": "datetimepicker_nofuture",
133
                "input_width": "10",
134
                "sortable": False}),  # no datesort without index
135
            ("getDateReceived", {
136
                "title": _("Date Received"),
137
                "toggle": False}),
138
            ("getDueDate", {
139
                "title": _("Due Date"),
140
                "toggle": False}),
141
            ("getDateVerified", {
142
                "title": _("Date Verified"),
143
                "input_width": "10",
144
                "toggle": False}),
145
            ("getDatePublished", {
146
                "title": _("Date Published"),
147
                "toggle": False}),
148
            ("getSample", {
149
                "title": _("Sample"),
150
                "attr": "getSampleID",
151
                "index": "getSampleID",
152
                "replace_url": "getSampleURL",
153
                "toggle": False}),
154
            ("BatchID", {
155
                "title": _("Batch ID"),
156
                "index": "getBatchID",
157
                "sortable": True,
158
                "toggle": False}),
159
            ("Client", {
160
                "title": _("Client"),
161
                "index": "getClientTitle",
162
                "attr": "getClientTitle",
163
                "replace_url": "getClientURL",
164
                "toggle": True}),
165
            ("Province", {
166
                "title": _("Province"),
167
                "sortable": True,
168
                "index": "getProvince",
169
                "attr": "getProvince",
170
                "toggle": False}),
171
            ("District", {
172
                "title": _("District"),
173
                "sortable": True,
174
                "index": "getDistrict",
175
                "attr": "getDistrict",
176
                "toggle": False}),
177
            ("getClientReference", {
178
                "title": _("Client Ref"),
179
                "sortable": True,
180
                "index": "getClientReference",
181
                "toggle": False}),
182
            ("getClientSampleID", {
183
                "title": _("Client SID"),
184
                "toggle": False}),
185
            ("ClientContact", {
186
                "title": _("Contact"),
187
                "sortable": True,
188
                "index": "getContactFullName",
189
                "toggle": False}),
190
            ("getSampleTypeTitle", {
191
                "title": _("Sample Type"),
192
                "sortable": True,
193
                "toggle": True}),
194
            ("getSamplePointTitle", {
195
                "title": _("Sample Point"),
196
                "sortable": True,
197
                "index": "getSamplePointTitle",
198
                "toggle": False}),
199
            ("getStorageLocation", {
200
                "title": _("Storage Location"),
201
                "sortable": True,
202
                "index": "getStorageLocationTitle",
203
                "toggle": False}),
204
            ("SamplingDeviation", {
205
                "title": _("Sampling Deviation"),
206
                "sortable": True,
207
                "index": "getSamplingDeviationTitle",
208
                "toggle": False}),
209
            ("getSampler", {
210
                "title": _("Sampler"),
211
                "toggle": SamplingWorkflowEnabled}),
212
            ("getPreserver", {
213
                "title": _("Preserver"),
214
                "sortable": False,
215
                "toggle": False}),
216
            ("getProfilesTitle", {
217
                "title": _("Profile"),
218
                "sortable": True,
219
                "index": "getProfilesTitle",
220
                "toggle": False}),
221
            ("getAnalysesNum", {
222
                "title": _("Number of Analyses"),
223
                "sortable": True,
224
                "index": "getAnalysesNum",
225
                "toggle": False}),
226
            ("getTemplateTitle", {
227
                "title": _("Template"),
228
                "sortable": True,
229
                "index": "getTemplateTitle",
230
                "toggle": False}),
231
            ("Printed", {
232
                "title": _("Printed"),
233
                "sortable": False,
234
                "index": "getPrinted",
235
                "toggle": False}),
236
            ("state_title", {
237
                "title": _("State"),
238
                "sortable": True,
239
                "index": "review_state"}),
240
        ))
241
242
        # custom print transition
243
        print_stickers = {
244
            "id": "print_stickers",
245
            "title": _("Print stickers"),
246
            "url": "workflow_action?action=print_stickers"
247
        }
248
249
        self.review_states = [
250
            {
251
                "id": "default",
252
                "title": _("Active"),
253
                "contentFilter": {
254
                    "sort_on": "created",
255
                    "sort_order": "descending",
256
                },
257
                "transitions": [
258
                    {"id": "sample"},
259
                    {"id": "preserve"},
260
                    {"id": "receive"},
261
                    {"id": "retract"},
262
                    {"id": "verify"},
263
                    {"id": "prepublish"},
264
                    {"id": "publish"},
265
                    {"id": "republish"},
266
                    {"id": "cancel"},
267
                    {"id": "reinstate"},
268
                ],
269
                "custom_transitions": [print_stickers],
270
                "columns": self.columns.keys(),
271
            }, {
272
                "id": "to_be_sampled",
273
                "title": _("To Be Sampled"),
274
                "contentFilter": {
275
                    "review_state": ("to_be_sampled",),
276
                    "sort_on": "created",
277
                    "sort_order": "descending"},
278
                "transitions": [
279
                    {"id": "sample"},
280
                    {"id": "submit"},
281
                    {"id": "cancel"},
282
                ],
283
                "custom_transitions": [print_stickers],
284
                "columns": self.columns.keys()
285
            }, {
286
                "id": "to_be_preserved",
287
                "title": _("To Be Preserved"),
288
                "contentFilter": {
289
                    "review_state": ("to_be_preserved",),
290
                    "sort_on": "created",
291
                    "sort_order": "descending",
292
                },
293
                "transitions": [
294
                    {"id": "preserve"},
295
                    {"id": "cancel"},
296
                ],
297
                "custom_transitions": [print_stickers],
298
                "columns": self.columns.keys(),
299
            }, {
300
                "id": "scheduled_sampling",
301
                "title": _("Scheduled sampling"),
302
                "contentFilter": {
303
                    "review_state": ("scheduled_sampling",),
304
                    "sort_on": "created",
305
                    "sort_order": "descending",
306
                },
307
                "transitions": [
308
                    {"id": "sample"},
309
                    {"id": "cancel"},
310
                ],
311
                "custom_transitions": [print_stickers],
312
                "columns": self.columns.keys(),
313
            }, {
314
                "id": "sample_due",
315
                "title": _("Due"),
316
                "contentFilter": {
317
                    "review_state": (
318
                        "to_be_sampled",
319
                        "to_be_preserved",
320
                        "sample_due"),
321
                    "sort_on": "created",
322
                    "sort_order": "descending"},
323
                "transitions": [
324
                    {"id": "sample"},
325
                    {"id": "preserve"},
326
                    {"id": "receive"},
327
                    {"id": "cancel"},
328
                    {"id": "reinstate"},
329
                ],
330
                "custom_transitions": [print_stickers],
331
                "columns": self.columns.keys(),
332
            }, {
333
                "id": "sample_received",
334
                "title": _("Received"),
335
                "contentFilter": {
336
                    "review_state": "sample_received",
337
                    "sort_on": "created",
338
                    "sort_order": "descending",
339
                },
340
                "transitions": [
341
                    {"id": "prepublish"},
342
                    {"id": "cancel"},
343
                    {"id": "reinstate"},
344
                ],
345
                "custom_transitions": [print_stickers],
346
                "columns": self.columns.keys(),
347
            }, {
348
                "id": "to_be_verified",
349
                "title": _("To be verified"),
350
                "contentFilter": {
351
                    "review_state": "to_be_verified",
352
                    "sort_on": "created",
353
                    "sort_order": "descending",
354
                },
355
                "transitions": [
356
                    {"id": "retract"},
357
                    {"id": "verify"},
358
                    {"id": "prepublish"},
359
                    {"id": "cancel"},
360
                    {"id": "reinstate"},
361
                ],
362
                "custom_transitions": [print_stickers],
363
                "columns": self.columns.keys(),
364
            }, {
365
                "id": "verified",
366
                "title": _("Verified"),
367
                "contentFilter": {
368
                    "review_state": "verified",
369
                    "sort_on": "created",
370
                    "sort_order": "descending",
371
                },
372
                "transitions": [
373
                    {"id": "publish"},
374
                    {"id": "cancel"},
375
                ],
376
                "custom_transitions": [print_stickers],
377
                "columns": self.columns.keys(),
378
            }, {
379
                "id": "published",
380
                "title": _("Published"),
381
                "contentFilter": {
382
                    "review_state": ("published"),
383
                    "sort_on": "created",
384
                    "sort_order": "descending",
385
                },
386
                "transitions": [
387
                    {"id": "republish"},
388
                ],
389
                "custom_transitions": [],
390
                "columns": self.columns.keys(),
391
            }, {
392
                "id": "unpublished",
393
                "title": _("Unpublished"),
394
                "contentFilter": {
395
                    "cancellation_state": "active",
396
                    "review_state": (
397
                        "sample_registered",
398
                        "to_be_sampled",
399
                        "to_be_preserved",
400
                        "sample_due",
401
                        "sample_received",
402
                        "to_be_verified",
403
                        "attachment_due",
404
                        "verified",
405
                    ),
406
                    "sort_on": "created",
407
                    "sort_order": "descending",
408
                },
409
                "transitions": [
410
                    {"id": "sample"},
411
                    {"id": "preserve"},
412
                    {"id": "receive"},
413
                    {"id": "retract"},
414
                    {"id": "verify"},
415
                    {"id": "prepublish"},
416
                    {"id": "publish"},
417
                    {"id": "republish"},
418
                    {"id": "cancel"},
419
                    {"id": "reinstate"},
420
                ],
421
                "custom_transitions": [print_stickers],
422
                "columns": self.columns.keys(),
423
            }, {
424
                "id": "cancelled",
425
                "title": _("Cancelled"),
426
                "contentFilter": {
427
                    "cancellation_state": "cancelled",
428
                    "review_state": (
429
                        "sample_registered",
430
                        "to_be_sampled",
431
                        "to_be_preserved",
432
                        "sample_due",
433
                        "sample_received",
434
                        "to_be_verified",
435
                        "attachment_due",
436
                        "verified",
437
                        "published",
438
                    ),
439
                    "sort_on": "created",
440
                    "sort_order": "descending",
441
                },
442
                "transitions": [
443
                    {"id": "reinstate"},
444
                ],
445
                "custom_transitions": [],
446
                "columns": self.columns.keys(),
447
            }, {
448
                "id": "invalid",
449
                "title": _("Invalid"),
450
                "contentFilter": {
451
                    "review_state": "invalid",
452
                    "sort_on": "created",
453
                    "sort_order": "descending",
454
                },
455
                "transitions": [],
456
                "custom_transitions": [print_stickers],
457
                "columns": self.columns.keys(),
458
            }, {
459
                "id": "rejected",
460
                "title": _("Rejected"),
461
                "contentFilter": {
462
                    "review_state": "rejected",
463
                    "sort_on": "created",
464
                    "sort_order": "descending",
465
                },
466
                "transitions": [],
467
                "custom_transitions": [
468
                    {
469
                        "id": "print_stickers",
470
                        "title": _("Print stickers"),
471
                        "url": "workflow_action?action=print_stickers"},
472
                ],
473
                "columns": self.columns.keys(),
474
            }, {
475
                "id": "assigned",
476
                "title": get_image("assigned.png",
477
                                   title=t(_("Assigned"))),
478
                "contentFilter": {
479
                    "assigned_state": "assigned",
480
                    "cancellation_state": "active",
481
                    "review_state": ("sample_received",
482
                                     "attachment_due",),
483
                    "sort_on": "created",
484
                    "sort_order": "descending",
485
                },
486
                "transitions": [
487
                    {"id": "receive"},
488
                    {"id": "retract"},
489
                    {"id": "prepublish"},
490
                    {"id": "cancel"},
491
                ],
492
                "custom_transitions": [print_stickers],
493
                "columns": self.columns.keys(),
494
            }, {
495
                "id": "unassigned",
496
                "title": get_image("unassigned.png",
497
                                   title=t(_("Unsassigned"))),
498
                "contentFilter": {
499
                    "assigned_state": "unassigned",
500
                    "cancellation_state": "active",
501
                    "review_state": (
502
                        "sample_received",
503
                        "attachment_due",
504
                    ),
505
                    "sort_on": "created",
506
                    "sort_order": "descending",
507
                },
508
                "transitions": [
509
                    {"id": "receive"},
510
                    {"id": "retract"},
511
                    {"id": "prepublish"},
512
                    {"id": "cancel"},
513
                ],
514
                "custom_transitions": [print_stickers],
515
                "columns": self.columns.keys(),
516
            }, {
517
                "id": "late",
518
                "title": get_image("late.png",
519
                                   title=t(_("Late"))),
520
                "contentFilter": {
521
                    "cancellation_state": "active",
522
                    # Query only for unpublished ARs that are late
523
                    "review_state": (
524
                        "sample_received",
525
                        "attachment_due",
526
                        "to_be_verified",
527
                        "verified",
528
                    ),
529
                    "getDueDate": {
530
                        "query": DateTime(),
531
                        "range": "max",
532
                    },
533
                    "sort_on": "created",
534
                    "sort_order": "descending",
535
                },
536
                "custom_transitions": [print_stickers],
537
                "columns": self.columns.keys(),
538
            }
539
        ]
540
541
    def update(self):
542
        """Called before the listing renders
543
        """
544
        super(AnalysisRequestsView, self).update()
545
546
        self.workflow = api.get_tool("portal_workflow")
547
        self.member = self.mtool.getAuthenticatedMember()
548
        self.roles = self.member.getRoles()
549
550
        setup = api.get_bika_setup()
551
552
        # remove `to_be_sampled` filter
553
        if not setup.getSamplingWorkflowEnabled():
554
            self.review_states = filter(
555
                lambda x: x.get("id") != "to_be_sampled", self.review_states)
556
557
        # remove `scheduled_sampling` filter
558
        if not setup.getScheduleSamplingEnabled():
559
            self.review_states = filter(
560
                lambda x: x.get("id") != "scheduled_sampling",
561
                self.review_states)
562
563
        # remove `to_be_preserved` filter
564
        if not setup.getSamplePreservationEnabled():
565
            self.review_states = filter(
566
                lambda x: x.get("id") != "to_be_preserved", self.review_states)
567
568
        # remove `rejected` filter
569
        if not setup.getRejectionReasons():
570
            self.review_states = filter(
571
                lambda x: x.get("id") != "rejected", self.review_states)
572
573
        self.hideclientlink = "RegulatoryInspector" in self.roles \
574
            and "Manager" not in self.roles \
575
            and "LabManager" not in self.roles \
576
            and "LabClerk" not in self.roles
577
578
        if self.context.portal_type == "AnalysisRequestsFolder" and \
579
                (self.mtool.checkPermission(AddAnalysisRequest, self.context)):
580
            self.context_actions[_("Add")] = \
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
581
                {"url": "ar_add?ar_count=1",
582
                 'permission': 'Add portal content',
583
                 "icon": "++resource++bika.lims.images/add.png"}
584
585
        self.editresults = -1
586
        self.clients = {}
587
        # self.user_is_preserver = "Preserver" in self.roles
588
        # Printing workflow enabled?
589
        # If not, remove the Column
590
        self.printwfenabled = \
591
            self.context.bika_setup.getPrintingWorkflowEnabled()
592
        printed_colname = "Printed"
593
        if not self.printwfenabled and printed_colname in self.columns:
594
            # Remove "Printed" columns
595
            del self.columns[printed_colname]
596
            tmprvs = []
597
            for rs in self.review_states:
598
                tmprs = rs
599
                tmprs["columns"] = [c for c in rs.get("columns", []) if
600
                                    c != printed_colname]
601
                tmprvs.append(tmprs)
602
            self.review_states = tmprvs
603
        elif self.printwfenabled:
604
            # Print button to choose multiple ARs and print them.
605
            review_states = []
606
            for review_state in self.review_states:
607
                review_state.get("custom_transitions", []).extend(
608
                    [{"id": "print",
609
                      "title": _("Print"),
610
                      "url": "workflow_action?action=print"}, ])
611
                review_states.append(review_state)
612
            self.review_states = review_states
613
614
        # Only "BIKA: ManageAnalysisRequests" may see the copy to new button.
615
        # elsewhere it is hacked in where required.
616
        if self.copy_to_new_allowed:
617
            review_states = []
618
            for review_state in self.review_states:
619
                review_state.get("custom_transitions", []).extend(
620
                    [{"id": "copy_to_new",
621
                      "title": _("Copy to new"),
622
                      "url": "workflow_action?action=copy_to_new"}, ])
623
                review_states.append(review_state)
624
            self.review_states = review_states
625
626
        # Hide Preservation/Sampling workflow actions if the edit columns
627
        # are not displayed.
628
        toggle_cols = self.get_toggle_cols()
629
        new_states = []
630
        for i, state in enumerate(self.review_states):
631
            if state["id"] == self.review_state:
632
                if "getSampler" not in toggle_cols \
633
                   or "getDateSampled" not in toggle_cols:
634
                    if "hide_transitions" in state:
635
                        state["hide_transitions"].append("sample")
636
                    else:
637
                        state["hide_transitions"] = ["sample", ]
638
                if "getPreserver" not in toggle_cols \
639
                   or "getDatePreserved" not in toggle_cols:
640
                    if "hide_transitions" in state:
641
                        state["hide_transitions"].append("preserve")
642
                    else:
643
                        state["hide_transitions"] = ["preserve", ]
644
            new_states.append(state)
645
        self.review_states = new_states
646
647
    def before_render(self):
648
        """Before template render hook
649
        """
650
        # If the current user is a client contact, display those analysis
651
        # requests that belong to same client only
652
        super(AnalysisRequestsView, self).before_render()
653
        client = api.get_current_client()
654
        if client:
655
            self.contentFilter['path'] = {
656
                "query": "/".join(client.getPhysicalPath()),
657
                "level": 0}
658
            # No need to display the Client column
659
            self.remove_column('Client')
660
661
    def isItemAllowed(self, obj):
662
        """ If Adnvanced Filter bar is enabled, this method checks if the item
663
        matches advanced filter bar criteria
664
        """
665
        return not self.filter_bar_enabled or self.filter_bar_check_item(obj)
666
667
    def folderitems(self, full_objects=False, classic=False):
668
        # We need to get the portal catalog here in roder to save process
669
        # while iterating over folderitems
670
        self.portal_catalog = api.get_tool("portal_catalog")
671
        return BikaListingView.folderitems(self, full_objects, classic)
672
673
    def folderitem(self, obj, item, index):
674
        # Additional info from AnalysisRequest to be added in the item
675
        # generated by default by bikalisting.
676
        # Call the folderitem method from the base class
677
        item = BikaListingView.folderitem(self, obj, item, index)
678
        if not item:
679
            return None
680
        # This variable will contain the full analysis request if there is
681
        # need to work with the full object instead of the brain
682
        full_object = None
683
        item["Creator"] = self.user_fullname(obj.Creator)
684
        # If we redirect from the folderitems view we should check if the
685
        # user has permissions to medify the element or not.
686
        priority_sort_key = obj.getPrioritySortkey
687
        if not priority_sort_key:
688
            # Default priority is Medium = 3.
689
            # The format of PrioritySortKey is <priority>.<created>
690
            priority_sort_key = "3.%s" % obj.created.ISO8601()
691
        priority = priority_sort_key.split(".")[0]
692
        priority_text = PRIORITIES.getValue(priority)
693
        priority_div = """<div class="priority-ico priority-%s">
694
                          <span class="notext">%s</span><div>
695
                       """
696
        item["replace"]["Priority"] = priority_div % (priority, priority_text)
697
        item["replace"]["getProfilesTitle"] = obj.getProfilesTitleStr
698
699
        analysesnum = obj.getAnalysesNum
700
        if analysesnum:
701
            num_verified = str(analysesnum[0])
702
            num_total = str(analysesnum[1])
703
            item["getAnalysesNum"] = "{0}/{1}".format(num_verified, num_total)
704
        else:
705
            item["getAnalysesNum"] = ""
706
707
        # Progress
708
        num_verified = 0
709
        num_submitted = 0
710
        num_total = 0
711
        if analysesnum and len(analysesnum) > 1:
712
            num_verified = analysesnum[0]
713
            num_total = analysesnum[1]
714
            num_submitted = num_total - num_verified
715
            if len(analysesnum) > 2:
716
                num_wo_results = analysesnum[2]
717
                num_submitted = num_total - num_verified - num_wo_results
718
        num_steps_total = num_total * 2
719
        num_steps = (num_verified * 2) + (num_submitted)
720
        progress_perc = 0
721
        if num_steps > 0 and num_steps_total > 0:
722
            progress_perc = (num_steps * 100) / num_steps_total
723
        progress = '<div class="progress-bar-container">' + \
724
                   '<div class="progress-bar" style="width:{0}%"></div>' + \
725
                   '<div class="progress-perc">{0}%</div></div>'
726
        item["replace"]["Progress"] = progress.format(progress_perc)
727
728
        item["BatchID"] = obj.getBatchID
729
        if obj.getBatchID:
730
            item['replace']['BatchID'] = "<a href='%s'>%s</a>" % \
731
                (obj.getBatchURL, obj.getBatchID)
732
        # TODO: SubGroup ???
733
        # val = obj.Schema().getField('SubGroup').get(obj)
734
        # item['SubGroup'] = val.Title() if val else ''
735
736
        date = obj.getSamplingDate
737
        item["SamplingDate"] = \
738
            self.ulocalized_time(date, long_format=1) if date else ""
739
        date = obj.getDateReceived
740
        item["getDateReceived"] = \
741
            self.ulocalized_time(date, long_format=1) if date else ""
742
        date = obj.getDueDate
743
        item["getDueDate"] = \
744
            self.ulocalized_time(date, long_format=1) if date else ""
745
        date = obj.getDatePublished
746
        item["getDatePublished"] = \
747
            self.ulocalized_time(date, long_format=1) if date else ""
748
        date = obj.getDateVerified
749
        item["getDateVerified"] = \
750
            self.ulocalized_time(date, long_format=1) if date else ""
751
752
        if self.printwfenabled:
753
            item["Printed"] = ""
754
            printed = obj.getPrinted if hasattr(obj, "getPrinted") else "0"
755
            print_icon = ""
756
            if printed == "0":
757
                print_icon = get_image("delete.png",
758
                                       title=t(_("Not printed yet")))
759
            elif printed == "1":
760
                print_icon = get_image("ok.png",
761
                                       title=t(_("Printed")))
762
            elif printed == "2":
763
                print_icon = get_image(
764
                    "exclamation.png",
765
                    title=t(_("Republished after last print")))
766
            item["after"]["Printed"] = print_icon
767
        item["SamplingDeviation"] = obj.getSamplingDeviationTitle
768
769
        item["getStorageLocation"] = obj.getStorageLocationTitle
770
771
        after_icons = ""
772
        # Getting a dictionary with each workflow id and current state in it
773
        states_dict = obj.getObjectWorkflowStates
774
        if obj.assigned_state == 'assigned':
775
            after_icons += get_image("worksheet.png",
776
                                     title=t(_("All analyses assigned")))
777
        if states_dict.get('review_state', '') == 'invalid':
778
            after_icons += get_image("delete.png",
779
                                     title=t(_("Results have been withdrawn")))
780
781
        due_date = obj.getDueDate
782
        if due_date and due_date < (obj.getDatePublished or DateTime()):
783
            due_date_str = self.ulocalized_time(due_date)
784
            img_title = "{}: {}".format(t(_("Late Analyses")), due_date_str)
785
            after_icons += get_image("late.png", title=img_title)
786
787
        if obj.getSamplingDate and obj.getSamplingDate > DateTime():
788
            after_icons += get_image("calendar.png",
789
                                     title=t(_("Future dated sample")))
790
        if obj.getInvoiceExclude:
791
            after_icons += get_image("invoice_exclude.png",
792
                                     title=t(_("Exclude from invoice")))
793
        if obj.getHazardous:
794
            after_icons += get_image("hazardous.png",
795
                                     title=t(_("Hazardous")))
796
        if after_icons:
797
            item['after']['getId'] = after_icons
798
799
        item['Created'] = self.ulocalized_time(obj.created, long_format=1)
800
        if obj.getContactUID:
801
            item['ClientContact'] = obj.getContactFullName
802
            item['replace']['ClientContact'] = "<a href='%s'>%s</a>" % \
803
                (obj.getContactURL, obj.getContactFullName)
804
        else:
805
            item["ClientContact"] = ""
806
        # TODO-performance: If SamplingWorkflowEnabled, we have to get the
807
        # full object to check the user permissions, so far this is
808
        # a performance hit.
809
        if obj.getSamplingWorkflowEnabled:
810
            # We don't do anything with Sampling Date.
811
            # User can modify Sampling date
812
            # inside AR view. In this listing view,
813
            # we only let the user to edit Date Sampled
814
            # and Sampler if he wants to make 'sample' transaction.
815
            if not obj.getDateSampled:
816
                datesampled = self.ulocalized_time(
817
                    DateTime(), long_format=True)
818
                item["class"]["getDateSampled"] = "provisional"
819
            else:
820
                datesampled = self.ulocalized_time(obj.getDateSampled,
821
                                                   long_format=True)
822
823
            sampler = obj.getSampler
824
            if sampler:
825
                item["replace"]["getSampler"] = obj.getSamplerFullName
826
            if "Sampler" in self.roles and not sampler:
827
                sampler = self.member.id
828
                item["class"]["getSampler"] = "provisional"
829
            # sampling workflow - inline edits for Sampler and Date Sampled
830
            if states_dict.get('review_state', '') == 'to_be_sampled':
831
                # We need to get the full object in order to check
832
                # the permissions
833
                full_object = obj.getObject()
834
                checkPermission =\
835
                    self.context.portal_membership.checkPermission
836
                if checkPermission(SampleSample, full_object):
837
                    item["required"] = ["getSampler", "getDateSampled"]
838
                    item["allow_edit"] = ["getSampler", "getDateSampled"]
839
                    # TODO-performance: hit performance while getting the
840
                    # sample object...
841
                    # TODO Can LabManagers be a Sampler?!
842
                    samplers = getUsers(
843
                        full_object.getSample(),
844
                        ["Sampler", ])
845
                    username = self.member.getUserName()
846
                    users = [({
847
                        "ResultValue": u,
848
                        "ResultText": samplers.getValue(u)}) for u in samplers]
849
                    item['choices'] = {'getSampler': users}
850
                    Sampler = sampler and sampler or \
851
                        (username in samplers.keys() and username) or ''
852
                    sampler = Sampler
853
                else:
854
                    datesampled = self.ulocalized_time(obj.getDateSampled,
855
                                                       long_format=True)
856
                    sampler = obj.getSamplerFullName if obj.getSampler else ''
857
        else:
858
            datesampled = self.ulocalized_time(obj.getDateSampled,
859
                                               long_format=True)
860
            sampler = ""
861
        item["getDateSampled"] = datesampled
862
        item["getSampler"] = sampler
863
864
        # These don't exist on ARs
865
        # XXX This should be a list of preservers...
866
        item["getPreserver"] = ""
867
        item["getDatePreserved"] = ""
868
        # TODO-performance: If inline preservation wants to be used, we
869
        # have to get the full object to check the user permissions, so
870
        # far this is a performance hit.
871
        # inline edits for Preserver and Date Preserved
872
        # if checkPermission(PreserveSample, obj):
873
        #     item['required'] = ['getPreserver', 'getDatePreserved']
874
        #     item['allow_edit'] = ['getPreserver', 'getDatePreserved']
875
        #     preservers = getUsers(obj, ['Preserver',
876
        #                           'LabManager', 'Manager'])
877
        #     username = self.member.getUserName()
878
        #     users = [({'ResultValue': u,
879
        #                'ResultText': preservers.getValue(u)})
880
        #              for u in preservers]
881
        #     item['choices'] = {'getPreserver': users}
882
        #     preserver = username in preservers.keys() and username or ''
883
        #     item['getPreserver'] = preserver
884
        #     item['getDatePreserved'] = self.ulocalized_time(
885
        #         DateTime(),
886
        #         long_format=1)
887
        #     item['class']['getPreserver'] = 'provisional'
888
        #     item['class']['getDatePreserved'] = 'provisional'
889
890
        # Submitting user may not verify results
891
        # Thee conditions to improve performance, some functions to check
892
        # the condition need to get the full analysis request.
893
        if states_dict.get("review_state", "") == "to_be_verified":
894
            allowed = user.has_permission(
895
                VerifyPermission,
896
                username=self.member.getUserName())
897
            # TODO-performance: isUserAllowedToVerify getts all analysis
898
            # objects inside the analysis request.
899
            if allowed:
900
                # Gettin the full object if not get before
901
                full_object = full_object if full_object else obj.getObject()
902
                if not full_object.isUserAllowedToVerify(self.member):
903
                    item["after"]["state_title"] = get_image(
904
                        "submitted-by-current-user.png",
905
                        title=t(_("Cannot verify: Submitted by current user")))
906
        return item
907
908
    @property
909
    def copy_to_new_allowed(self):
910
        mtool = api.get_tool("portal_membership")
911
        if mtool.checkPermission(ManageAnalysisRequests, self.context) \
912
                or mtool.checkPermission(ModifyPortalContent, self.context):
913
            return True
914
        return False
915
916
    def getFilterBar(self):
917
        """
918
        This function creates an instance of BikaListingFilterBar if the
919
        class has not created one yet.
920
        :returns: a BikaListingFilterBar instance
921
        """
922
        self._advfilterbar = self._advfilterbar if self._advfilterbar else \
923
            AnalysisRequestsBikaListingFilterBar(
924
                context=self.context, request=self.request)
925
        return self._advfilterbar
926
927
    def getDefaultAddCount(self):
928
        return self.context.bika_setup.getDefaultNumberOfARsToAdd()
929