-
-
Notifications
You must be signed in to change notification settings - Fork 152
/
Copy pathreferencesample.py
281 lines (261 loc) · 9.24 KB
/
referencesample.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# -*- coding: utf-8 -*-
#
# This file is part of SENAITE.CORE.
#
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright 2018-2021 by it's authors.
# Some rights reserved, see README and LICENSE.
from AccessControl import ClassSecurityInfo
from bika.lims import api
from bika.lims import bikaMessageFactory as _
from bika.lims.browser.fields import ReferenceResultsField
from bika.lims.browser.fields import UIDReferenceField
from bika.lims.browser.widgets import DateTimeWidget as bika_DateTimeWidget
from bika.lims.browser.widgets import ReferenceResultsWidget
from bika.lims.config import PROJECTNAME
from bika.lims.content.bikaschema import BikaSchema
from bika.lims.interfaces import IDeactivable
from bika.lims.interfaces import IReferenceSample
from DateTime import DateTime
from Products.Archetypes.atapi import registerType
from Products.Archetypes.BaseFolder import BaseFolder
from Products.Archetypes.config import REFERENCE_CATALOG
from Products.Archetypes.Field import BooleanField
from Products.Archetypes.Field import ComputedField
from Products.Archetypes.Field import DateTimeField
from Products.Archetypes.Field import StringField
from Products.Archetypes.Field import TextField
from Products.Archetypes.Schema import Schema
from Products.Archetypes.Widget import BooleanWidget
from Products.Archetypes.Widget import ComputedWidget
from Products.Archetypes.Widget import StringWidget
from Products.Archetypes.Widget import TextAreaWidget
from Products.CMFCore.utils import getToolByName
from senaite.core.browser.widgets.referencewidget import ReferenceWidget
from senaite.core.catalog import SETUP_CATALOG
from zope.interface import implements
schema = BikaSchema.copy() + Schema((
UIDReferenceField(
"ReferenceDefinition",
schemata="Description",
allowed_types=("ReferenceDefinition",),
widget=ReferenceWidget(
label=_(
"label_referencesample_referencedefinition",
default="Reference Definition"),
description=_(
"description_referencesample_referencedefinition",
default="Select the reference definition for this sample"),
catalog=SETUP_CATALOG,
query={
"is_active": True,
"sort_on": "sortable_title",
"sort_order": "ascending"
},
),
),
BooleanField('Blank',
schemata = 'Description',
default = False,
widget = BooleanWidget(
label=_("Blank"),
description=_("Reference sample values are zero or 'blank'"),
),
),
BooleanField('Hazardous',
schemata = 'Description',
default = False,
widget = BooleanWidget(
label=_("Hazardous"),
description=_("Samples of this type should be treated as hazardous"),
),
),
UIDReferenceField(
"Manufacturer",
schemata="Description",
allowed_types=("Manufacturer",),
vocabulary="getManufacturers",
widget=ReferenceWidget(
label=_(
"label_referencesample_manufacturer",
default="Manufacturer"),
description=_(
"description_referencesample_manufacturer",
default="Select the manufacturer for this sample"),
catalog=SETUP_CATALOG,
query={
"is_active": True,
"sort_on": "sortable_title",
"sort_order": "ascending"
},
),
),
StringField('CatalogueNumber',
schemata = 'Description',
widget = StringWidget(
label=_("Catalogue Number"),
),
),
StringField('LotNumber',
schemata = 'Description',
widget = StringWidget(
label=_("Lot Number"),
),
),
TextField(
"Remarks",
allowable_content_types=("text/plain",),
schemata="Description",
widget=TextAreaWidget(
label=_("Remarks"),
)
),
DateTimeField('DateSampled',
schemata = 'Dates',
widget = bika_DateTimeWidget(
label=_("Date Sampled"),
),
),
DateTimeField('DateReceived',
schemata = 'Dates',
default_method = 'current_date',
widget = bika_DateTimeWidget(
label=_("Date Received"),
),
),
DateTimeField('DateOpened',
schemata = 'Dates',
widget = bika_DateTimeWidget(
label=_("Date Opened"),
),
),
DateTimeField('ExpiryDate',
schemata = 'Dates',
required = 1,
widget = bika_DateTimeWidget(
label=_("Expiry Date"),
),
),
DateTimeField('DateExpired',
schemata = 'Dates',
widget = bika_DateTimeWidget(
label=_("Date Expired"),
visible = {'edit':'hidden'},
),
),
DateTimeField('DateDisposed',
schemata = 'Dates',
widget = bika_DateTimeWidget(
label=_("Date Disposed"),
visible = {'edit':'hidden'},
),
),
ReferenceResultsField('ReferenceResults',
schemata = 'Reference Values',
required = 1,
subfield_validators = {
'result':'analysisspecs_validator',},
widget = ReferenceResultsWidget(
label=_("Expected Values"),
),
),
ComputedField('SupplierUID',
expression = 'context.aq_parent.UID()',
widget = ComputedWidget(
visible = False,
),
),
ComputedField('ReferenceDefinitionUID',
expression = 'here.getReferenceDefinition() and here.getReferenceDefinition().UID() or None',
widget = ComputedWidget(
visible = False,
),
),
))
schema['title'].schemata = 'Description'
class ReferenceSample(BaseFolder):
implements(IReferenceSample, IDeactivable)
security = ClassSecurityInfo()
schema = schema
_at_rename_after_creation = True
def _renameAfterCreation(self, check_auto_id=False):
from senaite.core.idserver import renameAfterCreation
renameAfterCreation(self)
security.declarePublic('current_date')
def current_date(self):
return DateTime()
security.declarePublic('getResultsRangeDict')
def getResultsRangeDict(self):
specs = {}
for spec in self.getReferenceResults():
uid = spec['uid']
specs[uid] = {}
specs[uid]['result'] = spec['result']
specs[uid]['min'] = spec.get('min', '')
specs[uid]['max'] = spec.get('max', '')
return specs
def getSupportedServices(self, only_uids=True):
"""Return a list with the services supported by this reference sample,
those for which there is a valid results range assigned in reference
results
:param only_uids: returns a list of uids or a list of objects
:return: list of uids or AnalysisService objects
"""
uids = map(lambda range: range['uid'], self.getReferenceResults())
uids = filter(api.is_uid, uids)
if only_uids:
return uids
brains = api.search({'UID': uids}, 'uid_catalog')
return map(api.get_object, brains)
security.declarePublic('getReferenceAnalyses')
def getReferenceAnalyses(self):
""" return all analyses linked to this reference sample """
return self.objectValues('ReferenceAnalysis')
security.declarePublic('getServices')
def getServices(self):
""" get all services for this Sample """
tool = getToolByName(self, REFERENCE_CATALOG)
services = []
for spec in self.getReferenceResults():
service = tool.lookupObject(spec['uid'])
services.append(service)
return services
def isValid(self):
"""
Returns if the current Reference Sample is valid. This is, the sample
hasn't neither been expired nor disposed.
"""
today = DateTime()
expiry_date = self.getExpiryDate()
if expiry_date and today > expiry_date:
return False
# TODO: Do We really need ExpiryDate + DateExpired? Any difference?
date_expired = self.getDateExpired()
if date_expired and today > date_expired:
return False
date_disposed = self.getDateDisposed()
if date_disposed and today > date_disposed:
return False
return True
# XXX workflow methods
def workflow_script_expire(self):
""" expire sample """
self.setDateExpired(DateTime())
self.reindexObject()
def workflow_script_dispose(self):
""" dispose sample """
self.setDateDisposed(DateTime())
self.reindexObject()
registerType(ReferenceSample, PROJECTNAME)