Passed
Pull Request — 2.x (#1966)
by Ramon
04:36
created

bika.lims.browser.batch.batchbook   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 255
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 34
eloc 175
dl 0
loc 255
rs 9.68
c 0
b 0
f 0

6 Methods

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