Passed
Push — master ( d8e2ec...90ae0b )
by Jordi
10:07 queued 04:19
created

build.bika.lims.browser.analysisrequest.analysisrequests   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 751
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 66
eloc 577
dl 0
loc 751
rs 3.12
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A AnalysisRequestsView.getDefaultAddCount() 0 2 1
F AnalysisRequestsView.update() 0 84 18
A AnalysisRequestsView.get_progress_percentage() 0 25 5
F AnalysisRequestsView.folderitem() 0 186 34
A AnalysisRequestsView.copy_to_new_allowed() 0 7 3
A AnalysisRequestsView.folderitems() 0 7 1
A AnalysisRequestsView.before_render() 0 13 2
B AnalysisRequestsView.__init__() 0 386 2

How to fix   Complexity   

Complexity

Complex classes like build.bika.lims.browser.analysisrequest.analysisrequests often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
10
from bika.lims import api
11
from bika.lims import bikaMessageFactory as _
12
from bika.lims.browser.bika_listing import BikaListingView
13
from bika.lims.catalog import CATALOG_ANALYSIS_REQUEST_LISTING
14
from bika.lims.config import PRIORITIES
15
from bika.lims.permissions import AddAnalysisRequest, TransitionSampleSample
16
from bika.lims.permissions import ManageAnalysisRequests
17
from bika.lims.utils import get_image
18
from bika.lims.utils import get_progress_bar_html
19
from bika.lims.utils import getUsers
20
from bika.lims.utils import t
21
from DateTime import DateTime
22
from Products.CMFCore.permissions import ModifyPortalContent
23
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
24
25
26
class AnalysisRequestsView(BikaListingView):
27
    """Listing View for all Analysis Requests in the System
28
    """
29
30
    template = ViewPageTemplateFile("templates/analysisrequests.pt")
31
32
    def __init__(self, context, request):
33
        super(AnalysisRequestsView, self).__init__(context, request)
34
35
        # hide the right column
36
        request.set("disable_plone.rightcolumn", 1)
37
38
        # hide the editable border
39
        if self.context.portal_type == "AnalysisRequestsFolder":
40
            self.request.set("disable_border", 1)
41
42
        # catalog used for the query
43
        self.catalog = CATALOG_ANALYSIS_REQUEST_LISTING
44
45
        self.contentFilter = {
46
            "sort_on": "created",
47
            "sort_order": "descending",
48
            "isRootAncestor": True,  # only root ancestors
49
        }
50
51
        self.context_actions = {}
52
53
        self.allow_edit = True
54
55
        self.show_select_row = False
56
        self.show_select_column = True
57
        self.form_id = "analysisrequests"
58
59
        ar_image_path = "/++resource++bika.lims.images/sample_big.png"
60
        self.icon = "{}{}".format(self.portal_url, ar_image_path)
61
        self.title = self.context.translate(_("Samples"))
62
        self.description = ""
63
64
        SamplingWorkflowEnabled = \
65
            self.context.bika_setup.getSamplingWorkflowEnabled()
