Passed
Push — master ( b60410...062e8c )
by Jordi
05:05
created

BatchBookView.folderitems()   F

Complexity

Conditions 34

Size

Total Lines 143
Code Lines 112

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 112
dl 0
loc 143
rs 0
c 0
b 0
f 0
cc 34
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like bika.lims.browser.batch.batchbook.BatchBookView.folderitems() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 re
22
from operator import itemgetter
23
24
from AccessControl import getSecurityManager
25
26
from Products.CMFPlone import PloneMessageFactory
27
from Products.CMFCore.permissions import ModifyPortalContent
28
29
from bika.lims import api
30
from bika.lims import bikaMessageFactory as _
31
from bika.lims.browser.bika_listing import BikaListingView
32
from bika.lims.permissions import EditResults
33
from bika.lims.permissions import AddAnalysisRequest
34
from bika.lims.permissions import ManageAnalysisRequests
35
36
37
class BatchBookView(BikaListingView):
38
39
    def __init__(self, context, request):
40
        super(BatchBookView, self).__init__(context, request)
41
        self.icon = self.portal_url + \
42
            "/++resource++bika.lims.images/batchbook_big.png"
43
        self.context_actions = {}
44
        self.contentFilter = {"sort_on": "created"}
45
        self.title = context.Title()
46
        self.Description = context.Description()
47
        self.show_select_all_checkbox = True
48
49
        self.show_column_toggles = True
50
        self.show_select_row = False
51
        self.show_select_column = True
52
        self.pagesize = 999999
53
        self.form_id = "list"
54
        self.page_start_index = 0
55
        self.show_categories = True
56
        self.expand_all_categories = True
57
58
        self.insert_submit_button = False
59
60
        request.set('disable_plone.rightcolumn', 1)
61
62
        self.columns = {
63
            'AnalysisRequest': {
64
                'title': _('Sample'),
65
                'index': 'id',
66
                'sortable': True,
67
            },
68
69
            'SampleType': {
70
                'title': _('Sample Type'),
71
                'sortable': True,
72
            },
73
            'SamplePoint': {
74
                'title': _('Sample Point'),
75
                'sortable': True,
76
            },
77
            'ClientOrderNumber': {
78
                'title': _('Client Order Number'),
79
                'sortable': True,
80
            },
81
            'created': {
82
                'title': PloneMessageFactory('Date Created'),
83
                'index': 'created',
84
                'toggle': False,
85
            },
86
            'state_title': {
87
                'title': _('State'),
88
                'index': 'review_state'
89
            },
90
        }
91
92
        self.review_states = [
93
            {'id': 'default',
94
             'title': _('All'),
95
             'contentFilter': {},
96
             'columns': ['AnalysisRequest',
97
                         'ClientOrderNumber',
98
                         'SampleType',
99
                         'SamplePoint',
100
                         'created',
101
                         'state_title'],
102
             },
103
        ]
104
105
    @property
106
    def copy_to_new_allowed(self):
107
        mtool = api.get_tool('portal_membership')
108
        if mtool.checkPermission(ManageAnalysisRequests, self.context) \
109
                or mtool.checkPermission(ModifyPortalContent, self.context) \
110
                or mtool.checkPermission(AddAnalysisRequest, self.portal):
111
            return True
112
        return False
113
114
    def __call__(self):
115
        # Allow "Modify portal content" to see edit widgets
116
        mtool = api.get_tool('portal_membership')
117
        self.allow_edit = mtool.checkPermission("Modify portal content", self.context)
118
        # Allow certain users to duplicate ARs (Copy to new).
119
        if self.copy_to_new_allowed:
120
            review_states = []
121
            for review_state in self.review_states:
122
                custom_transitions = review_state.get('custom_transitions', [])
123
                custom_transitions.extend(
124
                    [{'id': 'copy_to_new',
125
                      'title': _('Copy to new'),
126
                      'url': 'workflow_action?action=copy_to_new'},
127
                     ])
128
                review_state['custom_transitions'] = custom_transitions
129
                review_states.append(review_state)
130
            self.review_states = review_states
131
        return super(BatchBookView, self).__call__()
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
        wf = api.get_tool('portal_workflow')
138
        schema = self.context.Schema()
139
140
        ars = []
141
142
        for o in schema.getField('InheritedObjects').get(self.context):
143
            if o.portal_type == 'AnalysisRequest':
144
                if o not in ars:
145
                    ars.append(o)
146
            elif o.portal_type == 'Batch':
147
                for ar in o.getAnalysisRequests(is_active=True):
148
                    if ar not in ars:
149
                        ars.append(ar)
150
151
        for ar in self.context.getAnalysisRequests(is_active=True):
152
            if ar not in ars:
153
                ars.append(ar)
154
155
        self.categories = []
