Passed
Push — master ( b60410...062e8c )
by Jordi
05:05
created

quests.AnalysisRequestsView.folderitem()   F

Complexity

Conditions 34

Size

Total Lines 186
Code Lines 130

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 130
dl 0
loc 186
rs 0
c 0
b 0
f 0
cc 34
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

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