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

bika.lims.jsonapi.read.read()   F

Complexity

Conditions 22

Size

Total Lines 111
Code Lines 84

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 84
dl 0
loc 111
rs 0
c 0
b 0
f 0
cc 22
nop 2

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.jsonapi.read.read() 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
from Products.CMFPlone.utils import safe_unicode
22
from bika.lims import logger, to_utf8
23
from bika.lims.interfaces import IJSONReadExtender
24
from bika.lims.jsonapi import get_include_fields
25
from plone.jsonapi.core import router
26
from plone.jsonapi.core.interfaces import IRouteProvider
27
from plone.protect.authenticator import AuthenticatorView
28
from bika.lims.jsonapi import load_brain_metadata
29
from bika.lims.jsonapi import load_field_values
30
from bika.lims.jsonapi import get_include_methods
31
from bika.lims.jsonapi import load_method_values
32
from Products.CMFCore.utils import getToolByName
33
from zope import interface
34
from zope.component import getAdapters
35
import re
36
import App
37
38
39
def read(context, request):
40
    tag = AuthenticatorView(context, request).authenticator()
41
    pattern = '<input .*name="(\w+)".*value="(\w+)"'
42
    _authenticator = re.match(pattern, tag).groups()[1]
43
44
    ret = {
45
        "url": router.url_for("read", force_external=True),
46
        "success": True,
47
        "error": False,
48
        "objects": [],
49
        "_authenticator": _authenticator,
50
    }
51
    debug_mode = App.config.getConfiguration().debug_mode
52
    catalog_name = request.get("catalog_name", "portal_catalog")
53
    if not catalog_name:
54
        raise ValueError("bad or missing catalog_name: " + catalog_name)
55
    catalog = getToolByName(context, catalog_name)
56
    indexes = catalog.indexes()
57
58
    contentFilter = {}
59
    for index in indexes:
60
        if index in request:
61
            if index == 'UID' and safe_unicode(request[index]) == "":
62
                msg = 'Request with no UID for %s catalog. Dismissing UID ' \
63
                      'while filtering' % catalog_name
64
                logger.warning(msg)
65
            if index == 'review_state' and "{" in request[index]:
66
                continue
67
            contentFilter[index] = safe_unicode(request[index])
68
        if "%s[]"%index in request:
69
            value = request["%s[]"%index]
70
            if type(value) in (list, tuple):
71
                contentFilter[index] = [safe_unicode(v) for v in value]
72
            else:
73
                contentFilter[index] = value
74
75
    if 'limit' in request:
76
        try:
77
            contentFilter['sort_limit'] = int(request["limit"])
78
        except ValueError:
79
            pass
80
    sort_on = request.get('sort_on', 'id')
81
    contentFilter['sort_on'] = sort_on
82
    # sort order
83
    sort_order = request.get('sort_order', '')
84
    if sort_order:
85
        contentFilter['sort_order'] = sort_order
86
    else:
87
        contentFilter['sort_order'] = 'ascending'
88
89
    include_fields = get_include_fields(request)
90
91
    include_methods = get_include_methods(request)
92
93
    # Get matching objects from catalog
94
    proxies = catalog(**contentFilter)
95
96
    if debug_mode:
97
        if len(proxies) == 0:
98
            logger.info("contentFilter {} returned zero objects"
99
                        .format(contentFilter))
100
        elif len(proxies) == 1:
101
            logger.info("contentFilter {} returned {} ({})".format(
102
                contentFilter, proxies[0].portal_type, proxies[0].UID))
103
        else:
104
            types = ','.join(set([p.portal_type for p in proxies]))
105
            logger.info("contentFilter {} returned {} items (types: {})"
106
                        .format(contentFilter, len(proxies), types))
107
108
    # batching items
109
    page_nr = int(request.get("page_nr", 0))
110
    try:
111
        page_size = int(request.get("page_size", 10))
112
    except ValueError:
113
        page_size = 10
114
    # page_size == 0: show all
115
    if page_size == 0:
116
        page_size = len(proxies)
117
    first_item_nr = page_size * page_nr
118
    if first_item_nr > len(proxies):
119
        first_item_nr = 0
120
    page_proxies = proxies[first_item_nr:first_item_nr + page_size]
121
    for proxy in page_proxies:
122
        obj_data = {}
123
124
        # Place all proxy attributes into the result.
125
        obj_data.update(load_brain_metadata(proxy, include_fields))
126
127
        # Place all schema fields ino the result.
128
        obj = proxy.getObject()
129
        obj_data.update(load_field_values(obj, include_fields))
130
        # Add methods results
131
        obj_data.update(load_method_values(obj, include_methods))
132
133
        obj_data['path'] = "/".join(obj.getPhysicalPath())
134
135
        # call any adapters that care to modify this data.
136
        adapters = getAdapters((obj, ), IJSONReadExtender)
137
        for name, adapter in adapters:
138
            adapter(request, obj_data)
139
140
        ret['objects'].append(obj_data)
141
142
    ret['total_objects'] = len(proxies)
143
    ret['first_object_nr'] = first_item_nr
144
    last_object_nr = first_item_nr + len(page_proxies)
145
    if last_object_nr > ret['total_objects']:
146
        last_object_nr = ret['total_objects']
147
    ret['last_object_nr'] = last_object_nr
148
149
    return ret
150
151
152
class Read(object):
153
    interface.implements(IRouteProvider)
154
155
    def initialize(self, context, request):
156
        pass
157
158
    @property
159
    def routes(self):
160
        return (
161
            ("/read", "read", self.read, dict(methods=['GET', 'POST'])),
162
        )
163
164
    def read(self, context, request):
165
        """/@@API/read: Search the catalog and return data for all objects found
166
167
        Optional parameters:
168
169
            - catalog_name: uses portal_catalog if unspecified
170
            - limit  default=1
171
            - All catalog indexes are searched for in the request.
172
173
        {
174
            runtime: Function running time.
175
            error: true or string(message) if error. false if no error.
176
            success: true or string(message) if success. false if no success.
177
            objects: list of dictionaries, containing catalog metadata
178
        }
179
        """
180
181
        return read(context, request)
182