66
67
        self.columns = collections.OrderedDict((
68
            ("Priority", {
69
                "title": "",
70
                "index": "getPrioritySortkey",
71
                "sortable": True, }),
72
            ("Progress", {
73
                "title": "Progress",
74
                "sortable": False,
75
                "toggle": True}),
76
            ("getId", {
77
                "title": _("Sample ID"),
78
                "attr": "getId",
79
                "replace_url": "getURL",
80
                "index": "getId"}),
81
            ("getClientOrderNumber", {
82
                "title": _("Client Order"),
83
                "sortable": True,
84
                "toggle": False}),
85
            ("Creator", {
86
                "title": _("Creator"),
87
                "index": "getCreatorFullName",
88
                "sortable": True,
89
                "toggle": True}),
90
            ("Created", {
91
                "title": _("Date Registered"),
92
                "index": "created",
93
                "toggle": False}),
94
            ("SamplingDate", {
95
                "title": _("Expected Sampling Date"),
96
                "index": "getSamplingDate",
97
                "toggle": SamplingWorkflowEnabled}),
98
            ("getDateSampled", {
99
                "title": _("Date Sampled"),
100
                "toggle": True,
101
                "input_class": "datetimepicker_nofuture",
102
                "input_width": "10"}),
103
            ("getDatePreserved", {
104
                "title": _("Date Preserved"),
105
                "toggle": False,
106
                "input_class": "datetimepicker_nofuture",
107
                "input_width": "10",
108
                "sortable": False}),  # no datesort without index
109
            ("getDateReceived", {
110
                "title": _("Date Received"),
111
                "toggle": False}),
112
            ("getDueDate", {
113
                "title": _("Due Date"),
114
                "toggle": False}),
115
            ("getDateVerified", {
116
                "title": _("Date Verified"),
117
                "input_width": "10",
118
                "toggle": False}),
119
            ("getDatePublished", {
120
                "title": _("Date Published"),
121
                "toggle": False}),
122
            ("BatchID", {
123
                "title": _("Batch ID"),
124
                "index": "getBatchID",
125
                "sortable": True,
126
                "toggle": False}),
127
            ("Client", {
128
                "title": _("Client"),
129
                "index": "getClientTitle",
130
                "attr": "getClientTitle",
131
                "replace_url": "getClientURL",
132
                "toggle": True}),
133
            ("ClientID", {
134
                "title": _("Client ID"),
135
                "index": "getClientID",
136
                "attr": "getClientID",
137
                "replace_url": "getClientURL",
138
                "toggle": True}),
139
            ("Province", {
140
                "title": _("Province"),
141
                "sortable": True,
142
                "index": "getProvince",
143
                "attr": "getProvince",
144
                "toggle": False}),
145
            ("District", {
146
                "title": _("District"),
147
                "sortable": True,
148
                "index": "getDistrict",
149
                "attr": "getDistrict",
150
                "toggle": False}),
151
            ("getClientReference", {
152
                "title": _("Client Ref"),
153
                "sortable": True,
154
                "index": "getClientReference",
155
                "toggle": False}),
156
            ("getClientSampleID", {
157
                "title": _("Client SID"),
158
                "toggle": False}),
159
            ("ClientContact", {
160
                "title": _("Contact"),
161
                "sortable": True,
162
                "index": "getContactFullName",
163
                "toggle": False}),
164
            ("getSampleTypeTitle", {
165
                "title": _("Sample Type"),
166
                "sortable": True,
167
                "toggle": True}),
168
            ("getSamplePointTitle", {
169
                "title": _("Sample Point"),
170
                "sortable": True,
171
                "index": "getSamplePointTitle",
172
                "toggle": False}),
173
            ("getStorageLocation", {
174
                "title": _("Storage Location"),
175
                "sortable": True,
176
                "index": "getStorageLocationTitle",
177
                "toggle": False}),
178
            ("SamplingDeviation", {
179
                "title": _("Sampling Deviation"),
180
                "sortable": True,
181
                "index": "getSamplingDeviationTitle",
182
                "toggle": False}),
183
            ("getSampler", {
184
                "title": _("Sampler"),
185
                "toggle": SamplingWorkflowEnabled}),
186
            ("getPreserver", {
187
                "title": _("Preserver"),
188
                "sortable": False,
189
                "toggle": False}),
190
            ("getProfilesTitle", {
191
                "title": _("Profile"),
192
                "sortable": True,
193
                "index": "getProfilesTitle",
194
                "toggle": False}),
195
            ("getAnalysesNum", {
196
                "title": _("Number of Analyses"),
197
                "sortable": True,
198
                "index": "getAnalysesNum",
199
                "toggle": False}),
200
            ("getTemplateTitle", {
201
                "title": _("Template"),
202
                "sortable": True,
203
                "index": "getTemplateTitle",
204
                "toggle": False}),
205
            ("Printed", {
206
                "title": _("Printed"),
207
                "sortable": False,
208
                "index": "getPrinted",
209
                "toggle": False}),
210
            ("state_title", {
211
                "title": _("State"),
212
                "sortable": True,
213
                "index": "review_state"}),
214
        ))
215
216
        # custom print transition
217
        print_stickers = {
218
            "id": "print_stickers",
219
            "title": _("Print stickers"),
220
            "url": "workflow_action?action=print_stickers"
221
        }
