Passed
Push — master ( 0fd6dc...b85412 )
by Jordi
06:32
created

bika.lims.browser.analysisrequest.analysisrequests.QueuedAnalysisRequestsCount.__call__()   A

Complexity

Conditions 4

Size

Total Lines 16
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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