Passed
Push — master ( cd3c58...e71d5a )
by Jordi
04:38
created

FolderView.before_render()   B

Complexity

Conditions 7

Size

Total Lines 38
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 38
rs 7.9759
c 0
b 0
f 0
cc 7
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
11
from bika.lims import api
12
from bika.lims import bikaMessageFactory as _
13
from bika.lims.browser.bika_listing import BikaListingView
14
from bika.lims.catalog import CATALOG_WORKSHEET_LISTING
15
from bika.lims.permissions import EditWorksheet
16
from bika.lims.permissions import ManageWorksheets
17
from bika.lims.utils import get_display_list
18
from bika.lims.utils import get_link
19
from bika.lims.utils import getUsers
20
from bika.lims.utils import user_fullname
21
from Products.Archetypes.config import REFERENCE_CATALOG
22
from Products.CMFCore.utils import getToolByName
23
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
24
25
26
class FolderView(BikaListingView):
27
    """Listing view for Worksheets
28
    """
29
    template = ViewPageTemplateFile("../templates/worksheets.pt")
30
31
    def __init__(self, context, request):
32
        super(FolderView, self).__init__(context, request)
33
34
        self.catalog = CATALOG_WORKSHEET_LISTING
35
        self.contentFilter = {
36
            "review_state": ["open", "to_be_verified", "verified", "rejected"],
37
            "sort_on": "created",
38
            "sort_order": "reverse"
39
        }
40
41
        self.title = self.context.translate(_("Worksheets"))
42
        self.description = ""
43
44
        self.icon = "{}/{}".format(
45
            self.portal_url,
46
            "++resource++bika.lims.images/worksheet_big.png"
47
        )
48
49
        self.context_actions = {
50
            _("Add"): {
51
                "url": "createObject?type_name=InstrumentMaintenanceTask",
52
                "icon": "++resource++bika.lims.images/add.png"}
53
        }
54
55
        self.context_actions = {
56
            _("Add"): {
57
                "url": "worksheet_add",
58
                "icon": "++resource++bika.lims.images/add.png",
59
                "class": "worksheet_add"}
60
        }
61
62
        self.show_select_column = True
63
        self.show_select_all_checkbox = True
64
        self.filter_by_user = False
65
        self.selected_state = ""
66
        self.analyst_choices = []
67
        self.can_reassign = False
68
        self.can_manage = False
69
70
        self.rc = getToolByName(self, REFERENCE_CATALOG)
71
72
        # this is a property of self, because self.getAnalysts returns it
73
        self.analysts = getUsers(self, ["Manager", "LabManager", "Analyst"])
74
        self.analysts = self.analysts.sortedByValue()
75
        self.analyst_choices = []
76
        for a in self.analysts:
77
            self.analyst_choices.append({
78
                "ResultValue": a,
79
                "ResultText": self.analysts.getValue(a),
80
            })
81
82
        self.columns = collections.OrderedDict((
83
            ("Title", {
84
                "title": _("Worksheet"),
85
                "index": "getId"}),
86
            ("Analyst", {
87
                "title": _("Analyst"),
88
                "index": "getAnalyst"}),
89
            ("Template", {
90
                "title": _("Template"),
91
                "attr": "getWorksheetTemplateTitle",
92
                "replace_url": "getWorksheetTemplateURL"}),
93
            ("NumRegularSamples", {
94
                "title": _("Samples")}),
95
            ("NumQCAnalyses", {
96
                "title": _("QC Analyses")}),
97
            ("NumRegularAnalyses", {
98
                "title": _("Routine Analyses")}),
99
            ("CreationDate", {
100
                "title": _("Created"),
101
                "index": "created"}),
102
            ("state_title", {
103
                "title": _("State"),
104
                "index": "review_state",
105
                "attr": "state_title"}),
106
        ))
