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

ViewView.__init__()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nop 3
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
from datetime import datetime
10
11
from bika.lims import api
12
from bika.lims import bikaMessageFactory as _
13
from bika.lims import logger
14
from bika.lims.browser import BrowserView
15
from bika.lims.browser.analyses import AnalysesView
16
from bika.lims.browser.bika_listing import BikaListingView
17
from bika.lims.browser.chart.analyses import EvolutionChart
18
from bika.lims.utils import get_image
19
from bika.lims.utils import get_link
20
from bika.lims.utils import t
21
from plone.app.layout.globals.interfaces import IViewView
22
from plone.memoize import view
23
from Products.Archetypes.config import REFERENCE_CATALOG
24
from Products.ATContentTypes.utils import DT2dt
25
from Products.CMFCore.utils import getToolByName
26
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
27
from zope.interface import implements
28
29
30
class ViewView(BrowserView):
31
    """ Reference Sample View
32
    """
33
    implements(IViewView)
34
    template = ViewPageTemplateFile("templates/referencesample_view.pt")
35
36
    def __init__(self, context, request):
37
        BrowserView.__init__(self, context, request)
38
        self.icon = self.portal_url +\
39
            "/++resource++bika.lims.images/referencesample_big.png"
40
41
    def __call__(self):
42
        rc = getToolByName(self.context, REFERENCE_CATALOG)
43
        self.results = {}  # {category_title: listofdicts}
44
        for r in self.context.getReferenceResults():
45
            service = rc.lookupObject(r['uid'])
46
            cat = service.getCategoryTitle()
47
            if cat not in self.results:
48
                self.results[cat] = []
49
            r['service'] = service
50
            self.results[cat].append(r)
51
        self.categories = self.results.keys()
52
        self.categories.sort()
53
        return self.template()
54
55
56
class ReferenceAnalysesViewView(BrowserView):
57
    """ View of Reference Analyses linked to the Reference Sample.
58
    """
59
60
    implements(IViewView)
61
    template = ViewPageTemplateFile("templates/referencesample_analyses.pt")
62
63
    def __init__(self, context, request):
64
        super(ReferenceAnalysesViewView, self).__init__(context, request)
65
        self.icon = self.portal_url + \
66
            "/++resource++bika.lims.images/referencesample_big.png"
67
        self.title = self.context.translate(_("Reference Analyses"))
68
        self.description = ""
69
        self._analysesview = None
70
71
    def __call__(self):
72
        return self.template()
73
74
    def get_analyses_table(self):
75
        """ Returns the table of Reference Analyses
76
        """
77
        return self.get_analyses_view().contents_table()
78
79
    def get_analyses_view(self):
80
        if not self._analysesview:
81
            # Creates the Analyses View if not exists yet
82
            self._analysesview = ReferenceAnalysesView(
83
                self.context, self.request)
84
            self._analysesview.allow_edit = False
85
            self._analysesview.show_select_column = False
86
            self._analysesview.show_workflow_action_buttons = False
87
            self._analysesview.form_id = "%s_qcanalyses" % self.context.UID()
88
            self._analysesview.review_states[0]['transitions'] = [{}]
89
        return self._analysesview
90
91
    def getReferenceSampleId(self):
92
        return self.context.id
93
94
    def get_analyses_json(self):
95
        return self.get_analyses_view().chart.get_json()
96
97
98
class ReferenceAnalysesView(AnalysesView):
99
    """ Reference Analyses on this sample
100
    """
101
    implements(IViewView)
102
103
    def __init__(self, context, request):
104
        AnalysesView.__init__(self, context, request)
105
        self.contentFilter = {
106
            'portal_type': 'ReferenceAnalysis',
107
            'path': {'query': "/".join(self.context.getPhysicalPath()),
108
                     'level': 0}}