222
223
        self.review_states = [
224
            {
225
                "id": "default",
226
                "title": _("Active"),
227
                "contentFilter": {
228
                    "review_state": (
229
                        "sample_registered",
230
                        "scheduled_sampling",
231
                        "to_be_sampled",
232
                        "sample_due",
233
                        "sample_received",
234
                        "attachment_due",
235
                        "to_be_preserved",
236
                        "to_be_verified",
237
                        "verified",
238
                    ),
239
                    "sort_on": "created",
240
                    "sort_order": "descending",
241
                },
242
                "custom_transitions": [print_stickers],
243
                "columns": self.columns.keys(),
244
            }, {
245
                "id": "to_be_sampled",
246
                "title": _("To Be Sampled"),
247
                "contentFilter": {
248
                    "review_state": ("to_be_sampled",),
249
                    "sort_on": "created",
250
                    "sort_order": "descending"},
251
                "custom_transitions": [print_stickers],
252
                "columns": self.columns.keys()
253
            }, {
254
                "id": "to_be_preserved",
255
                "title": _("To Be Preserved"),
256
                "contentFilter": {
257
                    "review_state": ("to_be_preserved",),
258
                    "sort_on": "created",
259
                    "sort_order": "descending",
260
                },
261
                "custom_transitions": [print_stickers],
262
                "columns": self.columns.keys(),
263
            }, {
264
                "id": "scheduled_sampling",
265
                "title": _("Scheduled sampling"),
266
                "contentFilter": {
267
                    "review_state": ("scheduled_sampling",),
268
                    "sort_on": "created",
269
                    "sort_order": "descending",
270
                },
271
                "custom_transitions": [print_stickers],
272
                "columns": self.columns.keys(),
273
            }, {
274
                "id": "sample_due",
275
                "title": _("Due"),
276
                "contentFilter": {
277
                    "review_state": (
278
                        "to_be_sampled",
279
                        "to_be_preserved",
280
                        "sample_due"),
281
                    "sort_on": "created",
282
                    "sort_order": "descending"},
283
                "custom_transitions": [print_stickers],
284
                "columns": self.columns.keys(),
285
            }, {
286
                "id": "sample_received",
287
                "title": _("Received"),
288
                "contentFilter": {
289
                    "review_state": "sample_received",
290
                    "sort_on": "created",
291
                    "sort_order": "descending",
292
                },
293
                "custom_transitions": [print_stickers],
294
                "columns": self.columns.keys(),
295
            }, {
296
                "id": "to_be_verified",
297
                "title": _("To be verified"),
298
                "contentFilter": {
299
                    "review_state": "to_be_verified",
300
                    "sort_on": "created",
301
                    "sort_order": "descending",
302
                },
303
                "custom_transitions": [print_stickers],
304
                "columns": self.columns.keys(),
305
            }, {
306
                "id": "verified",
307
                "title": _("Verified"),
308
                "contentFilter": {
309
                    "review_state": "verified",
310
                    "sort_on": "created",
311
                    "sort_order": "descending",
312
                },
313
                "custom_transitions": [print_stickers],
314
                "columns": self.columns.keys(),
315
            }, {
316
                "id": "published",
317
                "title": _("Published"),
318
                "contentFilter": {
319
                    "review_state": ("published"),
320
                    "sort_on": "created",
321
                    "sort_order": "descending",
322
                },
323
                "custom_transitions": [],
324
                "columns": self.columns.keys(),
325
            }, {
326
                "id": "cancelled",
327
                "title": _("Cancelled"),
328
                "contentFilter": {
329
                    "review_state": "cancelled",
330
                    "sort_on": "created",
331
                    "sort_order": "descending",
332
                },
333
                "custom_transitions": [],
334
                "columns": self.columns.keys(),
335
            }, {
336
                "id": "invalid",
337
                "title": _("Invalid"),
338
                "contentFilter": {
339
                    "review_state": "invalid",
340
                    "sort_on": "created",
341
                    "sort_order": "descending",
342
                },
343
                "custom_transitions": [print_stickers],
344
                "columns": self.columns.keys(),
345
            }, {
346
                "id": "all",
347
                "title": _("All"),
348
                "contentFilter": {
349
                    "sort_on": "created",
350
                    "sort_order": "descending",
351
                },
352
                "custom_transitions": [print_stickers],
353
                "columns": self.columns.keys(),
354
            }, {
355
                "id": "rejected",
356
                "title": _("Rejected"),
357
                "contentFilter": {
358
                    "review_state": "rejected",
359
                    "sort_on": "created",
360
                    "sort_order": "descending",
361
                },
362
                "custom_transitions": [
363
                    {
364
                        "id": "print_stickers",
365
                        "title": _("Print stickers"),
366
                        "url": "workflow_action?action=print_stickers"},
367
                ],
368
                "columns": self.columns.keys(),
369
            }, {
370
                "id": "assigned",
371
                "title": get_image("assigned.png",
372
                                   title=t(_("Assigned"))),
373
                "contentFilter": {
374
                    "assigned_state": "assigned",
375
                    "review_state": ("sample_received",
376
                                     "attachment_due",),
377
                    "sort_on": "created",
378
                    "sort_order": "descending",
379
                },
380
                "custom_transitions": [print_stickers],
381
                "columns": self.columns.keys(),
382
            }, {
383
                "id": "unassigned",
384
                "title": get_image("unassigned.png",
385
                                   title=t(_("Unsassigned"))),
386
                "contentFilter": {
387
                    "assigned_state": "unassigned",
388
                    "review_state": (
389
                        "sample_received",
390
                        "attachment_due",
391
                    ),
392
                    "sort_on": "created",
393
                    "sort_order": "descending",
394
                },
395
                "custom_transitions": [print_stickers],
396
                "columns": self.columns.keys(),
397
            }, {
398
                "id": "late",
399
                "title": get_image("late.png",
400
                                   title=t(_("Late"))),
401
                "contentFilter": {
402
                    # Query only for unpublished ARs that are late
403
                    "review_state": (
404
                        "sample_received",
405
                        "attachment_due",
406
                        "to_be_verified",
407
                        "verified",
408
                    ),
409
                    "getDueDate": {
410
                        "query": DateTime(),
411
                        "range": "max",
412
                    },
413
                    "sort_on": "created",
414
                    "sort_order": "descending",
415
                },
416
                "custom_transitions": [print_stickers],
417
                "columns": self.columns.keys(),
418
            }
419
        ]
