Passed
Push — master ( 5f8981...8742c1 )
by Ramon
04:39
created

FolderView.folderitem()   A

Complexity

Conditions 4

Size

Total Lines 45
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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