get_historicresults()   F
last analyzed

Complexity

Conditions 15

Size

Total Lines 77
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 77
rs 2.9998
c 0
b 0
f 0
cc 15
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.health.browser.patient.historicresults.get_historicresults() 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.HEALTH.
4
#
5
# SENAITE.HEALTH is free software: you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by the Free
7
# Software 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.CMFCore.utils import getToolByName
22
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
23
from bika.lims.browser import BrowserView
24
from bika.health import bikaMessageFactory as _
25
from zope.interface import implements
26
from plone.app.layout.globals.interfaces import IViewView
27
from Products.CMFPlone.i18nl10n import ulocalized_time
28
import plone
29
import json
30
31
32
class HistoricResultsView(BrowserView):
33
    implements(IViewView)
34
35
    template = ViewPageTemplateFile("historicresults.pt")
36
37
    def __init__(self, context, request):
38
        BrowserView.__init__(self, context, request)
39
        self.context = context
40
        self.request = request
41
        self._rows = None
42
        self._dates = None
43
        path = "/++resource++bika.health.images"
44
        self.icon = self.portal_url + path + "/historicresults_big.png"
45
        self.title = self.context.translate(_("Historic Results"))
46
        self.description = ""
47
48
    def __call__(self):
49
        self._load()
50
        return self.template()
51
52
    def get_dates(self):
53
        """ Gets the result capture dates for which at least one analysis
54
            result has been found for the current Patient.
55
        """
56
        return self._dates
57
58
    def get_rows(self):
59
        """ Returns a dictionary with rows with the following structure:
60
            rows = {<sampletype_uid>: {
61
                        'object': <sampletype>,
62
                        'analyses': {
63
                            <analysisservice_uid>: {
64
                                'object': <analysisservice>,
65
                                'title': <analysisservice.title>,
66
                                'units': <analysisservice.units>,
67
                                'specs': {'error', 'min', 'max', ...},
68
                                <date> : {
69
                                    'object': <analysis>,
70
                                    'result': <analysis.result>,
71
                                    'date': <analysis.resultdate>
72
                                },
73
                            }
74
                        }
75
                    }}
76
        """
77
        return self._rows
78
79
    def _load(self):
80
        """ Loads the Controller acessors and other stuff
81
        """
82
        self._dates, self._rows = get_historicresults(self.context)
83
84
85
def get_historicresults(patient):
86
    if not patient:
87
        return [], {}
88
89
    rows = {}
90
    dates = []
91
    uid = patient.UID()
92
    states = ['verified', 'published']
93
94
    # Retrieve the AR IDs for the current patient
95
    bc = getToolByName(patient, 'bika_catalog')
96
    ars = [ar.id for ar
97
           in bc(portal_type='AnalysisRequest', review_state=states)
98
           if 'Patient' in ar.getObject().Schema()
99
           and ar.getObject().Schema().getField('Patient').get(ar.getObject())
100
           and ar.getObject().Schema().getField('Patient').get(ar.getObject()).UID() == uid]
101
102
    # Retrieve all the analyses, sorted by ResultCaptureDate DESC
103
    bc = getToolByName(patient, 'bika_analysis_catalog')
104
    analyses = [an.getObject() for an
105
                in bc(portal_type='Analysis',
106
                      getRequestID=ars,
107
                      sort_on='getResultCaptureDate',
108
                      sort_order='reverse')]
109
110
    # Build the dictionary of rows
111
    for analysis in analyses:
112
        ar = analysis.aq_parent
113
        sampletype = ar.getSampleType()
114
        row = rows.get(sampletype.UID()) if sampletype.UID() in rows.keys() \
115
            else {'object': sampletype, 'analyses': {}}
116
        anrow = row.get('analyses')
117
        service_uid = analysis.getServiceUID()
118
        asdict = anrow.get(service_uid, {'object': analysis,
119
                                         'title': analysis.Title(),
120
                                         'keyword': analysis.getKeyword(),
121
                                         'units': analysis.getUnit()})
122
        date = analysis.getResultCaptureDate() or analysis.created()
123
        date = ulocalized_time(date, 1, None, patient, 'bika')
124
        # If more than one analysis of the same type has been
125
        # performed in the same datetime, get only the last one
126
        if date not in asdict.keys():
127
            asdict[date] = {'object': analysis,
128
                            'result': analysis.getResult(),
129
                            'formattedresult': analysis.getFormattedResult()}
130
            # Get the specs
131
            # Only the specs applied to the last analysis for that
132
            # sample type will be taken into consideration.
133
            # We assume specs from previous analyses are obsolete.
134
            if 'specs' not in asdict.keys():
135
                spec = analysis.getAnalysisSpecs()                
136
                spec = spec.getResultsRangeDict() if spec else {}
137
                specs = spec.get(analysis.getKeyword(), {})
138
                if not specs.get('rangecomment', ''):
139
                    if specs.get('min', '') and specs.get('max', ''):
140
                        specs['rangecomment'] = '%s - %s' % \
141
                            (specs.get('min'), specs.get('max'))
142
                    elif specs.get('min', ''):
143
                        specs['rangecomment'] = '> %s' % specs.get('min')
144
                    elif specs.get('max', ''):
145
                        specs['rangecomment'] = '< %s' % specs.get('max')
146
147
                    if specs.get('error', '0') != '0' \
148
                            and specs.get('rangecomment', ''):
149
                        specs['rangecomment'] = ('%s (%s' %
150
                                                 (specs.get('rangecomment'),
151
                                                  specs.get('error'))) + '%)'
152
                asdict['specs'] = specs
153
154
            if date not in dates:
155
                dates.append(date)
156
        anrow[service_uid] = asdict
157
        row['analyses'] = anrow
158
        rows[sampletype.UID()] = row
159
    dates.sort(reverse=False)
160
161
    return dates, rows
162
163
164
class historicResultsJSON(BrowserView):
165
    """ Returns a JSON array datatable in a tabular format.
166
    """
167
168
    def __init__(self, context, request):
169
        BrowserView.__init__(self, context, request)
170
        self.context = context
171
        self.request = request
172
173
    def __call__(self):
174
        dates, data = get_historicresults(self.context)
175
        datatable = []
176
        for andate in dates:
177
            datarow = {'date': ulocalized_time(
178
                andate, 1, None, self.context, 'bika')}
179
            for row in data.itervalues():
180
                for anrow in row['analyses'].itervalues():
181
                    serie = anrow['title']
182
                    datarow[serie] = anrow.get(andate, {}).get('result', '')
183
            datatable.append(datarow)
184
        return json.dumps(datatable)
185