420
421
    def update(self):
422
        """Called before the listing renders
423
        """
424
        super(AnalysisRequestsView, self).update()
425
426
        self.workflow = api.get_tool("portal_workflow")
427
        self.member = self.mtool.getAuthenticatedMember()
428
        self.roles = self.member.getRoles()
429
430
        setup = api.get_bika_setup()
431
432
        # remove `to_be_sampled` filter
433
        if not setup.getSamplingWorkflowEnabled():
434
            self.review_states = filter(
435
                lambda x: x.get("id") != "to_be_sampled", self.review_states)
436
437
        # remove `scheduled_sampling` filter
438
        if not setup.getScheduleSamplingEnabled():
439
            self.review_states = filter(
440
                lambda x: x.get("id") != "scheduled_sampling",
441
                self.review_states)
442
443
        # remove `to_be_preserved` filter
444
        if not setup.getSamplePreservationEnabled():
445
            self.review_states = filter(
446
                lambda x: x.get("id") != "to_be_preserved", self.review_states)
447
448
        # remove `rejected` filter
449
        if not setup.getRejectionReasons():
450
            self.review_states = filter(
451
                lambda x: x.get("id") != "rejected", self.review_states)
452
453
        self.hideclientlink = "RegulatoryInspector" in self.roles \
454
            and "Manager" not in self.roles \
455
            and "LabManager" not in self.roles \
456
            and "LabClerk" not in self.roles
457
458
        if self.context.portal_type == "AnalysisRequestsFolder" and \
459
                (self.mtool.checkPermission(AddAnalysisRequest, self.context)):
460
            self.context_actions[_("Add")] = \
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable _ does not seem to be defined.
Loading history...
461
                {"url": "ar_add?ar_count=1",
462
                 'permission': 'Add portal content',
463
                 "icon": "++resource++bika.lims.images/add.png"}
