Passed
Push — master ( 92201a...bcbb8c )
by Jordi
04:45
created

bika.lims.browser.widgets.referenceresultswidget   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 265
Duplicated Lines 7.55 %

Importance

Changes 0
Metric Value
wmc 26
eloc 162
dl 20
loc 265
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A ReferenceResultsWidget.process_form() 0 41 4
B ReferenceResultsView.__init__() 0 53 2
A ReferenceResultsView.update() 0 6 1
A ReferenceResultsView.get_required_columns() 0 5 1
A ReferenceResultsView.folderitems() 0 6 1
A ReferenceResultsView.get_editable_columns() 0 5 1
A ReferenceResultsWidget._get_spec_value() 20 20 5
A ReferenceResultsView.get_reference_results() 0 6 2
B ReferenceResultsView.folderitem() 0 48 7
A ReferenceResultsView.show_categories_enabled() 0 5 1
A ReferenceResultsWidget.ReferenceResults() 0 11 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
8
import collections
9
10
from AccessControl import ClassSecurityInfo
11
from bika.lims import api
12
from bika.lims import bikaMessageFactory as _
13
from bika.lims.browser.bika_listing import BikaListingView
14
from bika.lims.utils import get_image
15
from bika.lims.utils import get_link
16
from plone.memoize import view
17
from Products.Archetypes.Registry import registerWidget
18
from Products.Archetypes.Widget import TypesWidget
19
20
21
class ReferenceResultsView(BikaListingView):
22
    """Listing table to display Reference Results
23
    """
24
25
    def __init__(self, context, request, fieldvalue=[], allow_edit=True):
26
        super(ReferenceResultsView, self).__init__(context, request)
27
28
        self.catalog = "bika_setup_catalog"
29
        self.contentFilter = {
30
            "portal_type": "AnalysisService",
31
            "inactive_state": "active",
32
            "sort_on": "sortable_title",
33
            "sort_order": "ascending",
34
        }
35
        self.context_actions = {}
36
37
        self.show_sort_column = False
38
        self.show_column_toggles = False
39
        self.show_select_column = True
40
        self.show_select_all_checkbox = True
41
        self.pagesize = 999999
42
        self.allow_edit = True
43
        self.show_search = False
44
        self.omit_form = True
45
46
        # Categories
47
        if self.show_categories_enabled():
48
            self.categories = []
49
            self.show_categories = True
50
            self.expand_all_categories = False
51
            self.category_index = "getCategoryTitle"
52
53
        self.columns = collections.OrderedDict((
54
            ("Title", {
55
                "title": _("Service"),
56
                "sortable": False}),
57
            ("result", {
58
                "title": _("Expected Result"),
59
                "sortable": False}),
60
            ("error", {
61
                "title": _("Permitted Error %"),
62
                "sortable": False}),
63
            ("min", {
64
                "title": _("Min"),
65
                "sortable": False}),
66
            ("max", {
67
                "title": _("Max"),
68
                "sortable": False}),
69
        ))
70
71
        self.review_states = [
72
            {
73
                "id": "default",
74
                "title": _("All"),
75
                "contentFilter": {},
76
                "transitions": [],
77
                "columns": self.columns.keys(),
78
            },
79
        ]
80
81
    def update(self):
82
        """Update hook
83
        """
84
        super(ReferenceResultsView, self).update()
85
        self.categories.sort()
86
        self.referenceresults = self.get_reference_results()
87
88
    @view.memoize
89
    def show_categories_enabled(self):
90
        """Check in the setup if categories are enabled
91
        """
92
        return self.context.bika_setup.getCategoriseAnalysisServices()
93
94
    @view.memoize
95
    def get_reference_results(self):
96
        """Return a mapping of Analysis Service -> Reference Results
97
        """
98
        referenceresults = self.context.getReferenceResults()
99
        return dict(map(lambda rr: (rr.get("uid"), rr), referenceresults))
100
101
    def get_editable_columns(self):
102
        """Return editable fields
103
        """
104
        columns = ["result", "error", "min", "max"]
105
        return columns
106
107
    def get_required_columns(self):
108
        """Return required editable fields
109
        """
110
        columns = ["result"]
111
        return columns
112
113
    def folderitems(self):
114
        """TODO: Refactor to non-classic mode
115
        """
116
        items = super(ReferenceResultsView, self).folderitems()
117
        self.categories.sort()
118
        return items
119
120
    def folderitem(self, obj, item, index):
121
        """Service triggered each time an item is iterated in folderitems.
122
123
        The use of this service prevents the extra-loops in child objects.
124
125
        :obj: the instance of the class to be foldered
126
        :item: dict containing the properties of the object to be used by
127
            the template
128
        :index: current index of the item
129
        """
130
        # ensure we have an object and not a brain