107
        self.review_states = [
108
            {
109
                "id": "default",
110
                "title": _("All"),
111
                "contentFilter": {
112
                    "review_state": [
113
                        "open",
114
                        "to_be_verified",
115
                        "verified"
116
                    ],
117
                    "sort_on":"CreationDate",
118
                    "sort_order": "reverse"},
119
                "transitions":[
120
                    {"id": "retract"},
121
                    {"id": "verify"},
122
                    {"id": "reject"}
123
                ],
124
                "custom_transitions": [],
125
                "columns": self.columns.keys(),
126
            }, {
127
                # getAuthenticatedMember does not work in __init__ so "mine" is
128
                # configured further in "folderitems" below.
129
                "id": "mine",
130
                "title": _("Mine"),
131
                "contentFilter": {
132
                    "review_state": [
133
                        "open",
134
                        "to_be_verified",
135
                        "verified",
136
                        "rejected"
137
                    ],
138
                    "sort_on":"CreationDate",
139
                    "sort_order": "reverse"},
140
                "transitions":[
141
                    {"id": "retract"},
142
                    {"id": "verify"},
143
                    {"id": "reject"}
144
                ],
145
                "custom_transitions": [],
146
                "columns": self.columns.keys(),
147
            }, {
148
                "id": "open",
149
                "title": _("Open"),
150
                "contentFilter": {
151
                    "review_state": "open",
152
                    "sort_on": "CreationDate",
153
                    "sort_order": "reverse"},
154
                "transitions": [],
155
                "custom_transitions": [],
156
                "columns": self.columns.keys(),
157
            }, {
158
                "id": "to_be_verified",
159
                "title": _("To be verified"),
160
                "contentFilter": {
161
                    "review_state": "to_be_verified",
162
                    "sort_on": "CreationDate",
163
                    "sort_order": "reverse"},
164
                "transitions": [
165
                    {"id": "retract"},
166
                    {"id": "verify"},
167
                    {"id": "reject"}
168
                ],
169
                "custom_transitions": [],
170
                "columns": self.columns.keys()
171
            }, {
172
                "id": "verified",
173
                "title": _("Verified"),
174
                "contentFilter": {
175
                    "review_state": "verified",
176
                    "sort_on": "CreationDate",
177
                    "sort_order": "reverse"
178
                },
179
                "transitions": [],
180
                "custom_transitions": [],
181
                "columns": self.columns.keys(),
182
            }
183
        ]
184
185
    def before_render(self):
186
        """Before render hook of the listing base view
187
        """
188
        super(FolderView, self).before_render()
189
190
        # disable the editable border of the Add-, Display- and Workflow menu
191
        self.request.set("disable_border", 1)
192
193
        # the current selected WF state
194
        self.selected_state = self.get_selected_state()
195
196
        self.allow_edit = self.is_edit_allowed()
197
        self.can_manage = self.is_manage_allowed()
198
199
        # Check if analysts can be assigned
200
        if self.is_analyst_assignment_allowed():
201
            self.can_reassign = True
202
            self.allow_analyst_reassignment()
203
204
        if not self.can_manage:
205
            # The current has no prvileges to manage WS.
206
            # Remove the add button
207
            self.context_actions = {}
208
209
        if self.context.bika_setup.getRestrictWorksheetUsersAccess():
210
            # Display only the worksheets assigned to the current user unless
211
            # the user belongs to a privileged role
212
            allowed = ["Manager", "LabManager", "RegulatoryInspector"]
213
            diff = filter(lambda role: role in allowed, self.member.getRoles())
0 ignored issues
show
introduced by
The variable allowed does not seem to be defined in case self.context.bika_setup....tWorksheetUsersAccess() on line 209 is False. Are you sure this can never be the case?
Loading history...
214
            self.filter_by_user = len(diff) == 0
215
216
        if self.filter_by_user:
217
            # Remove 'Mine' button and hide 'Analyst' column
218
            del self.review_states[1]  # Mine
219
            self.columns["Analyst"]["toggle"] = False
220
            self.contentFilter["getAnalyst"] = self.member.id
221
            for rvw in self.review_states:
222
                rvw["contentFilter"]["getAnalyst"] = self.member.id
223
224
    def is_analyst_assignment_allowed(self):
225
        """Check if the analyst can be assigned
226
        """
227
        if not self.allow_edit:
228
            return False
229
        if not self.can_manage:
230
            return False
231
        if self.filter_by_user:
232
            return False
233
        return True
234
235
    def allow_analyst_reassignment(self):
236
        """Allow the Analyst reassignment
237
        """
238
        reassing_analyst_transition = {
239
            "id": "reassign",
240
            "title": _("Reassign")}
241
        for rs in self.review_states:
242
            if rs["id"] not in ["default", "mine", "open"]:
243
                continue
244
            rs["custom_transitions"].append(reassing_analyst_transition)
245
        self.show_select_column = True
246
        self.show_workflow_action_buttons = True
247
248
    def is_manage_allowed(self):
249
        """Check if the User is allowed to manage
250
        """
251
        checkPermission = self.context.portal_membership.checkPermission
252
        return checkPermission(ManageWorksheets, self.context)
253
254
    def is_edit_allowed(self):
255
        """Check if edit is allowed
256
        """