464
465
        self.editresults = -1
466
        self.clients = {}
467
        # self.user_is_preserver = "Preserver" in self.roles
468
        # Printing workflow enabled?
469
        # If not, remove the Column
470
        self.printwfenabled = \
471
            self.context.bika_setup.getPrintingWorkflowEnabled()
472
        printed_colname = "Printed"
473
        if not self.printwfenabled and printed_colname in self.columns:
474
            # Remove "Printed" columns
475
            del self.columns[printed_colname]
476
            tmprvs = []
477
            for rs in self.review_states:
478
                tmprs = rs
479
                tmprs["columns"] = [c for c in rs.get("columns", []) if
480
                                    c != printed_colname]
481
                tmprvs.append(tmprs)
482
            self.review_states = tmprvs
483
        elif self.printwfenabled:
484
            # Print button to choose multiple ARs and print them.
485
            review_states = []
486
            for review_state in self.review_states:
487
                review_state.get("custom_transitions", []).extend(
488
                    [{"id": "print_sample",
489
                      "title": _("Print"),
490
                      "url": "workflow_action?action=print_sample"}, ])
491
                review_states.append(review_state)
492
            self.review_states = review_states
493
494
        # Only "senaite.core: ManageAnalysisRequests" may see the copy to new button.
495
        # elsewhere it is hacked in where required.
496
        if self.copy_to_new_allowed:
497
            review_states = []
498
            for review_state in self.review_states:
499
                review_state.get("custom_transitions", []).extend(
500
                    [{"id": "copy_to_new",
501
                      "title": _("Copy to new"),
502
                      "url": "workflow_action?action=copy_to_new"}, ])
503
                review_states.append(review_state)
504
            self.review_states = review_states
505
506
    def before_render(self):
507
        """Before template render hook
508
        """
509
        # If the current user is a client contact, display those analysis
510
        # requests that belong to same client only
511
        super(AnalysisRequestsView, self).before_render()
512
        client = api.get_current_client()
513
        if client:
514
            self.contentFilter['path'] = {
515
                "query": "/".join(client.getPhysicalPath()),
516
                "level": 0}
517
            # No need to display the Client column
518
            self.remove_column('Client')
519
520
    def folderitems(self, full_objects=False, classic=False):
521
        # We need to get the portal catalog here in roder to save process
522
        # while iterating over folderitems
523
        self.portal_catalog = api.get_tool("portal_catalog")
524
        items = BikaListingView.folderitems(self, full_objects, classic)
525
        # Keep the original sorting, but display partitions below parents
526
        return items
527
528
    def folderitem(self, obj, item, index):
529
        # Additional info from AnalysisRequest to be added in the item
530
        # generated by default by bikalisting.
531
        # Call the folderitem method from the base class
532
        item = BikaListingView.folderitem(self, obj, item, index)
533
        if not item:
534
            return None
535
        # This variable will contain the full analysis request if there is
536
        # need to work with the full object instead of the brain
537
        full_object = None
538
        item["Creator"] = self.user_fullname(obj.Creator)
539
        # If we redirect from the folderitems view we should check if the
540
        # user has permissions to medify the element or not.
541
        priority_sort_key = obj.getPrioritySortkey
542
        if not priority_sort_key:
543
            # Default priority is Medium = 3.
544
            # The format of PrioritySortKey is <priority>.<created>
545
            priority_sort_key = "3.%s" % obj.created.ISO8601()
546
        priority = priority_sort_key.split(".")[0]
547
        priority_text = PRIORITIES.getValue(priority)
548
        priority_div = """<div class="priority-ico priority-%s">
549
                          <span class="notext">%s</span><div>
550
                       """
551
        item["replace"]["Priority"] = priority_div % (priority, priority_text)
552
        item["replace"]["getProfilesTitle"] = obj.getProfilesTitleStr
553
554
        analysesnum = obj.getAnalysesNum
555
        if analysesnum:
556
            num_verified = str(analysesnum[0])
557
            num_total = str(analysesnum[1])
558
            item["getAnalysesNum"] = "{0}/{1}".format(num_verified, num_total)