109
        self.columns = {
110
            'id': {'title': _('ID'), 'toggle': False},
111
            'getReferenceAnalysesGroupID': {
112
                'title': _('QC Sample ID'), 'toggle': True},
113
            'Category': {'title': _('Category'), 'toggle': True},
114
            'Service': {'title': _('Service'), 'toggle': True},
115
            'Worksheet': {'title': _('Worksheet'), 'toggle': True},
116
            'Method': {
117
                'title': _('Method'),
118
                'sortable': False,
119
                'toggle': True},
120
            'Instrument': {
121
                'title': _('Instrument'),
122
                'sortable': False,
123
                'toggle': True},
124
            'Result': {'title': _('Result'), 'toggle': True},
125
            'CaptureDate': {
126
                'title': _('Captured'),
127
                'index': 'getResultCaptureDate',
128
                'toggle': True},
129
            'Uncertainty': {'title': _('+-'), 'toggle': True},
130
            'DueDate': {'title': _('Due Date'),
131
                        'index': 'getDueDate',
132
                        'toggle': True},
133
            'retested': {'title': _('Retested'),
134
                         'type': 'boolean', 'toggle': True},
135
            'state_title': {'title': _('State'), 'toggle': True},
136
        }
137
        self.review_states = [
138
            {'id': 'default',
139
             'title': _('All'),
140
             'contentFilter': {},
141
             'transitions': [],
142
             'columns':['id',
143
                        'getReferenceAnalysesGroupID',
144
                        'Category',
145
                        'Service',
146
                        'Worksheet',
147
                        'Method',
148
                        'Instrument',
149
                        'Result',
150
                        'CaptureDate',
151
                        'Uncertainty',
152
                        'DueDate',
153
                        'state_title'],
154
             },
155
        ]
156
        self.chart = EvolutionChart()
157
158
    def isItemAllowed(self, obj):
159
        """
160
        :obj: it is a brain
161
        """
162
        allowed = super(ReferenceAnalysesView, self).isItemAllowed(obj)
163
        return allowed if not allowed else obj.getResult != ''
164
165
    def folderitem(self, obj, item, index):
166
        """
167
        :obj: it is a brain
168
        """
169
        item = super(ReferenceAnalysesView, self).folderitem(obj, item, index)
170
        if not item:
171
            return None
172
        item['Category'] = obj.getCategoryTitle
173
        wss = self.rc.getBackReferences(
174
            obj.UID,
175
            relationship="WorksheetAnalysis")
176
        if not wss:
177
            logger.warn(
178
                'No Worksheet found for ReferenceAnalysis {}'
179
                .format(obj.getId))
180
        elif wss and len(wss) == 1:
181
            # TODO-performance: We are getting the object here...
182
            ws = wss[0].getSourceObject()
183
            item['Worksheet'] = ws.Title()
184
            anchor = '<a href="%s">%s</a>' % (ws.absolute_url(), ws.Title())
185
            item['replace']['Worksheet'] = anchor
186
        else:
187
            logger.warn(
188
                'More than one Worksheet found for ReferenceAnalysis {}'
189
                .format(obj.getId))
190
191
        # Add the analysis to the QC Chart
192
        self.chart.add_analysis(obj)
193
        return item
194
195
196
class ReferenceResultsView(BikaListingView):
197
    """Listing of all reference results
198
    """
199
200
    def __init__(self, context, request):
201
        super(ReferenceResultsView, self).__init__(context, request)
202
203
        self.catalog = "bika_setup_catalog"
204
        self.contentFilter = {
205
            "portal_type": "AnalysisService",
206
            "UID": self.get_reference_results().keys(),
207
            "inactive_state": "active",
208
            "sort_on": "sortable_title",
209
            "sort_order": "ascending",
210
        }
211
        self.context_actions = {}
212
        self.title = self.context.translate(_("Reference Values"))
213
        self.icon = "{}/{}".format(
214
            self.portal_url,
215
            "/++resource++bika.lims.images/referencesample_big.png"
216
        )
217
        self.show_sort_column = False
218
        self.show_select_row = False
219
        self.show_workflow_action_buttons = False
220
        self.show_select_column = False
221
        self.pagesize = 999999
222
        self.show_search = False
223
224
        # Categories
225
        if self.show_categories_enabled():
226
            self.categories = []
227
            self.show_categories = True
228
            self.expand_all_categories = True
229
            self.category_index = "getCategoryTitle"