156
        analyses = {}
157
        items = []
158
        distinct = []  # distinct analyses (each one a different service)
159
        keywords = []
160
        for ar in ars:
161
            analyses[ar.id] = []
162
            for analysis in ar.getAnalyses(full_objects=True):
163
                analyses[ar.id].append(analysis)
164
                if analysis.getKeyword() not in keywords:
165
                    # we use a keyword check, because versioned services are !=.
166
                    keywords.append(analysis.getKeyword())
167
                    distinct.append(analysis)
168
169
            batchlink = ""
170
            batch = ar.getBatch()
171
            if batch:
172
                batchlink = "<a href='%s'>%s</a>" % (
173
                    batch.absolute_url(), batch.Title())
174
175
            arlink = "<a href='%s'>%s</a>" % (
176
                ar.absolute_url(), ar.Title())
177
178
            subgroup = ar.Schema()['SubGroup'].get(ar)
179
            sub_title = subgroup.Title() if subgroup else 'No Subgroup'
180
            sub_sort = subgroup.getSortKey() if subgroup else '1'
181
            sub_class = re.sub(r"[^A-Za-z\w\d\-\_]", '', sub_title)
182
183
            if [sub_sort, sub_title] not in self.categories:
184
                self.categories.append([sub_sort, sub_title])
185
186
            review_state = wf.getInfoFor(ar, 'review_state')
187
            state_title = wf.getTitleForStateOnType(
188
                review_state, 'AnalysisRequest')
189
190
            item = {
191
                'obj': ar,
192
                'id': ar.id,
193
                'uid': ar.UID(),
194
                'category': sub_title,
195
                'title': ar.Title(),
196
                'type_class': 'contenttype-AnalysisRequest',
197
                'url': ar.absolute_url(),
198
                'relative_url': ar.absolute_url(),
199
                'view_url': ar.absolute_url(),
200
                'created': self.ulocalized_time(ar.created(), long_format=1),
201
                'sort_key': ar.created(),
202
                'replace': {
203
                    'Batch': batchlink,
204
                    'AnalysisRequest': arlink,
205
                },
206
                'before': {},
207
                'after': {},
208
                'choices': {},
209
                'class': {'Batch': 'Title'},
210
                'state_class': 'state-active subgroup_{0}'.format(sub_class) if sub_class else 'state-active',
211
                'allow_edit': [],
212
                'Batch': '',
213
                'SamplePoint': ar.getSamplePoint().Title() if ar.getSamplePoint() else '',
214
                'SampleType': ar.getSampleType().Title() if ar.getSampleType() else '',
215
                'ClientOrderNumber': ar.getClientOrderNumber(),
216
                'AnalysisRequest': '',
217
                'state_title': state_title,
218
            }
219
            items.append(item)
220
221
        unitstr = '<em class="discreet" style="white-space:nowrap;">%s</em>'
222
        checkPermission = getSecurityManager().checkPermission
223
224
        # Insert columns for analyses
225
        for d_a in distinct:
226
            keyword = d_a.getKeyword()
227
            short = d_a.getShortTitle()
228
            title = d_a.Title()
229
            self.columns[keyword] = {
230
                'title':  short if short else title,
231
                'sortable': True
232
            }
233
            self.review_states[0]['columns'].insert(
234
                len(self.review_states[0]['columns']) - 1, keyword)
235
236
            # Insert values for analyses
237
            for i, item in enumerate(items):
238
                for analysis in analyses[item['id']]:
239
                    if keyword not in items[i]:
240
                        items[i][keyword] = ''
241
                    if analysis.getKeyword() != keyword:
242
                        continue
243
244
                    edit = checkPermission(EditResults, analysis)
245
                    calculation = analysis.getCalculation()
246
                    if self.allow_edit and edit and not calculation:
247
                        items[i]['allow_edit'].append(keyword)
248
                        if not self.insert_submit_button:
249
                            self.insert_submit_button = True
250
251
                    value = analysis.getResult()
252
                    items[i][keyword] = value
253
                    items[i]['class'][keyword] = ''
254
255
                    if value or (edit and not calculation):
256
257
                        unit = unitstr % d_a.getUnit()
258
                        items[i]['after'][keyword] = unit
259
260
                if keyword not in items[i]['class']:
261
                    items[i]['class'][keyword] = 'empty'
262
        if self.insert_submit_button:
263
            transitions = self.review_states[0].get('custom_transitions', [])
264
            transitions.append({
265
                'id': 'submit',
266
                'title': _('Submit')
267
            })
268
            self.review_states[0]['custom_transitions'] = transitions
269
270
        self.categories.sort()
271
        self.categories = [x[1] for x in self.categories]
272
273
        items = sorted(items, key=itemgetter("sort_key"))
274
275
        return items
276