Passed
Push — 2.x ( 0ba30f...7ab1ba )
by Jordi
08:49
created

ServicesWidget.folderitem()   C

Complexity

Conditions 10

Size

Total Lines 73
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 47
dl 0
loc 73
rs 5.9345
c 0
b 0
f 0
cc 10
nop 4

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 senaite.core.browser.widgets.services_widget.ServicesWidget.folderitem() 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
import collections
4
5
from bika.lims import api
6
from bika.lims import bikaMessageFactory as _
7
from bika.lims.api.security import check_permission
8
from bika.lims.utils import format_supsub
9
from bika.lims.utils import get_image
10
from bika.lims.utils import get_link
11
from plone.memoize import view
12
from senaite.core.catalog import SETUP_CATALOG
13
from senaite.core.permissions import FieldEditProfiles
14
from senaite.core.z3cform.widgets.listing.view import DefaultListingWidget
15
from zope.i18n.locales import locales
16
17
18
class ServicesWidget(DefaultListingWidget):
19
    """Listing widget for Analysis Services
20
    """
21
    def __init__(self, field, request):
22
        super(ServicesWidget, self).__init__(field, request)
23
24
        self.catalog = SETUP_CATALOG
25
        self.contentFilter = {
26
            "portal_type": "AnalysisService",
27
            "sort_on": "sortable_title",
28
            "sort_order": "ascending",
29
            "is_active": True,
30
        }
31
32
        # group the current records by UID
33
        self.records = {}
34
        for record in self.get_value():
35
            uid = record.get("uid")
36
            self.records[uid] = record
37
38
        # listing config
39
        self.allow_edit = True
40
        self.context_actions = {}
41
        self.fetch_transitions_on_select = False
42
        self.omit_form = True
43
        self.pagesize = 999999
44
        self.show_column_toggles = False
45
        self.show_search = True
46
        self.show_select_all_checkbox = False
47
        self.show_select_column = True
48
        self.show_table_footer = False
49
50
        # Categories
51
        if self.show_categories_enabled():
52
            self.categories = []
53
            self.show_categories = True
54
            self.expand_all_categories = False
55
56
        self.columns = collections.OrderedDict((
57
            ("Title", {
58
                "title": _(
59
                    u"listing_services_column_title",
60
                    default=u"Service"
61
                ),
62
                "index": "sortable_title",
63
                "sortable": False
64
            }),
65
            ("Keyword", {
66
                "title": _(
67
                    u"listing_services_column_keyword",
68
                    default=u"Keyword"
69
                ),
70
                "sortable": False
71
            }),
72
            ("Methods", {
73
                "title": _(
74
                    u"listing_services_column_methods",
75
                    default=u"Methods"
76
                ),
77
                "sortable": False
78
            }),
79
            ("Unit", {
80
                "title": _(
81
                    u"listing_services_column_unit",
82
                    default=u"Unit"
83
                ),
84
                "sortable": False
85
            }),
86
            ("Price", {
87
                "title": _(
88
                    u"listing_services_column_price",
89
                    default=u"Price"
90
                ),
91
                "sortable": False,
92
            }),
93
            ("Hidden", {
94
                "title": _(
95
                    u"listing_services_column_hidden",
96
                    default=u"Hidden"
97
                ),
98
                "sortable": False,
99
            }),
100
        ))
101
102
        cols = self.columns.keys()
103
        if not self.show_prices():
104
            cols.remove("Price")
105
106
        self.review_states = [
107
            {
108
                "id": "default",
109
                "title": _(
110
                    u"listing_services_state_all",
111
                    default=u"All"
112
                ),
113
                "contentFilter": {},
114
                "transitions": [{"id": "disallow-all-possible-transitions"}],
115
                "columns": cols,
116
            },
117
        ]
118
119
    @view.memoize
120
    def show_categories_enabled(self):
121
        """Check in the setup if categories are enabled
122
        """
123
        bika_setup = api.get_bika_setup()
124
        return bika_setup.getCategoriseAnalysisServices()
125
126
    @view.memoize
127
    def show_prices(self):
128
        """Checks if prices should be shown or not
129
        """
130
        bika_setup = api.get_setup()
131
        return bika_setup.getShowPrices()
132
133
    @view.memoize
134
    def get_currency_symbol(self):
135
        """Get the currency Symbol
136
        """
137
        locale = locales.getLocale("en")