230
231
        self.columns = collections.OrderedDict((
232
            ("Title", {
233
                "title": _("Analysis Service"),
234
                "sortable": False}),
235
            ("result", {
236
                "title": _("Expected Result"),
237
                "sortable": False}),
238
            ("error", {
239
                "title": _("Permitted Error %"),
240
                "sortable": False}),
241
            ("min", {
242
                "title": _("Min"),
243
                "sortable": False}),
244
            ("max", {
245
                "title": _("Max"),
246
                "sortable": False}),
247
        ))
248
249
        self.review_states = [
250
            {
251
                "id": "default",
252
                "title": _("All"),
253
                "contentFilter": {},
254
                "columns": self.columns.keys()
255
            }
256
        ]
257
258
    def update(self):
259
        """Update hook
260
        """
261
        super(ReferenceResultsView, self).update()
262
        self.categories.sort()
263
264
    @view.memoize
265
    def show_categories_enabled(self):
266
        """Check in the setup if categories are enabled
267
        """
268
        return self.context.bika_setup.getCategoriseAnalysisServices()
269
270
    @view.memoize
271
    def get_reference_results(self):
272
        """Return a mapping of Analysis Service -> Reference Results
273
        """
274
        referenceresults = self.context.getReferenceResults()
275
        return dict(map(lambda rr: (rr.get("uid"), rr), referenceresults))
276
277
    def folderitem(self, obj, item, index):
278
        """Service triggered each time an item is iterated in folderitems.
279
280
        The use of this service prevents the extra-loops in child objects.
281
282
        :obj: the instance of the class to be foldered
283
        :item: dict containing the properties of the object to be used by
284
            the template
285
        :index: current index of the item
286
        """
287
288
        uid = api.get_uid(obj)
289
        url = api.get_url(obj)
290
        title = api.get_title(obj)
291
292
        # get the category
293
        if self.show_categories_enabled():
294
            category = obj.getCategoryTitle()
295
            if category not in self.categories:
296
                self.categories.append(category)
297
            item["category"] = category
298
299
        ref_results = self.get_reference_results()
300
        ref_result = ref_results.get(uid)
301
302
        item["Title"] = title
303
        item["replace"]["Title"] = get_link(url, value=title)
304
        item["result"] = ref_result.get("result")
305
        item["min"] = ref_result.get("min")
306
        item["max"] = ref_result.get("max")
307
308
        # Icons
309
        after_icons = ""
310
        if obj.getAccredited():
311
            after_icons += get_image(
312
                "accredited.png", title=_("Accredited"))
313
        if obj.getAttachmentOption() == "r":
314
            after_icons += get_image(
315
                "attach_reqd.png", title=_("Attachment required"))
316
        if obj.getAttachmentOption() == "n":
317
            after_icons += get_image(
318
                "attach_no.png", title=_("Attachment not permitted"))
319
        if after_icons:
320
            item["after"]["Title"] = after_icons
321
322
        return item
323
324
325
class ReferenceSamplesView(BikaListingView):
326
    """Main reference samples folder view
327
    """
328
    def __init__(self, context, request):
329
        super(ReferenceSamplesView, self).__init__(context, request)
330
        self.icon = self.portal_url + \
331
            "/++resource++bika.lims.images/referencesample_big.png"
332
        self.title = self.context.translate(_("Reference Samples"))
333
        self.catalog = 'bika_catalog'
334
        self.contentFilter = {'portal_type': 'ReferenceSample',
335
                              'sort_on': 'id',
336
                              'sort_order': 'reverse',
337
                              'path': {"query": ["/"], "level": 0}, }
338
        self.context_actions = {}
339
        self.show_select_column = True
