Passed
Pull Request — 2.x (#1966)
by Ramon
06:03
created

BatchBookView.update()   A

Complexity

Conditions 3

Size

Total Lines 18
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 18
rs 9.7
c 0
b 0
f 0
cc 3
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 re
22
from collections import OrderedDict
23
from operator import itemgetter
24
25
from bika.lims import api
26
from bika.lims import bikaMessageFactory as _
27
from bika.lims.api.security import check_permission
28
from bika.lims.browser.bika_listing import BikaListingView
29
from bika.lims.interfaces import IBatchBookView
30
from bika.lims.permissions import AddAnalysisRequest
31
from bika.lims.permissions import FieldEditAnalysisResult
32
from bika.lims.permissions import ManageAnalysisRequests
33
from Products.CMFCore.permissions import ModifyPortalContent
34
from Products.CMFPlone import PloneMessageFactory
35
from zope.interface import implementer
36
from bika.lims.utils import t
37
38
39
@implementer(IBatchBookView)
40
class BatchBookView(BikaListingView):
41
42
    def __init__(self, context, request):
43
        super(BatchBookView, self).__init__(context, request)
44
45
        self.context_actions = {}
46
        self.contentFilter = {"sort_on": "created"}
47
        self.title = context.Title()
48
        self.Description = context.Description()
49
50
        self.show_select_column = True
51
        self.pagesize = 999999
52
        self.form_id = "batchbook"
53
        self.show_categories = True
54
        self.expand_all_categories = True
55
        self.show_column_toggles = False
56
57
        self.icon = "{}{}".format(
58
            self.portal_url, "/senaite_theme/icon/batchbook")
59
60
        self.columns = OrderedDict((
61
            ("AnalysisRequest", {
62
                "title": _("Sample"),
63
                "index": "id",
64
                "sortable": True,
65
            }),
66
            ("SampleType", {
67
                "title": _("Sample Type"),
68
                "sortable": True,
69
            }),
70
            ("SamplePoint", {
71
                "title": _("Sample Point"),
72
                "sortable": True,
73
            }),
74
            ("ClientOrderNumber", {
75
                "title": _("Client Order Number"),
76
                "sortable": True,
77
            }),
78
            ("created", {
79
                "title": PloneMessageFactory("Date Created"),
80
                "index": "created",
81
                "toggle": False,
82
            }),
83
            ("state_title", {
84
                "title": _("State"),
85
                "index": "review_state"
86
            }),
87
        ))
88
89
        self.review_states = [
90
            {
91
                "id": "default",
92
                "title": _("All"),
93
                "contentFilter": {},
94
                "columns": self.columns.keys()
95
            },
96
        ]
97
98
    def is_copy_to_new_allowed(self):
99
        if check_permission(ManageAnalysisRequests, self.context) \
100
                or check_permission(ModifyPortalContent, self.context) \
101
                or check_permission(AddAnalysisRequest, self.portal):
102
            return True
103
        return False
104
105
    def can_edit_analysis_result(self, analysis):
106
        """Checks if the current user can edit the analysis result
107
        """
108
        return check_permission(FieldEditAnalysisResult, analysis)
109
110
    def update(self):
111
        """Update hook
112
        """
113
        super(BatchBookView, self).update()
114
        # XXX: Why is this for clients?
115
        if self.is_copy_to_new_allowed():
116
            review_states = []
117
            for review_state in self.review_states:
118
                custom_transitions = review_state.get("custom_transitions", [])
119
                custom_transitions.extend(
120
                    [{"id": "copy_to_new",
121
                      "title": _("Copy to new"),
122
                      "url": "workflow_action?action=copy_to_new"},
123
                     ])
124
                review_state["custom_transitions"] = custom_transitions
125
                review_states.append(review_state)
126
            self.review_states = review_states
127
        self.allow_edit = check_permission(ModifyPortalContent, self.context)
128
129
    def before_render(self):
130
        """Before render hook
131
        """
132
        super(BatchBookView, self).before_render()
133
134
    def folderitems(self):
135
        """Accumulate a list of all AnalysisRequest objects contained in
136
        this Batch, as well as those which are inherited.
137
        """
138
        ars = self.context.getAnalysisRequests(is_active=True)
139
        self.total = len(ars)
140
141
        self.categories = []
142
        analyses = {}
143
        items = []
144
        distinct = []  # distinct analyses (each one a different service)
145
        keywords = []
146
        for ar in ars:
147
            analyses[ar.id] = []
148
            for analysis in ar.getAnalyses(full_objects=True):
149
                analyses[ar.id].append(analysis)
150
                if analysis.getKeyword() not in keywords:
151
                    # we use a keyword check, because versioned services are !=.
152
                    keywords.append(analysis.getKeyword())
153
                    distinct.append(analysis)
154
155
            batchlink = ""
156
            batch = ar.getBatch()
157
            if batch:
158
                batchlink = "<a href='%s'>%s</a>" % (
159
                    batch.absolute_url(), batch.Title())
160
161
            arlink = "<a href='%s'>%s</a>" % (
162
                ar.absolute_url(), ar.Title())
163
164
            subgroup = ar.Schema()["SubGroup"].get(ar)
165
            sub_title = subgroup.Title() if subgroup else t(_("No Subgroup"))
166
            sub_sort = subgroup.getSortKey() if subgroup else "1"
167
            sub_class = re.sub(r"[^A-Za-z\w\d\-\_]", "", sub_title)
168
169
            if [sub_sort, sub_title] not in self.categories:
170
                self.categories.append([sub_sort, sub_title])
171
172
            wf = api.get_tool("portal_workflow")
173
            review_state = api.get_review_status(ar)
174
            state_title = wf.getTitleForStateOnType(
175
                review_state, "AnalysisRequest")
176
177
            item = {
178
                "obj": ar,
179
                "id": ar.id,
180
                "uid": ar.UID(),
181
                "category": sub_title,
182
                "title": ar.Title(),
183
                "type_class": "contenttype-AnalysisRequest",
184
                "url": ar.absolute_url(),
185
                "relative_url": ar.absolute_url(),
186
                "view_url": ar.absolute_url(),
187
                "created": self.ulocalized_time(ar.created(), long_format=1),
188
                "sort_key": ar.created(),
189
                "replace": {
190
                    "Batch": batchlink,
191
                    "AnalysisRequest": arlink,
192
                },
193
                "before": {},
194
                "after": {},
195
                "choices": {},
196
                "class": {"Batch": "Title"},
197
                "state_class": "state-active subgroup_{0}".format(sub_class) if sub_class else "state-active",
198
                "allow_edit": [],
199
                "Batch": "",
200
                "SamplePoint": ar.getSamplePoint().Title() if ar.getSamplePoint() else "",
201
                "SampleType": ar.getSampleType().Title() if ar.getSampleType() else "",
202
                "ClientOrderNumber": ar.getClientOrderNumber(),
203
                "AnalysisRequest": "",
204
                "state_title": state_title,
205
            }
206
            items.append(item)
207
208
        unitstr = '<em class="discreet" style="white-space:nowrap;">%s</em>'
209
210
        # Insert columns for analyses
211
        for d_a in distinct:
212
            keyword = d_a.getKeyword()
213
            short = d_a.getShortTitle()
214
            title = d_a.Title()
215
216
            self.columns[keyword] = {
217
                "title":  short if short else title,
218
                "sortable": False,
219
            }
220
            self.review_states[0]["columns"].insert(
221
                len(self.review_states[0]["columns"]) - 1, keyword)
222
223
            # Insert values for analyses
224
            for i, item in enumerate(items):
225
                for analysis in analyses[item["id"]]:
226
                    if keyword not in items[i]:
227
                        items[i][keyword] = ""
228
                    if analysis.getKeyword() != keyword:
229
                        continue
230
231
                    # check if the user can edit the analysis result
232
                    can_edit_result = self.can_edit_analysis_result(analysis)
233
234
                    calculation = analysis.getCalculation()
235
                    if self.allow_edit and can_edit_result and not calculation:
236
                        items[i]["allow_edit"].append(keyword)
237
                        self.columns[keyword]["ajax"] = True
238
239
                    value = analysis.getResult()
240
                    items[i][keyword] = value
241
                    items[i]["class"][keyword] = ""
242
243
                    if value or (can_edit_result and not calculation):
244
                        unit = unitstr % d_a.getUnit()
245
                        items[i]["after"][keyword] = unit
246
247
                if keyword not in items[i]["class"]:
248
                    items[i]["class"][keyword] = "empty"
249
250
        self.categories.sort()
251
        self.categories = [x[1] for x in self.categories]
252
253
        items = sorted(items, key=itemgetter("sort_key"))
254
255
        return items
256