257
        checkPermission = self.context.portal_membership.checkPermission
258
        return checkPermission(EditWorksheet, self.context)
259
260
    def get_selected_state(self):
261
        """Returns the current selected state
262
        """
263
        form_key = "{}_review_state".format(self.form_id)
264
        return self.request.get(form_key, "default")
265
266
    def folderitems(self):
267
        """Return folderitems as brains
268
        """
269
        return super(FolderView, self).folderitems(classic=False)
270
271
    def folderitem(self, obj, item, index):
272
        """Service triggered each time an item is iterated in folderitems.
273
274
        The use of this service prevents the extra-loops in child objects.
275
276
        :obj: the instance of the class to be foldered
277
        :item: dict containing the properties of the object to be used by
278
            the template
279
        :index: current index of the item
280
        """
281
282
        layout = obj.getLayout
283
        title = api.get_title(obj)
284
        url = api.get_url(obj)
285
286
        item["CreationDate"] = self.ulocalized_time(obj.created)
287
        if len(obj.getAnalysesUIDs) == 0:
288
            item["table_row_class"] = "state-empty-worksheet"
289
290
        title_link = "{}/{}".format(url, "add_analyses")
291
        if len(layout) > 0:
292
            title_link = "{}/{}".format(url, "manage_results")
293
294
        item["Title"] = title
295
        item["replace"]["Title"] = get_link(title_link, value=title)
296
297
        pos_parent = {}
298
        for slot in layout:
299
            # compensate for bad data caused by a stupid bug.
300
            if type(slot["position"]) in (list, tuple):
301
                slot["position"] = slot["position"][0]
302
            if slot["position"] == "new":
303
                continue
304
            if slot["position"] in pos_parent:
305
                continue
306
            pos_parent[slot["position"]] =\
307
                self.rc.lookupObject(slot.get("container_uid"))
308
309
        # Total QC Analyses
310
        item["NumQCAnalyses"] = str(obj.getNumberOfQCAnalyses)
311
        # Total Routine Analyses
312
        item["NumRegularAnalyses"] = str(obj.getNumberOfRegularAnalyses)
313
        # Total Number of Samples
314
        item["NumRegularSamples"] = str(obj.getNumberOfRegularSamples)
315
316
        review_state = item["review_state"]
317
        if self.can_reassign and review_state == "open":
318
            item["Analyst"] = obj.getAnalyst
319
            item["allow_edit"] = ["Analyst"]
320
            item["required"] = ["Analyst"]
321
            item["choices"] = {"Analyst": self.analyst_choices}
322
        else:
323
            fullname = user_fullname(self.context, obj.getAnalyst)
324
            item["Analyst"] = fullname
325
326
        return item
327
328
    def getAnalysts(self):
329
        """Returns all analysts
330
        """
331
        return self.analysts
332
333
    def getWorksheetTemplates(self):
334
        """Returns a DisplayList with all active worksheet templates
335
336
        :return: DisplayList of worksheet templates (uid, title)
337
        :rtype: DisplayList
338
        """
339
        brains = self._get_worksheet_templates_brains()
340
        return get_display_list(brains)
341
342
    def getInstruments(self):
343
        """Returns a DisplayList with all active Instruments
344
345
        :return: DisplayList of worksheet templates (uid, title)
346
        :rtype: DisplayList
347
        """
348
        brains = self._get_instruments_brains()
349
        return get_display_list(brains)
350
351
    def getTemplateInstruments(self):
352
        """Returns worksheet templates as JSON
353
        """
354
        items = dict()
355
        templates = self._get_worksheet_templates_brains()
356
        for template in templates:
357
            template_obj = api.get_object(template)
358
            uid_template = api.get_uid(template_obj)
359
            instrument = template_obj.getInstrument()
360
            uid_instrument = ""
361
            if instrument:
362
                uid_instrument = api.get_uid(instrument)
363
            items[uid_template] = uid_instrument
364
365
        return json.dumps(items)
366
367
    def _get_worksheet_templates_brains(self):
368
        """Returns all active worksheet templates
369
370
        :returns: list of worksheet template brains
371
        """
372
        query = {
373
            "portal_type": "WorksheetTemplate",
374
            "inactive_state": "active",
375
        }
376
        return api.search(query, "bika_setup_catalog")
377
378
    def _get_instruments_brains(self):
379
        """Returns all active Instruments
380
381
        :returns: list of brains
382
        """
383
        query = {
384
            "portal_type": "Instrument",
385
            "inactive_state": "active"
386
        }
387
        return api.search(query, "bika_setup_catalog")
388