Passed
Push — master ( 61fb2b...e9b448 )
by Ramon
05:21
created

AnalysisRequestsView.before_render()   A

Complexity

Conditions 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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