340
341
        self.columns = {
342
            'ID': {
343
                'title': _('ID'),
344
                'index': 'id'},
345
            'Title': {
346
                'title': _('Title'),
347
                'index': 'sortable_title',
348
                'toggle': True},
349
            'Supplier': {
350
                'title': _('Supplier'),
351
                'toggle': True,
352
                'attr': 'aq_parent.Title',
353
                'replace_url': 'aq_parent.absolute_url'},
354
            'Manufacturer': {
355
                'title': _('Manufacturer'),
356
                'toggle': True,
357
                'attr': 'getManufacturer.Title',
358
                'replace_url': 'getManufacturer.absolute_url'},
359
            'Definition': {
360
                'title': _('Reference Definition'),
361
                'toggle': True,
362
                'attr': 'getReferenceDefinition.Title',
363
                'replace_url': 'getReferenceDefinition.absolute_url'},
364
            'DateSampled': {
365
                'title': _('Date Sampled'),
366
                'index': 'getDateSampled',
367
                'toggle': True},
368
            'DateReceived': {
369
                'title': _('Date Received'),
370
                'index': 'getDateReceived',
371
                'toggle': True},
372
            'DateOpened': {
373
                'title': _('Date Opened'),
374
                'toggle': True},
375
            'ExpiryDate': {
376
                'title': _('Expiry Date'),
377
                'index': 'getExpiryDate',
378
                'toggle': True},
379
            'state_title': {
380
                'title': _('State'),
381
                'toggle': True},
382
        }
383
        self.review_states = [
384
            {'id': 'default',
385
             'title': _('Current'),
386
             'contentFilter': {'review_state': 'current'},
387
             'columns': ['ID',
388
                         'Title',
389
                         'Supplier',
390
                         'Manufacturer',
391
                         'Definition',
392
                         'DateSampled',
393
                         'DateReceived',
394
                         'DateOpened',
395
                         'ExpiryDate']},
396
            {'id': 'expired',
397
             'title': _('Expired'),
398
             'contentFilter': {'review_state': 'expired'},
399
             'columns': ['ID',
400
                         'Title',
401
                         'Supplier',
402
                         'Manufacturer',
403
                         'Definition',
404
                         'DateSampled',
405
                         'DateReceived',
406
                         'DateOpened',
407
                         'ExpiryDate']},
408
            {'id': 'disposed',
409
             'title': _('Disposed'),
410
             'contentFilter': {'review_state': 'disposed'},
411
             'columns': ['ID',
412
                         'Title',
413
                         'Supplier',
414
                         'Manufacturer',
415
                         'Definition',
416
                         'DateSampled',
417
                         'DateReceived',
418
                         'DateOpened',
419
                         'ExpiryDate']},
420
            {'id': 'all',
421
             'title': _('All'),
422
             'contentFilter': {},
423
             'columns': ['ID',
424
                         'Title',
425
                         'Supplier',
426
                         'Manufacturer',
427
                         'Definition',
428
                         'DateSampled',
429
                         'DateReceived',
430
                         'DateOpened',
431
                         'ExpiryDate',
432
                         'state_title']},
433
        ]
434
435
    def folderitem(self, obj, item, index):
436
        if item.get('review_state', 'current') == 'current':
437
            # Check expiry date
438
            exdate = obj.getExpiryDate()
439
            if exdate:
440
                expirydate = DT2dt(exdate).replace(tzinfo=None)
441
                if (datetime.today() > expirydate):
442
                    # Trigger expiration
443
                    self.workflow.doActionFor(obj, 'expire')
444
                    item['review_state'] = 'expired'
445
                    item['obj'] = obj
446
447
        if self.contentFilter.get('review_state', '') \
448
           and item.get('review_state', '') == 'expired':
449
            # This item must be omitted from the list
450
            return None
451
452
        item['ID'] = obj.id
453
        item['DateSampled'] = self.ulocalized_time(
454
            obj.getDateSampled(), long_format=True)
455
        item['DateReceived'] = self.ulocalized_time(obj.getDateReceived())
456
        item['DateOpened'] = self.ulocalized_time(obj.getDateOpened())
457
        item['ExpiryDate'] = self.ulocalized_time(obj.getExpiryDate())
458
459
        after_icons = ''
460
        if obj.getBlank():
461
            after_icons += "<img\
462
            src='%s/++resource++bika.lims.images/blank.png' \
463
            title='%s'>" % (self.portal_url, t(_('Blank')))
464
        if obj.getHazardous():
465
            after_icons += "<img\
466
            src='%s/++resource++bika.lims.images/hazardous.png' \
467
            title='%s'>" % (self.portal_url, t(_('Hazardous')))
468
        item['replace']['ID'] = "<a href='%s/base_view'>%s</a>&nbsp;%s" % \
469
            (item['url'], item['ID'], after_icons)
470
        return item
471