131
        obj = api.get_object(obj)
132
        uid = api.get_uid(obj)
133
        url = api.get_url(obj)
134
        title = api.get_title(obj)
135
136
        # get the category
137
        if self.show_categories_enabled():
138
            category = obj.getCategoryTitle()
139
            if category not in self.categories:
140
                self.categories.append(category)
141
            item["category"] = category
142
143
        rr = self.referenceresults.get(uid, {})
144
        item["Title"] = title
145
        item["replace"]["Title"] = get_link(url, value=title)
146
        item["allow_edit"] = self.get_editable_columns()
147
        item["required"] = self.get_required_columns()
148
        item["selected"] = rr and True or False
149
        item["result"] = rr.get("result", "")
150
        item["min"] = rr.get("min", "")
151
        item["max"] = rr.get("max", "")
152
153
        # Icons
154
        after_icons = ""
155
        if obj.getAccredited():
156
            after_icons += get_image(
157
                "accredited.png", title=_("Accredited"))
158
        if obj.getAttachmentOption() == "r":
159
            after_icons += get_image(
160
                "attach_reqd.png", title=_("Attachment required"))
161
        if obj.getAttachmentOption() == "n":
162
            after_icons += get_image(
163
                "attach_no.png", title=_("Attachment not permitted"))
164
        if after_icons:
165
            item["after"]["Title"] = after_icons
166
167
        return item
168
169
170
class ReferenceResultsWidget(TypesWidget):
171
    """Reference Results Widget
172
    """
173
    _properties = TypesWidget._properties.copy()
174
    _properties.update({
175
        "macro": "bika_widgets/referenceresultswidget",
176
        "helper_js": ("bika_widgets/referenceresultswidget.js",),
177
        "helper_css": ("bika_widgets/referenceresultswidget.css",)
178
    })
179
180
    security = ClassSecurityInfo()
181
182
    security.declarePublic("process_form")
183
184
    def process_form(self, instance, field, form,
185
                     empty_marker=None, emptyReturnsMarker=False):
186
        """Return a list of dictionaries fit for ReferenceResultsField
187
        consumption. Only services which have float()able entries in result,min
188
        and max field will be included. If any of min, max, or result fields
189
        are blank, the row value is ignored here.
190
        """
191
        values = []
192
193
        # Process settings from the reference definition first
194
        ref_def = form.get("ReferenceDefinition")
195
        ref_def_uid = ref_def and ref_def[0]
196
        if ref_def_uid:
197
            ref_def_obj = api.get_object_by_uid(ref_def_uid)
198
            rr = ref_def_obj.getReferenceResults()
199
            values.extend(rr)
200
201
        # selected services
202
        service_uids = form.get("uids", [])
203
204
        for uid in service_uids:
205
            result = self._get_spec_value(form, uid, "result")
206
            if not result:
207
                # User has to set a value for result subfield at least
208
                continue
209
210
            # If neither min nor max have been set, assume we only accept a
211
            # discrete result (like if % of error was 0).
212
            s_min = self._get_spec_value(form, uid, "min", result)
213
            s_max = self._get_spec_value(form, uid, "max", result)
214
215
            service = api.get_object_by_uid(uid)
216
            values.append({
217
                "keyword": service.getKeyword(),
218
                "uid": uid,
219
                "result": result,
220
                "min": s_min,
221
                "max": s_max
222
            })
223
224
        return values, {}
225
226 View Code Duplication
    def _get_spec_value(self, form, uid, key, default=''):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
227
        """Returns the value assigned to the passed in key for the analysis
228
        service uid from the passed in form.
229
230
        If check_floatable is true, will return the passed in default if the
231
        obtained value is not floatable
232
        :param form: form being submitted
233
        :param uid: uid of the Analysis Service the specification relates
234
        :param key: id of the specs param to get (e.g. 'min')
235
        :param check_floatable: check if the value is floatable
236
        :param default: fallback value that will be returned by default
237
        :type default: str, None
238
        """
239
        if not form or not uid:
240
            return default
241
        values = form.get(key, None)
242
        if not values or len(values) == 0:
243
            return default
244
        value = values[0].get(uid, default)
245
        return api.is_floatable(value) and value or default
246
247
    security.declarePublic("ReferenceResults")
248
249
    def ReferenceResults(self, field, allow_edit=False):
250
        """Render Reference Results Table
251
        """
252
        instance = getattr(self, "instance", field.aq_parent)
253
        table = api.get_view("table_reference_results",
254
                             context=instance,
255
                             request=self.REQUEST)
256
        # Call listing hooks
257
        table.update()
258
        table.before_render()
259
        return table.ajax_contents_table()
260
261
262
registerWidget(ReferenceResultsWidget,
263
               title="Reference definition results",
264
               description=("Reference definition results."),)
265