Passed
Pull Request — 2.x (#1962)
by Ramon
05:25
created

FolderView.get_selected_state()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nop 1
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-2021 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.utils import get_display_list
28
from bika.lims.utils import get_link
29
from bika.lims.utils import get_progress_bar_html
30
from bika.lims.utils import getUsers
31
from bika.lims.utils import user_fullname
32
from Products.Archetypes.config import REFERENCE_CATALOG
33
from Products.CMFCore.utils import getToolByName
34
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
35
from senaite.core.catalog import WORKSHEET_CATALOG
36
from senaite.core.permissions import AddWorksheet
37
from senaite.core.permissions.worksheet import can_add_worksheet
38
from senaite.core.permissions.worksheet import can_edit_worksheet
39
from senaite.core.permissions.worksheet import can_manage_worksheets
40
41
42
class FolderView(BikaListingView):
43
    """Listing view for Worksheets
44
    """
45
    template = ViewPageTemplateFile("../templates/worksheets.pt")
46
47
    def __init__(self, context, request):
48
        super(FolderView, self).__init__(context, request)
49
50
        self.catalog = WORKSHEET_CATALOG
51
        self.contentFilter = {
52
            "review_state": ["open", "to_be_verified", "verified", "rejected"],
53
            "sort_on": "created",
54
            "sort_order": "reverse"
55
        }
56
57
        self.title = self.context.translate(_("Worksheets"))
58
        self.description = ""
59
60
        self.icon = "{}/{}".format(
61
            self.portal_url,
62
            "++plone++senaite.core.static/assets/icons/worksheet.svg"
63
        )
64
65
        self.context_actions = {
66
            _("Add"): {
67
                "url": "worksheet_add",
68
                "icon": "++resource++bika.lims.images/add.png",
69
                "class": "worksheet_add",
70
                "permission": AddWorksheet,
71
            }
72
        }
73
74
        self.show_select_column = True
75
        self.show_select_all_checkbox = True
76
        self.filter_by_user = False
77
        self.selected_state = ""
78
        self.analyst_choices = []
79
        self.can_reassign = False
80
        self.can_manage = False
81
82
        self.rc = getToolByName(self, REFERENCE_CATALOG)
83
84
        # this is a property of self, because self.getAnalysts returns it
85
        self.analysts = getUsers(self, ["Manager", "LabManager", "Analyst"])
86
        self.analysts = self.analysts.sortedByValue()
87
        self.analyst_choices = []
88
        for a in self.analysts:
89
            self.analyst_choices.append({
90
                "ResultValue": a,
91
                "ResultText": self.analysts.getValue(a),
92
            })
93
94
        self.columns = collections.OrderedDict((
95
            ("getProgressPercentage", {
96
               "title": _("Progress")}),
97
            ("Title", {
98
                "title": _("Worksheet"),
99
                "index": "getId"}),
100
            ("Analyst", {
101
                "title": _("Analyst"),
102
                "index": "getAnalyst"}),
103
            ("getWorksheetTemplateTitle", {
104
                "title": _("Template"),
105
                "replace_url": "getWorksheetTemplateURL"}),
106
            ("getNumberOfRegularSamples", {
107
                "title": _("Samples")}),
108
            ("getNumberOfQCAnalyses", {
109
                "title": _("QC Analyses")}),
110
            ("getNumberOfRegularAnalyses", {
111
                "title": _("Routine Analyses")}),
112
            ("CreationDate", {
113
                "title": _("Created"),
114
                "index": "created"}),
115
            ("state_title", {
116
                "title": _("State"),
117
                "index": "review_state",
118
                "attr": "state_title"}),
119
        ))
120
        self.review_states = [
121
            {
122
                "id": "default",
123
                "title": _("Active"),
124
                "contentFilter": {
125
                    "review_state": [
126
                        "open",
127
                        "to_be_verified",
128
                    ],
129
                    "sort_on": "CreationDate",
130
                    "sort_order": "reverse"},
131
                "transitions": [],
132
                "custom_transitions": [],
133
                "columns": self.columns.keys(),
134
            }, {
135
                "id": "open",
136
                "title": _("Open"),
137
                "contentFilter": {
138
                    "review_state": "open",
139
                    "sort_on": "CreationDate",
140
                    "sort_order": "reverse"},
141
                "transitions": [],
142
                "custom_transitions": [],
143
                "columns": self.columns.keys(),
144
            }, {
145
                "id": "to_be_verified",
146
                "title": _("To be verified"),
147
                "contentFilter": {
148
                    "review_state": "to_be_verified",
149
                    "sort_on": "CreationDate",
150
                    "sort_order": "reverse"},
151
                "transitions": [],
152
                "custom_transitions": [],
153
                "columns": self.columns.keys()
154
            }, {
155
                "id": "verified",
156
                "title": _("Verified"),
157
                "contentFilter": {
158
                    "review_state": "verified",
159
                    "sort_on": "CreationDate",
160
                    "sort_order": "reverse"
161
                },
162
                "transitions": [],
163
                "custom_transitions": [],
164
                "columns": self.columns.keys(),
165
            }, {
166
                "id": "all",
167
                "title": _("All"),
168
                "contentFilter": {
169
                    "review_state": [
170
                        "open",
171
                        "to_be_verified",
172
                        "verified",
173
                        "rejected",
174
                    ],
175
                    "sort_on":"CreationDate",
176
                    "sort_order": "reverse"},
177
                "transitions":[],
178
                "custom_transitions": [],
179
                "columns": self.columns.keys(),
180
            }, {
181
                # getAuthenticatedMember does not work in __init__ so "mine" is
182
                # configured further in "folderitems" below.
183
                "id": "mine",
184
                "title": _("Mine"),
185
                "contentFilter": {
186
                    "review_state": [
187
                        "open",
188
                        "to_be_verified",
189
                        "verified",
190
                        "rejected"
191
                    ],
192
                    "sort_on":"CreationDate",
193
                    "sort_order": "reverse"},
194
                "transitions":[],
195
                "custom_transitions": [],
196
                "columns": self.columns.keys(),
197
            }
198
        ]
199
200
    def before_render(self):
201
        """Before render hook of the listing base view
202
        """
203
        super(FolderView, self).before_render()
204
205
        # disable the editable border of the Add-, Display- and Workflow menu
206
        self.request.set("disable_border", 1)
207
208
        # the current selected WF state
209
        self.selected_state = self.get_selected_state()
210
211
        self.allow_edit = self.is_edit_allowed()
212
        self.can_manage = self.is_manage_allowed()
213
214
        # Check if analysts can be assigned
215
        if self.is_analyst_assignment_allowed():
216
            self.can_reassign = True
217
            self.allow_analyst_reassignment()
218
219
        if not self.can_manage:
220
            # The current has no prvileges to manage WS.
221
            # Remove the add button
222
            self.context_actions = {}
223
224
        if self.context.bika_setup.getRestrictWorksheetUsersAccess():
225
            # Display only the worksheets assigned to the current user unless
226
            # the user belongs to a privileged role
227
            allowed = ["Manager", "LabManager", "RegulatoryInspector"]
228
            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 224 is False. Are you sure this can never be the case?
Loading history...
229
            self.filter_by_user = len(diff) == 0
230
231
        if self.filter_by_user:
232
            # Remove 'Mine' button and hide 'Analyst' column
233
            del self.review_states[1]  # Mine
234
            self.columns["Analyst"]["toggle"] = False
235
            self.contentFilter["getAnalyst"] = self.member.id
236
            for rvw in self.review_states:
237
                rvw["contentFilter"]["getAnalyst"] = self.member.id
238
239
    def is_analyst_assignment_allowed(self):
240
        """Check if the analyst can be assigned
241
        """
242
        if not self.allow_edit:
243
            return False
244
        if not self.can_manage:
245
            return False
246
        if self.filter_by_user:
247
            return False
248
        return True
249
250
    def allow_analyst_reassignment(self):
251
        """Allow the Analyst reassignment
252
        """
253
        reassing_analyst_transition = {
254
            "id": "reassign",
255
            "title": _("Reassign")}
256
        for rs in self.review_states:
257
            if rs["id"] not in ["default", "mine", "open", "all"]:
258
                continue
259
            rs["custom_transitions"].append(reassing_analyst_transition)
260
        self.show_select_column = True
261
        self.show_workflow_action_buttons = True
262
263
    def can_add(self):
264
        """Check if the user is allowed to add a worksheet
265
        """
266
        return can_add_worksheet(self.context)
267
268
    def is_manage_allowed(self):
269
        """Check if the User is allowed to manage
270
        """
271
        return can_manage_worksheets(self.context)
272
273
    def is_edit_allowed(self):
274
        """Check if edit is allowed
275
        """
276
        return can_edit_worksheet(self.context)
277
278
    def get_selected_state(self):
279
        """Returns the current selected state
280
        """
281
        form_key = "{}_review_state".format(self.form_id)
282
        return self.request.get(form_key, "default")
283
284
    def folderitem(self, obj, item, index):
285
        """Service triggered each time an item is iterated in folderitems.
286
287
        The use of this service prevents the extra-loops in child objects.
288
289
        :obj: the instance of the class to be foldered
290
        :item: dict containing the properties of the object to be used by
291
            the template
292
        :index: current index of the item
293
        """
294
295
        title = api.get_title(obj)
296
        url = api.get_url(obj)
297
298
        item["CreationDate"] = self.ulocalized_time(obj.created)
299
300
        title_link = "{}/{}".format(url, "add_analyses")
301
        if len(obj.getAnalysesUIDs) > 0:
302
            title_link = "{}/{}".format(url, "manage_results")
303
304
        item["Title"] = title
305
        item["replace"]["Title"] = get_link(title_link, value=title)
306
307
        # Total QC Analyses
308
        item["getNumberOfQCAnalyses"] = str(
309
            obj.getNumberOfQCAnalyses)
310
        # Total Routine Analyses
311
        item["getNumberOfRegularAnalyses"] = str(
312
            obj.getNumberOfRegularAnalyses)
313
        # Total Number of Samples
314
        item["getNumberOfRegularSamples"] = str(
315
            obj.getNumberOfRegularSamples)
316
317
        # Progress
318
        progress = obj.getProgressPercentage
319
        progress_bar_html = get_progress_bar_html(progress)
320
        item["replace"]["getProgressPercentage"] = progress_bar_html
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, "senaite_catalog_setup")
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, "senaite_catalog_setup")
394