559
        else:
560
            item["getAnalysesNum"] = ""
561
562
        # Progress
563
        progress_perc = self.get_progress_percentage(obj)
564
        item["replace"]["Progress"] = get_progress_bar_html(progress_perc)
565
566
        item["BatchID"] = obj.getBatchID
567
        if obj.getBatchID:
568
            item['replace']['BatchID'] = "<a href='%s'>%s</a>" % \
569
                (obj.getBatchURL, obj.getBatchID)
570
        # TODO: SubGroup ???
571
        # val = obj.Schema().getField('SubGroup').get(obj)
572
        # item['SubGroup'] = val.Title() if val else ''
573
574
        date = obj.getSamplingDate
575
        item["SamplingDate"] = \
576
            self.ulocalized_time(date, long_format=1) if date else ""
577
        date = obj.getDateReceived
578
        item["getDateReceived"] = \
579
            self.ulocalized_time(date, long_format=1) if date else ""
580
        date = obj.getDueDate
581
        item["getDueDate"] = \
582
            self.ulocalized_time(date, long_format=1) if date else ""
583
        date = obj.getDatePublished
584
        item["getDatePublished"] = \
585
            self.ulocalized_time(date, long_format=1) if date else ""
586
        date = obj.getDateVerified
587
        item["getDateVerified"] = \
588
            self.ulocalized_time(date, long_format=1) if date else ""
589
590
        if self.printwfenabled:
591
            item["Printed"] = ""
592
            printed = obj.getPrinted if hasattr(obj, "getPrinted") else "0"
593
            print_icon = ""
594
            if printed == "0":
595
                print_icon = get_image("delete.png",
596
                                       title=t(_("Not printed yet")))
597
            elif printed == "1":
598
                print_icon = get_image("ok.png",
599
                                       title=t(_("Printed")))
600
            elif printed == "2":
601
                print_icon = get_image(
602
                    "exclamation.png",
603
                    title=t(_("Republished after last print")))
604
            item["after"]["Printed"] = print_icon
605
        item["SamplingDeviation"] = obj.getSamplingDeviationTitle
606
607
        item["getStorageLocation"] = obj.getStorageLocationTitle
608
609
        after_icons = ""
610
        # Getting a dictionary with each workflow id and current state in it
611
        states_dict = obj.getObjectWorkflowStates
612
        if obj.assigned_state == 'assigned':
613
            after_icons += get_image("worksheet.png",
614
                                     title=t(_("All analyses assigned")))
615
        if states_dict.get('review_state', '') == 'invalid':
616
            after_icons += get_image("delete.png",
617
                                     title=t(_("Results have been withdrawn")))
618
619
        due_date = obj.getDueDate
620
        if due_date and due_date < (obj.getDatePublished or DateTime()):
621
            due_date_str = self.ulocalized_time(due_date)
622
            img_title = "{}: {}".format(t(_("Late Analyses")), due_date_str)
623
            after_icons += get_image("late.png", title=img_title)
624
625
        if obj.getSamplingDate and obj.getSamplingDate > DateTime():
626
            after_icons += get_image("calendar.png",
627
                                     title=t(_("Future dated sample")))
628
        if obj.getInvoiceExclude:
629
            after_icons += get_image("invoice_exclude.png",
630
                                     title=t(_("Exclude from invoice")))
631
        if obj.getHazardous:
632
            after_icons += get_image("hazardous.png",
633
                                     title=t(_("Hazardous")))
634
        if after_icons:
635
            item['after']['getId'] = after_icons
636
637
        item['Created'] = self.ulocalized_time(obj.created, long_format=1)
638
        if obj.getContactUID:
639
            item['ClientContact'] = obj.getContactFullName
640
            item['replace']['ClientContact'] = "<a href='%s'>%s</a>" % \
641
                (obj.getContactURL, obj.getContactFullName)
642
        else:
643
            item["ClientContact"] = ""
644
        # TODO-performance: If SamplingWorkflowEnabled, we have to get the
645
        # full object to check the user permissions, so far this is
646
        # a performance hit.
647
        if obj.getSamplingWorkflowEnabled:
648
            # We don't do anything with Sampling Date.