138
        bika_setup = api.get_bika_setup()
139
        currency = bika_setup.getCurrency()
140
        return locale.numbers.currencies[currency].symbol
141
142
    @view.memoize
143
    def get_decimal_mark(self):
144
        """Returns the decimal mark
145
        """
146
        bika_setup = api.get_bika_setup()
147
        return bika_setup.getDecimalMark()
148
149
    @view.memoize
150
    def format_price(self, price):
151
        """Formats the price with the set decimal mark and correct currency
152
        """
153
        return u"{} {}{}{:02d}".format(
154
            self.get_currency_symbol(),
155
            price[0],
156
            self.get_decimal_mark(),
157
            price[1],
158
        )
159
160
    @view.memoize
161
    def is_edit_allowed(self):
162
        """Check if edit is allowed
163
        """
164
        return check_permission(FieldEditProfiles, self.context)
165
166
    @view.memoize
167
    def get_editable_columns(self):
168
        """Return editable fields
169
        """
170
        columns = []
171
        if self.is_edit_allowed():
172
            columns = ["Hidden"]
173
        return columns
174
175
    def extract(self):
176
        """Extract the value from the request for the field
177
        """
178
        form = self.request.form
179
        selected = form.get(self.select_checkbox_name, [])
180
181
        if not selected:
182
            return []
183
184
        # extract the data from the form for the field
185
        records = []
186
        hidden_services = form.get("Hidden", {})
187
        for uid in selected:
188
            records.append({
189
                "uid": uid,
190
                "hidden": hidden_services.get(uid) == "on",
191
            })
192
193
        return records
194
195
    def folderitems(self):
196
        items = super(ServicesWidget, self).folderitems()
197
        self.categories.sort()
198
        return items
199
200
    def folderitem(self, obj, item, index):
201
        """Service triggered each time an item is iterated in folderitems.
202
203
        The use of this service prevents the extra-loops in child objects.
204
205
        :obj: the instance of the class to be foldered
206
        :item: dict containing the properties of the object to be used by
207
            the template
208
        :index: current index of the item
209
        """
210
        item = super(ServicesWidget, self).folderitem(obj, item, index)
211
212
        # ensure we have an object and not a brain
213
        obj = api.get_object(obj)
214
        uid = api.get_uid(obj)
215
        url = api.get_url(obj)
216
        title = api.get_title(obj)
217
        keyword = obj.getKeyword()
218
219
        # get the category
220
        if self.show_categories_enabled():
221
            category = obj.getCategoryTitle()
222
            if category not in self.categories:
223
                self.categories.append(category)
224
            item["category"] = category
225
226
        hidden = False
227
        # get the hidden setting from the records
228
        if self.records.get(uid):
229
            record = self.records.get(uid, {}) or {}
230
            hidden = record.get("hidden", False)
231
        else:
232
            # get the default value from the service
233
            hidden = obj.getHidden()
234
235
        item["replace"]["Title"] = get_link(url, value=title)
236
        item["Price"] = self.format_price(obj.Price)
237
        item["allow_edit"] = self.get_editable_columns()
238
        item["selected"] = False
239
        item["Hidden"] = hidden
240
        item["replace"]["Hidden"] = _("Yes") if hidden else _("No")
241
        item["selected"] = uid in self.records
242
        item["Keyword"] = keyword
243
        item["replace"]["Keyword"] = "<code>{}</code>".format(keyword)
244
245
        # Add methods
246
        methods = obj.getMethods()
247
        if methods:
248
            links = map(
249
                lambda m: get_link(
250
                    m.absolute_url(), value=m.Title(), css_class="link"),
251
                methods)
252
            item["replace"]["Methods"] = ", ".join(links)
253
        else:
254
            item["methods"] = ""
255
256
        # Unit
257
        unit = obj.getUnit()
258
        item["Unit"] = unit or ""
259
        item["replace"]["Unit"] = unit and format_supsub(unit) or ""
260
261
        # Icons
262
        after_icons = ""
263
        if obj.getAccredited():
264
            after_icons += get_image(
265
                "accredited.png", title=_("Accredited"))
266
        if obj.getAttachmentRequired():
267
            after_icons += get_image(
268
                "attach_reqd.png", title=_("Attachment required"))
269
        if after_icons:
270
            item["after"]["Title"] = after_icons
271
272
        return item
273