649
            # User can modify Sampling date
650
            # inside AR view. In this listing view,
651
            # we only let the user to edit Date Sampled
652
            # and Sampler if he wants to make 'sample' transaction.
653
            if not obj.getDateSampled:
654
                datesampled = self.ulocalized_time(
655
                    DateTime(), long_format=True)
656
                item["class"]["getDateSampled"] = "provisional"
657
            else:
658
                datesampled = self.ulocalized_time(obj.getDateSampled,
659
                                                   long_format=True)
660
661
            sampler = obj.getSampler
662
            if sampler:
663
                item["replace"]["getSampler"] = obj.getSamplerFullName
664
            if "Sampler" in self.roles and not sampler:
665
                sampler = self.member.id
666
                item["class"]["getSampler"] = "provisional"
667
            # sampling workflow - inline edits for Sampler and Date Sampled
668
            if states_dict.get('review_state', '') == 'to_be_sampled':
669
                # We need to get the full object in order to check
670
                # the permissions
671
                full_object = obj.getObject()
672
                checkPermission =\
673
                    self.context.portal_membership.checkPermission
674
675
                # TODO Do we really need this check?
676
                if checkPermission(TransitionSampleSample, full_object):
677
                    item["required"] = ["getSampler", "getDateSampled"]
678
                    item["allow_edit"] = ["getSampler", "getDateSampled"]
679
                    # TODO-performance: hit performance while getting the
680
                    # sample object...
681
                    # TODO Can LabManagers be a Sampler?!
682
                    samplers = getUsers(full_object, ["Sampler", ])
683
                    username = self.member.getUserName()
684
                    users = [({
685
                        "ResultValue": u,
686
                        "ResultText": samplers.getValue(u)}) for u in samplers]
687
                    item['choices'] = {'getSampler': users}
688
                    Sampler = sampler and sampler or \
689
                        (username in samplers.keys() and username) or ''
690
                    sampler = Sampler
691
                else:
692
                    datesampled = self.ulocalized_time(obj.getDateSampled,
693
                                                       long_format=True)
694
                    sampler = obj.getSamplerFullName if obj.getSampler else ''
695
        else:
696
            datesampled = self.ulocalized_time(obj.getDateSampled,
697
                                               long_format=True)
698
            sampler = ""
699
        item["getDateSampled"] = datesampled
700
        item["getSampler"] = sampler
701
702
        # These don't exist on ARs
703
        # XXX This should be a list of preservers...
704
        item["getPreserver"] = ""
705
        item["getDatePreserved"] = ""
706
707
        # Advanced partitioning
708
        # append the UID of the primary AR as parent
709
        item["parent"] = obj.getRawParentAnalysisRequest or ""
710
        # append partition UIDs of this AR as children
711
        item["children"] = obj.getDescendantsUIDs or []
712
713
        return item
714
715
    def get_progress_percentage(self, ar_brain):
716
        """Returns the percentage of completeness of the Analysis Request
717
        """
718
        review_state = ar_brain.review_state
719
        if review_state == "published":
720
            return 100
721
722
        numbers = ar_brain.getAnalysesNum
723
724
        num_analyses = numbers[1] or 0
725
        if not num_analyses:
726
            return 0
727
728
        # [verified, total, not_submitted, to_be_verified]
729
        num_to_be_verified = numbers[3] or 0
730
        num_verified = numbers[0] or 0
731
732
        # 2 steps per analysis (submit, verify) plus one step for publish
733
        max_num_steps = (num_analyses * 2) + 1
734
        num_steps = num_to_be_verified + (num_verified * 2)
735
        if not num_steps:
736
            return 0
737
        if num_steps > max_num_steps:
738
            return 100
739
        return (num_steps * 100) / max_num_steps
740
741
    @property
742
    def copy_to_new_allowed(self):
743
        mtool = api.get_tool("portal_membership")
744
        if mtool.checkPermission(ManageAnalysisRequests, self.context) \
745
                or mtool.checkPermission(ModifyPortalContent, self.context):
746
            return True
747
        return False
748
749
    def getDefaultAddCount(self):
750
        return self.context.bika_setup.getDefaultNumberOfARsToAdd()
751