senaite.core.exportimport.setupdata   F
last analyzed

Complexity

Total Complexity 460

Size/Duplication

Total Lines 2431
Duplicated Lines 5.92 %

Importance

Changes 0
Metric Value
wmc 460
eloc 1903
dl 144
loc 2431
rs 0.8
c 0
b 0
f 0

87 Methods

Rating   Name   Duplication   Size   Complexity  
F Lab_Contacts.Import() 0 131 26
A Clients.Import() 0 23 4
B WorksheetImporter.get_object() 0 24 8
B Lab_Departments.Import() 0 29 6
A Lab_Products.Import() 0 10 3
A Sub_Groups.Import() 0 10 3
A Lab_Information.Import() 0 34 4
D WorksheetImporter.get_rows() 0 45 13
A WorksheetImporter.defer() 0 2 1
A WorksheetImporter.to_bool() 0 21 5
C WorksheetImporter.fill_addressfields() 0 24 9
A WorksheetImporter.to_float() 0 10 3
A WorksheetImporter.fill_contactfields() 0 24 4
A SetupDataSetList.__call__() 0 2 1
A WorksheetImporter.get_file_data() 0 12 3
A WorksheetImporter.Import() 0 2 1
A WorksheetImporter.__call__() 0 26 4
A WorksheetImporter.to_int() 0 10 3
A WorksheetImporter.__init__() 0 2 1
A Instrument_Types.Import() 0 8 3
B Sample_Points.Import() 0 32 8
A Analysis_Specifications.resolve_service() 0 13 2
A Reference_Definitions.Import() 0 16 3
A Sample_Conditions.Import() 0 11 3
A Reference_Samples.load_reference_analysis_interims() 18 18 5
D Instrument_Certifications.Import() 12 52 12
B Reference_Samples.load_reference_analyses() 0 29 5
A Preservations.Import() 0 9 3
D Worksheet_Templates.load_wst_layouts() 0 46 12
A Analysis_Requests.load_analysis_interims() 18 18 5
A Analysis_Profiles.Import() 0 26 3
B Instrument_Validations.Import() 29 31 7
C Reference_Definitions.load_reference_definition_results() 0 23 9
A Worksheet_Templates.load_wst_services() 0 14 5
A Setup.to_string_value() 0 4 2
A Worksheet_Templates.Import() 0 16 5
A Setup.get_field_value() 0 16 3
A Sample_Point_Sample_Types.Import() 0 14 4
A Storage_Locations.Import() 0 19 3
A Analysis_Services.get_instruments() 0 13 1
B Sample_Templates.load_sampletemplate_partitions() 0 25 7
B Analysis_Requests.Import() 0 38 6
D Instruments.Import() 23 83 11
A Reference_Samples.Import() 0 32 3
B Sample_Templates.Import() 0 35 5
B Setup.Import() 0 23 7
F Analysis_Services.Import() 0 132 12
A Analysis_Services.write_bucket() 0 7 2
A Analysis_Services.get_methods() 0 13 1
A Containers.Import() 0 24 4
A Sampling_Deviations.Import() 0 8 3
A Setup.to_fixedpoint_value() 0 2 1
B Client_Contacts.Import() 0 60 7
B Analysis_Categories.Import() 0 30 5
A Suppliers.Import() 0 24 3
A Setup.to_boolean_value() 0 2 1
C Calculations.Import() 0 46 10
F Analysis_Specifications.Import() 0 46 15
B Analysis_Profiles.load_analysis_profile_services() 0 20 7
A Calculations.get_interim_fields() 0 19 4
A Manufacturers.Import() 0 8 3
A Container_Types.Import() 0 9 3
B Setup.to_string_vocab_value() 0 22 7
A Setup.to_duration_value() 0 7 2
A Setup.to_reference_value() 0 12 3
B Supplier_Contacts.Import() 0 23 5
A Batch_Labels.Import() 0 7 3
A Instrument_Documents.Import() 0 8 3
C Analysis_Services.get_relations() 0 23 9
A Worksheet_Templates.__init__() 0 4 1
A Setup.to_integer_value() 0 2 1
A Sample_Templates.load_sampletemplate_services() 0 17 4
A Analysis_Services.load_result_options() 0 15 4
B Reference_Samples.load_reference_sample_results() 0 24 6
A Worksheet_Templates.load_definitions() 0 10 2
A ID_Prefixes.Import() 0 14 3
B Instrument_Schedule.Import() 0 31 6
B Invoice_Batches.Import() 0 20 5
A Analysis_Services.load_interim_fields() 0 18 4
B Instrument_Maintenance_Tasks.Import() 0 31 7
A Attachment_Types.Import() 0 9 3
A Analysis_Requests.load_analyses() 0 36 4
B Instrument_Calibrations.Import() 29 31 7
B Sample_Types.Import() 0 35 6
A Sample_Matrices.Import() 0 8 3
B Analysis_Services.load_service_uncertainties() 0 31 7
B Methods.Import() 11 40 8

6 Functions

Rating   Name   Duplication   Size   Complexity  
A lookup() 0 6 1
A Float() 0 6 2
A check_for_required_columns() 0 5 3
A get_addresses_from_row() 0 18 5
A read_file() 0 12 4
B addDocument() 0 44 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like senaite.core.exportimport.setupdata 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-2025 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import datetime
22
import os.path
23
import re
24
25
import transaction
26
from bika.lims import api
27
from bika.lims import bikaMessageFactory as _
28
from bika.lims import logger
29
from bika.lims.api import safe_unicode as u
30
from bika.lims.interfaces import ISetupDataSetList
31
from bika.lims.utils import getFromString
32
from bika.lims.utils import tmpID
33
from bika.lims.utils import to_unicode
34
from bika.lims.utils import to_utf8
35
from bika.lims.utils.analysis import create_analysis
36
from pkg_resources import resource_filename
37
from Products.Archetypes.event import ObjectInitializedEvent
38
from Products.CMFCore.utils import getToolByName
39
from Products.CMFPlone.utils import _createObjectByType
40
from Products.CMFPlone.utils import safe_unicode
41
from senaite.core.catalog import CLIENT_CATALOG
42
from senaite.core.catalog import CONTACT_CATALOG
43
from senaite.core.catalog import SENAITE_CATALOG
44
from senaite.core.catalog import SETUP_CATALOG
45
from senaite.core.exportimport.dataimport import SetupDataSetList as SDL
46
from senaite.core.i18n import translate as t
47
from senaite.core.idserver import renameAfterCreation
48
from senaite.core.schema.addressfield import BILLING_ADDRESS
49
from senaite.core.schema.addressfield import PHYSICAL_ADDRESS
50
from senaite.core.schema.addressfield import POSTAL_ADDRESS
51
from zope.event import notify
52
from zope.interface import implements
53
54
UID_CATALOG = "uid_catalog"
55
56
57
def get_addresses_from_row(row):
58
    """Fills the address fields for the specified object if allowed:
59
    PhysicalAddress, PostalAddress, CountryState, BillingAddress
60
    """
61
    types = [PHYSICAL_ADDRESS, POSTAL_ADDRESS, BILLING_ADDRESS]
62
    keys = ["Address", "City", "Zip", "Country"]
63
64
    address_list = []
65
    for address_type in types:
66
        address_item = {"type": address_type}
67
        for key in keys:
68
            field_name = "%s_%s" % (address_type.capitalize(), key)
69
            value = str(row.get(field_name, ""))
70
            if value:
71
                address_item.update({key.lower(): value})
72
        if len(address_item.keys()) > 1:
73
            address_list.append(address_item)
74
    return address_list
75
76
77
def lookup(context, portal_type, **kwargs):
78
    at = getToolByName(context, 'archetype_tool')
79
    catalog = at.catalog_map.get(portal_type, [None])[0] or UID_CATALOG
80
    catalog = getToolByName(context, catalog)
81
    kwargs['portal_type'] = portal_type
82
    return catalog(**kwargs)[0].getObject()
83
84
85
def check_for_required_columns(name, data, required):
86
    for column in required:
87
        if not data.get(column, None):
88
            message = _("%s has no '%s' column." % (name, column))
89
            raise Exception(t(message))
90
91
92
def Float(thing):
93
    try:
94
        f = float(thing)
95
    except ValueError:
96
        f = 0.0
97
    return f
98
99
100
def read_file(path):
101
    if os.path.isfile(path):
102
        return open(path, "rb").read()
103
    allowed_ext = ['pdf', 'jpg', 'jpeg', 'png', 'gif', 'ods', 'odt',
104
                   'xlsx', 'doc', 'docx', 'xls', 'csv', 'txt']
105
    allowed_ext += [e.upper() for e in allowed_ext]
106
    for e in allowed_ext:
107
        out = '%s.%s' % (path, e)
108
        if os.path.isfile(out):
109
            return open(out, "rb").read()
110
    raise IOError("File not found: %s. Allowed extensions: %s" %
111
                  (path, ','.join(allowed_ext)))
112
113
114
class SetupDataSetList(SDL):
115
116
    implements(ISetupDataSetList)
117
118
    def __call__(self):
119
        return SDL.__call__(self, projectname="bika.lims")
120
121
122
class WorksheetImporter(object):
123
124
    """Use this as a base, for normal tabular data sheet imports.
125
    """
126
127
    def __init__(self, context):
128
        self.adapter_context = context
129
130
    def __call__(self, lsd, workbook, dataset_project, dataset_name):
131
        self.lsd = lsd
132
        self.context = lsd.context
133
        self.workbook = workbook
134
        self.sheetname = self.__class__.__name__.replace("_", " ")
135
        try:
136
            self.worksheet = workbook[self.sheetname]
137
        except KeyError:
138
            self.worksheet = None
139
        self.dataset_project = dataset_project
140
        self.dataset_name = dataset_name
141
        if self.worksheet:
142
            logger.info("Loading {0}.{1}: {2}".format(
143
                self.dataset_project, self.dataset_name, self.sheetname))
144
            try:
145
                self.Import()
146
            except IOError:
147
                # The importer must omit the files not found inside the server filesystem (bika/lims/setupdata/test/
148
                # if the file is loaded from 'select existing file' or bika/lims/setupdata/uploaded if it's loaded from
149
                # 'Load from file') and finishes the import without errors. https://jira.bikalabs.com/browse/LIMS-1624
150
                warning = "Error while loading attached file from %s. The file will not be uploaded into the system."
151
                logger.warning(warning, self.sheetname)
152
                self.context.plone_utils.addPortalMessage("Error while loading some attached files. "
153
                                                          "The files weren't uploaded into the system.")
154
        else:
155
            logger.info("No records found: '{0}'".format(self.sheetname))
156
157
    def get_rows(self, startrow=3, worksheet=None):
158
        """Returns a generator for all rows in a sheet.
159
           Each row contains a dictionary where the key is the value of the
160
           first row of the sheet for each column.
161
           The data values are returned in utf-8 format.
162
           Starts to consume data from startrow
163
        """
164
165
        headers = []
166
        row_nr = 0
167
        worksheet = worksheet if worksheet else self.worksheet
168
        for row in worksheet.rows:  # .iter_rows():
169
            row_nr += 1
170
            if row_nr == 1:
171
                # headers = [cell.internal_value for cell in row]
172
                headers = [cell.value for cell in row]
173
                continue
174
            if row_nr % 1000 == 0:
175
                transaction.savepoint()
176
            if row_nr <= startrow:
177
                continue
178
            # row = [_c(cell.internal_value).decode('utf-8') for cell in row]
179
            new_row = []
180
            for cell in row:
181
                value = cell.value
182
                if value is None:
183
                    value = ''
184
                if isinstance(value, unicode):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable unicode does not seem to be defined.
Loading history...
185
                    value = value.encode('utf-8')
186
                # Strip any space, \t, \n, or \r characters from the left-hand
187
                # side, right-hand side, or both sides of the string
188
                if isinstance(value, str):
189
                    value = value.strip(' \t\n\r')
190
                new_row.append(value)
191
            row = dict(zip(headers, new_row))
192
193
            # parse out addresses
194
            for add_type in ['Physical', 'Postal', 'Billing']:
195
                row[add_type] = {}
196
                if add_type + "_Address" in row:
197
                    for key in ['Address', 'City', 'State', 'District', 'Zip', 'Country']:
198
                        row[add_type][key] = str(
199
                            row.get("%s_%s" % (add_type, key), ''))
200
201
            yield row
202
203
    def get_file_data(self, filename):
204
        if filename:
205
            try:
206
                path = resource_filename(
207
                    self.dataset_project,
208
                    "setupdata/%s/%s" % (self.dataset_name, filename))
209
                file_data = open(path, "rb").read()
210
            except Exception:
211
                file_data = None
212
        else:
213
            file_data = None
214
        return file_data
215
216
    def to_bool(self, value):
217
        """ Converts a sheet string value to a boolean value.
218
            Needed because of utf-8 conversions
219
        """
220
221
        try:
222
            value = value.lower()
223
        except Exception:
224
            pass
225
        try:
226
            value = value.encode('utf-8')
227
        except Exception:
228
            pass
229
        try:
230
            value = int(value)
231
        except Exception:
232
            pass
233
        if value in ('true', 1):
234
            return True
235
        else:
236
            return False
237
238
    def to_int(self, value, default=0):
239
        """ Converts a value o a int. Returns default if the conversion fails.
240
        """
241
        try:
242
            return int(value)
243
        except ValueError:
244
            try:
245
                return int(default)
246
            except Exception:
247
                return 0
248
249
    def to_float(self, value, default=0):
250
        """ Converts a value o a float. Returns default if the conversion fails.
251
        """
252
        try:
253
            return float(value)
254
        except ValueError:
255
            try:
256
                return float(default)
257
            except Exception:
258
                return 0.0
259
260
    def defer(self, **kwargs):
261
        self.lsd.deferred.append(kwargs)
262
263
    def Import(self):
264
        """ Override this.
265
        XXX Simple generic sheet importer
266
        """
267
268
    def fill_addressfields(self, row, obj):
269
        """ Fills the address fields for the specified object if allowed:
270
            PhysicalAddress, PostalAddress, CountryState, BillingAddress
271
        """
272
        addresses = {}
273
        for add_type in ["Physical", "Postal", "Billing", "CountryState"]:
274
            addresses[add_type] = {}
275
            for key in ["Address", "City", "State", "District", "Zip", "Country"]:
276
                addresses[add_type][key.lower()] = str(
277
                    row.get("%s_%s" % (add_type, key), ""))
278
279
        if addresses["CountryState"]["country"] == "" \
280
                and addresses["CountryState"]["state"] == "":
281
            addresses["CountryState"]["country"] = addresses["Physical"]["country"]
282
            addresses["CountryState"]["state"] = addresses["Physical"]["state"]
283
284
        if hasattr(obj, "setPhysicalAddress"):
285
            obj.setPhysicalAddress(addresses["Physical"])
286
        if hasattr(obj, "setPostalAddress"):
287
            obj.setPostalAddress(addresses["Postal"])
288
        if hasattr(obj, "setCountryState"):
289
            obj.setCountryState(addresses["CountryState"])
290
        if hasattr(obj, "setBillingAddress"):
291
            obj.setBillingAddress(addresses["Billing"])
292
293
    def fill_contactfields(self, row, obj):
294
        """ Fills the contact fields for the specified object if allowed:
295
            EmailAddress, Phone, Fax, BusinessPhone, BusinessFax, HomePhone,
296
            MobilePhone
297
        """
298
        fieldnames = [
299
            "EmailAddress",
300
            "Phone",
301
            "Fax",
302
            "BusinessPhone",
303
            "BusinessFax",
304
            "HomePhone",
305
            "MobilePhone",
306
        ]
307
        for fieldname in fieldnames:
308
            try:
309
                getattr(obj, fieldname)
310
            except AttributeError:
311
                if fieldname in row:
312
                    logger.info("Address field %s not found on %s" %
313
                                (fieldname, obj))
314
                continue
315
            value = row.get(fieldname, "")
316
            setattr(obj, fieldname, value)
317
318
    def get_object(self, catalog, portal_type, title=None, **kwargs):
319
        """This will return an object from the catalog.
320
        Logs a message and returns None if no object or multiple objects found.
321
        All keyword arguments are passed verbatim to the contentFilter
322
        """
323
        if not title and not kwargs:
324
            return None
325
        contentFilter = {"portal_type": portal_type}
326
        if title:
327
            contentFilter['title'] = to_unicode(title)
328
        contentFilter.update(kwargs)
329
        brains = catalog(contentFilter)
330
        if len(brains) > 1:
331
            logger.info("More than one object found for %s" % contentFilter)
332
            return None
333
        elif len(brains) == 0:
334
            if portal_type == 'AnalysisService':
335
                brains = catalog(portal_type=portal_type, getKeyword=title)
336
                if brains:
337
                    return brains[0].getObject()
338
            logger.info("No objects found for %s" % contentFilter)
339
            return None
340
        else:
341
            return brains[0].getObject()
342
343
344
class Sub_Groups(WorksheetImporter):
345
346
    def Import(self):
347
348
        container = self.context.setup.subgroups
349
        for row in self.get_rows(3):
350
            title = row.get("title")
351
            if not title:
352
                continue
353
            api.create(container, "SubGroup",
354
                       title=title, description=row.get("description"),
355
                       SortKey=row.get("SortKey"))
356
357
358
class Lab_Information(WorksheetImporter):
359
360
    def Import(self):
361
        laboratory = self.context.bika_setup.laboratory
362
        values = {}
363
        for row in self.get_rows(3):
364
            values[row['Field']] = row['Value']
365
366
        if values['AccreditationBodyLogo']:
367
            path = resource_filename(
368
                self.dataset_project,
369
                "setupdata/%s/%s" % (self.dataset_name,
370
                                     values['AccreditationBodyLogo']))
371
            try:
372
                file_data = read_file(path)
373
            except Exception as msg:
374
                file_data = None
375
                logger.warning(msg[0] + " Error on sheet: " + self.sheetname)
376
        else:
377
            file_data = None
378
379
        laboratory.edit(
380
            Name=values['Name'],
381
            LabURL=values['LabURL'],
382
            Confidence=values['Confidence'],
383
            LaboratoryAccredited=self.to_bool(values['LaboratoryAccredited']),
384
            AccreditationBodyLong=values['AccreditationBodyLong'],
385
            AccreditationBody=values['AccreditationBody'],
386
            AccreditationBodyURL=values['AccreditationBodyURL'],
387
            Accreditation=values['Accreditation'],
388
            AccreditationReference=values['AccreditationReference'],
389
            AccreditationBodyLogo=file_data,
390
            TaxNumber=values['TaxNumber'],
391
        )
392
        self.fill_contactfields(values, laboratory)
393
        self.fill_addressfields(values, laboratory)
394
395
396
class Lab_Contacts(WorksheetImporter):
397
398
    def Import(self):
399
        folder = self.context.bika_setup.bika_labcontacts
400
        portal_groups = getToolByName(self.context, 'portal_groups')
401
        portal_registration = getToolByName(
402
            self.context, 'portal_registration')
403
        rownum = 2
404
        for row in self.get_rows(3):
405
            rownum += 1
406
            if not row.get('Firstname', None):
407
                continue
408
409
            # Username already exists?
410
            username = row.get('Username', '')
411
            fullname = ('%s %s' %
412
                        (row['Firstname'], row.get('Surname', ''))).strip()
413
            if username:
414
                username = safe_unicode(username).encode('utf-8')
415
                bsc = getToolByName(self.context, SETUP_CATALOG)
416
                exists = [o.getObject() for o in bsc(
417
                    portal_type="LabContact") if o.getObject().getUsername() == username]
418
                if exists:
419
                    error = "Lab Contact: username '{0}' in row {1} already exists. This contact will be omitted.".format(
420
                        username, str(rownum))
421
                    logger.error(error)
422
                    continue
423
424
            # Is there a signature file defined? Try to get the file first.
425
            signature = None
426
            if row.get('Signature'):
427
                signature = self.get_file_data(row['Signature'])
428
                if not signature:
429
                    warning = "Lab Contact: Cannot load the signature file '{0}' for user '{1}'. The contact will be created, but without a signature image".format(
430
                        row['Signature'], username)
431
                    logger.warning(warning)
432
433
            obj = _createObjectByType("LabContact", folder, tmpID())
434
            obj.edit(
435
                title=fullname,
436
                Salutation=row.get('Salutation', ''),
437
                Firstname=row['Firstname'],
438
                Surname=row.get('Surname', ''),
439
                JobTitle=row.get('JobTitle', ''),
440
                Username=row.get('Username', ''),
441
                Signature=signature
442
            )
443
            obj.unmarkCreationFlag()
444
            renameAfterCreation(obj)
445
            notify(ObjectInitializedEvent(obj))
446
            self.fill_contactfields(row, obj)
447
            self.fill_addressfields(row, obj)
448
449
            if row['Department_title']:
450
                self.defer(src_obj=obj,
451
                           src_field='Department',
452
                           dest_catalog=SETUP_CATALOG,
453
                           dest_query={'portal_type': 'Department',
454
                                       'title': row['Department_title']}
455
                           )
456
457
            # Create Plone user
458
            if not row['Username']:
459
                warn = "Lab Contact: No username defined for user '{0}' in row {1}. Contact created, but without access credentials.".format(
460
                    fullname, str(rownum))
461
                logger.warning(warn)
462
            if not row.get('EmailAddress', ''):
463
                warn = "Lab Contact: No Email defined for user '{0}' in row {1}. Contact created, but without access credentials.".format(
464
                    fullname, str(rownum))
465
                logger.warning(warn)
466
467
            if (row['Username'] and row.get('EmailAddress', '')):
468
                username = safe_unicode(row['Username']).encode('utf-8')
469
                passw = row['Password']
470
                if not passw:
471
                    passw = username
472
                    warn = ("Lab Contact: No password defined for user '{0}' in row {1}."
473
                            " Password established automatically to '{2}'").format(username, str(rownum), passw)
474
                    logger.warning(warn)
475
476
                try:
477
                    member = portal_registration.addMember(
478
                        username,
479
                        passw,
480
                        properties={
481
                            'username': username,
482
                            'email': row['EmailAddress'],
483
                            'fullname': fullname}
484
                    )
485
                except Exception as msg:
486
                    logger.error(
487
                        "Client Contact: Error adding user (%s): %s" % (msg, username))
488
                    continue
489
490
                groups = row.get('Groups', '')
491
                if not groups:
492
                    warn = "Lab Contact: No groups defined for user '{0}' in row {1}. Group established automatically to 'Analysts'".format(
493
                        username, str(rownum))
494
                    logger.warning(warn)
495
                    groups = 'Analysts'
496
497
                group_ids = [g.strip() for g in groups.split(',')]
498
                # Add user to all specified groups
499
                for group_id in group_ids:
500
                    group = portal_groups.getGroupById(group_id)
501
                    if group:
502
                        group.addMember(username)
503
                roles = row.get('Roles', '')
504
                if roles:
505
                    role_ids = [r.strip() for r in roles.split(',')]
506
                    # Add user to all specified roles
507
                    for role_id in role_ids:
508
                        member._addRole(role_id)
509
                # If user is in LabManagers, add Owner local role on clients
510
                # folder
511
                if 'LabManager' in group_ids:
512
                    self.context.clients.manage_setLocalRoles(
513
                        username, ['Owner', ])
514
515
        # Now we have the lab contacts registered, try to assign the managers
516
        # to each department if required
517
        sheet = self.workbook["Lab Departments"]
518
        bsc = getToolByName(self.context, SETUP_CATALOG)
519
        for row in self.get_rows(3, sheet):
520
            if row['title'] and row['LabContact_Username']:
521
                dept = self.get_object(bsc, "Department", row.get('title'))
522
                if dept and not dept.getManager():
523
                    username = safe_unicode(
524
                        row['LabContact_Username']).encode('utf-8')
525
                    exists = [o.getObject() for o in bsc(
526
                        portal_type="LabContact") if o.getObject().getUsername() == username]
527
                    if exists:
528
                        dept.setManager(exists[0].UID())
529
530
531
class Lab_Departments(WorksheetImporter):
532
    """Import Lab Departments
533
    """
534
535
    def Import(self):
536
        setup = api.get_senaite_setup()
537
        container = setup.departments
538
        cat = getToolByName(self.context, CONTACT_CATALOG)
539
        lab_contacts = [o.getObject() for o in cat(portal_type="LabContact")]
540
        for row in self.get_rows(3):
541
            title = row.get("title")
542
            description = row.get("description")
543
            username = row.get("LabContact_Username")
544
            manager = None
545
546
            if not title:
547
                continue
548
549
            obj = api.create(container,
550
                             "Department",
551
                             title=title,
552
                             description=description)
553
554
            for contact in lab_contacts:
555
                if contact.getUsername() == username:
556
                    manager = contact
557
                    break
558
            if manager:
559
                obj.setManager(manager.UID())
560
            else:
561
                message = "Department: lookup of '%s' in LabContacts" \
562
                    "/Username failed." % username
563
                logger.info(message)
564
565
566
class Lab_Products(WorksheetImporter):
567
568
    def Import(self):
569
        # Refer to the default folder
570
        container = self.context.setup.labproducts
571
        # Iterate through the rows
572
        for row in self.get_rows(3):
573
            title = row.get("title")
574
            if not title:
575
                continue
576
            api.create(container, "LabProduct",
577
                       title=title, description=row.get("description"))
578
579
580
class Clients(WorksheetImporter):
581
582
    def Import(self):
583
        folder = self.context.clients
584
        for row in self.get_rows(3):
585
            obj = _createObjectByType("Client", folder, tmpID())
586
            if not row['Name']:
587
                message = "Client %s has no Name"
588
                raise Exception(message)
589
            if not row['ClientID']:
590
                message = "Client %s has no Client ID"
591
                raise Exception(message)
592
            obj.edit(Name=row['Name'],
593
                     ClientID=row['ClientID'],
594
                     MemberDiscountApplies=row[
595
                         'MemberDiscountApplies'] and True or False,
596
                     BulkDiscount=row['BulkDiscount'] and True or False,
597
                     TaxNumber=row.get('TaxNumber', ''),
598
                     AccountNumber=row.get('AccountNumber', '')
599
                     )
600
            self.fill_contactfields(row, obj)
601
            self.fill_addressfields(row, obj)
602
            obj.unmarkCreationFlag()
603
            renameAfterCreation(obj)
604
            notify(ObjectInitializedEvent(obj))
605
606
607
class Client_Contacts(WorksheetImporter):
608
609
    def Import(self):
610
        cat = api.get_tool(CLIENT_CATALOG)
611
        for row in self.get_rows(3):
612
            client = cat(portal_type="Client", getName=row["Client_title"])
613
            if len(client) == 0:
614
                client_contact = "%(Firstname)s %(Surname)s" % row
615
                error = "Client invalid: '%s'. " \
616
                        "The Client Contact %s will not be uploaded."
617
                logger.error(error, row["Client_title"], client_contact)
618
                continue
619
            client = client[0].getObject()
620
621
            def u_row_get(name):
622
                return api.safe_unicode(row.get(name, ""))
623
624
            contact = api.create(
625
                client, "Contact",
626
                salutation=u_row_get("Salutation"),
627
                firstname=u_row_get("Firstname"),
628
                surname=u_row_get("Surname"),
629
                username=u_row_get("Username"),
630
                job_title=u_row_get("JobTitle"),
631
                department=u_row_get("Department"),
632
            )
633
634
            self.fill_contactfields(row, contact)
635
            self.fill_addressfields(row, contact)
636
637
            # CC Contacts
638
            if row["CCContacts"]:
639
                names = [x.strip() for x in row["CCContacts"].split(",")]
640
                for _fullname in names:
641
                    self.defer(src_obj=contact,
642
                               src_field="cc_contact",
643
                               dest_catalog=CONTACT_CATALOG,
644
                               dest_query={
645
                                   "portal_type": "Contact",
646
                                   "getFullname": _fullname,
647
                               })
648
            # Create Plone user
649
            username = u_row_get("Username").encode("utf-8")
650
            password = u_row_get("Password").encode("utf-8")
651
            fullname = "%(Firstname)s %(Surname)s" % row
652
            if (username):
653
                try:
654
                    self.context.portal_registration.addMember(
655
                        username,
656
                        password,
657
                        properties={
658
                            "username": username,
659
                            "email": row["EmailAddress"],
660
                            "fullname": fullname,
661
                        }
662
                    )
663
                    # This will add the user to a client specific group which
664
                    # has the "Client" role assigned
665
                    contact.setUser(username)
666
667
                except Exception as msg:
668
                    logger.info("Error adding user (%s): %s" % (msg, username))
669
670
671
class Container_Types(WorksheetImporter):
672
673
    def Import(self):
674
        container = self.context.setup.containertypes
675
        for row in self.get_rows(3):
676
            title = row.get("title")
677
            if not title:
678
                continue
679
680
            api.create(container, "ContainerType",
681
                       title=title, description=row.get("description"))
682
683
684
class Preservations(WorksheetImporter):
685
686
    def Import(self):
687
        container = self.context.setup.samplepreservations
688
        for row in self.get_rows(3):
689
            title = row.get("title")
690
            if not title:
691
                continue
692
693
            api.create(container, "SamplePreservation",
694
                       title=title, description=row.get("description"))
695
696
697
class Containers(WorksheetImporter):
698
699
    def Import(self):
700
        bsc = getToolByName(self.context, SETUP_CATALOG)
701
        container = self.context.setup.samplecontainers
702
        for row in self.get_rows(3):
703
            title = row.get("title")
704
            if not title:
705
                continue
706
707
            description = row.get("description", "")
708
            capacity = row.get("Capacity", 0)
709
            pre_preserved = self.to_bool(row["PrePreserved"])
710
            containertype = None
711
            container_type_title = row.get("ContainerType_title", "")
712
713
            if container_type_title:
714
                containertype = self.get_object(
715
                    bsc, "ContainerType", container_type_title)
716
717
            api.create(container, "SampleContainer",
718
                       title=title,
719
                       description=description,
720
                       capacity=capacity,
721
                       pre_preserved=pre_preserved,
722
                       containertype=containertype)
723
724
725
class Suppliers(WorksheetImporter):
726
727
    def Import(self):
728
        container = self.context.setup.suppliers
729
        for row in self.get_rows(3):
730
            title = row.get("Name")
731
            if not title:
732
                continue
733
734
            api.create(container, "Supplier",
735
                       title=title,
736
                       description=row.get("description"),
737
                       tax_number=row.get("TaxNumber"),
738
                       phone=row.get("Phone", ""),
739
                       fax=row.get("Fax", ""),
740
                       email=row.get("EmailAddress", ""),
741
                       account_type=row.get("AccountType", {}),
742
                       account_name=row.get("AccountName", {}),
743
                       account_number=row.get("AccountNumber", ''),
744
                       bank_name=row.get("BankName", ""),
745
                       bank_branch=row.get("BankBranch", ""),
746
                       swift_code=row.get("SWIFTcode", ""),
747
                       iban=row.get("IBN", ""),
748
                       nib=row.get("NIB", ""),
749
                       website=row.get("Website", ""),
750
                       address=get_addresses_from_row(row))
751
752
753
class Supplier_Contacts(WorksheetImporter):
754
755
    def Import(self):
756
        bsc = getToolByName(self.context, SETUP_CATALOG)
757
        for row in self.get_rows(3):
758
            if not row['Supplier_Name']:
759
                continue
760
            if not row['Firstname']:
761
                continue
762
            folder = bsc(portal_type="Supplier",
763
                         Title=row['Supplier_Name'])
764
            if not folder:
765
                continue
766
            folder = folder[0].getObject()
767
            obj = _createObjectByType("SupplierContact", folder, tmpID())
768
            obj.edit(
769
                Firstname=row['Firstname'],
770
                Surname=row.get('Surname', ''),
771
                Username=row.get('Username')
772
            )
773
            self.fill_contactfields(row, obj)
774
            self.fill_addressfields(row, obj)
775
            obj.unmarkCreationFlag()
776
            renameAfterCreation(obj)
777
            notify(ObjectInitializedEvent(obj))
778
779
780
class Manufacturers(WorksheetImporter):
781
782
    def Import(self):
783
        container = self.context.setup.manufacturers
784
        for row in self.get_rows(3):
785
            title = row.get("title")
786
            if not title:
787
                continue
788
            api.create(container, "Manufacturer",
789
                       title=title, description=row.get("description"))
790
791
792
class Instrument_Types(WorksheetImporter):
793
794
    def Import(self):
795
        container = self.context.setup.instrumenttypes
796
        for row in self.get_rows(3):
797
            title = row.get("title")
798
            if not title:
799
                continue
800
            api.create(container, "InstrumentType",
801
                       title=title, description=row.get("description"))
802
803
804
class Instruments(WorksheetImporter):
805
806
    def Import(self):
807
        folder = self.context.bika_setup.bika_instruments
808
        bsc = getToolByName(self.context, SETUP_CATALOG)
809
        for row in self.get_rows(3):
810
            if ('Type' not in row
811
                or 'Supplier' not in row
812
                    or 'Brand' not in row):
813
                logger.info(
814
                    "Unable to import '%s'. Missing supplier, manufacturer or type" % row.get('title', ''))
815
                continue
816
817
            obj = _createObjectByType("Instrument", folder, tmpID())
818
819
            obj.edit(
820
                title=row.get('title', ''),
821
                AssetNumber=row.get('assetnumber', ''),
822
                description=row.get('description', ''),
823
                Type=row.get('Type', ''),
824
                Brand=row.get('Brand', ''),
825
                Model=row.get('Model', ''),
826
                SerialNo=row.get('SerialNo', ''),
827
                DataInterface=row.get('DataInterface', ''),
828
                Location=row.get('Location', ''),
829
                InstallationDate=row.get('Instalationdate', ''),
830
                UserManualID=row.get('UserManualID', ''),
831
            )
832
            instrumenttype = self.get_object(
833
                bsc, 'InstrumentType', title=row.get('Type'))
834
            manufacturer = self.get_object(
835
                bsc, 'Manufacturer', title=row.get('Brand'))
836
            supplier = self.get_object(
837
                bsc, 'Supplier', title=row.get('Supplier', ''))
838
            method = self.get_object(bsc, 'Method', title=row.get('Method'))
839
            obj.setInstrumentType(instrumenttype)
840
            obj.setManufacturer(manufacturer)
841
            obj.setSupplier(supplier)
842
            if method:
843
                obj.setMethods([method])
844
                obj.setMethod(method)
845
846
            # Attaching the instrument's photo
847 View Code Duplication
            if row.get('Photo', None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
848
                path = resource_filename(
849
                    self.dataset_project,
850
                    "setupdata/%s/%s" % (self.dataset_name,
851
                                         row['Photo'])
852
                )
853
                try:
854
                    file_data = read_file(path)
855
                    obj.setPhoto(file_data)
856
                except Exception as msg:
857
                    file_data = None
858
                    logger.warning(
859
                        msg[0] + " Error on sheet: " + self.sheetname)
860
861
            # Attaching the Installation Certificate if exists
862 View Code Duplication
            if row.get('InstalationCertificate', None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
863
                path = resource_filename(
864
                    self.dataset_project,
865
                    "setupdata/%s/%s" % (self.dataset_name,
866
                                         row['InstalationCertificate'])
867
                )
868
                try:
869
                    file_data = read_file(path)
870
                    obj.setInstallationCertificate(file_data)
871
                except Exception as msg:
872
                    logger.warning(
873
                        msg[0] + " Error on sheet: " + self.sheetname)
874
875
            # Attaching the Instrument's manual if exists
876
            if row.get("UserManualFile", None):
877
                row_dict = {
878
                    "DocumentID": row.get("UserManualID", "manual"),
879
                    "DocumentVersion": "",
880
                    "DocumentoLcation": "",
881
                    "DocumentType": "Manual",
882
                    "File": row.get("UserManualFile", None)
883
                }
884
                addDocument(self, row_dict, obj)
885
886
            obj.unmarkCreationFlag()
887
            renameAfterCreation(obj)
888
            notify(ObjectInitializedEvent(obj))
889
890
891 View Code Duplication
class Instrument_Validations(WorksheetImporter):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
892
893
    def Import(self):
894
        bsc = getToolByName(self.context, SETUP_CATALOG)
895
        for row in self.get_rows(3):
896
            if not row.get('instrument', None) or not row.get('title', None):
897
                continue
898
899
            folder = self.get_object(bsc, 'Instrument', row.get('instrument'))
900
            if folder:
901
                obj = _createObjectByType(
902
                    "InstrumentValidation", folder, tmpID())
903
                obj.edit(
904
                    title=row['title'],
905
                    DownFrom=row.get('downfrom', ''),
906
                    DownTo=row.get('downto', ''),
907
                    Validator=row.get('validator', ''),
908
                    Considerations=row.get('considerations', ''),
909
                    WorkPerformed=row.get('workperformed', ''),
910
                    Remarks=row.get('remarks', ''),
911
                    DateIssued=row.get('DateIssued', ''),
912
                    ReportID=row.get('ReportID', '')
913
                )
914
                # Getting lab contacts
915
                bsc = getToolByName(self.context, SETUP_CATALOG)
916
                lab_contacts = [o.getObject() for o in bsc(
917
                    portal_type="LabContact", is_active=True)]
918
                for contact in lab_contacts:
919
                    if contact.getFullname() == row.get('Worker', ''):
920
                        obj.setWorker(contact.UID())
921
                obj.unmarkCreationFlag()
922
                renameAfterCreation(obj)
923
                notify(ObjectInitializedEvent(obj))
924
925
926 View Code Duplication
class Instrument_Calibrations(WorksheetImporter):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
927
928
    def Import(self):
929
        bsc = getToolByName(self.context, SETUP_CATALOG)
930
        for row in self.get_rows(3):
931
            if not row.get('instrument', None) or not row.get('title', None):
932
                continue
933
934
            folder = self.get_object(bsc, 'Instrument', row.get('instrument'))
935
            if folder:
936
                obj = _createObjectByType(
937
                    "InstrumentCalibration", folder, tmpID())
938
                obj.edit(
939
                    title=row['title'],
940
                    DownFrom=row.get('downfrom', ''),
941
                    DownTo=row.get('downto', ''),
942
                    Calibrator=row.get('calibrator', ''),
943
                    Considerations=row.get('considerations', ''),
944
                    WorkPerformed=row.get('workperformed', ''),
945
                    Remarks=row.get('remarks', ''),
946
                    DateIssued=row.get('DateIssued', ''),
947
                    ReportID=row.get('ReportID', '')
948
                )
949
                # Gettinginstrument lab contacts
950
                bsc = getToolByName(self.context, SETUP_CATALOG)
951
                lab_contacts = [o.getObject() for o in bsc(
952
                    portal_type="LabContact", nactive_state='active')]
953
                for contact in lab_contacts:
954
                    if contact.getFullname() == row.get('Worker', ''):
955
                        obj.setWorker(contact.UID())
956
                obj.unmarkCreationFlag()
957
                renameAfterCreation(obj)
958
                notify(ObjectInitializedEvent(obj))
959
960
961
class Instrument_Certifications(WorksheetImporter):
962
963
    def Import(self):
964
        bsc = getToolByName(self.context, SETUP_CATALOG)
965
        for row in self.get_rows(3):
966
            if not row['instrument'] or not row['title']:
967
                continue
968
969
            folder = self.get_object(
970
                bsc, 'Instrument', row.get('instrument', ''))
971
            if folder:
972
                obj = _createObjectByType(
973
                    "InstrumentCertification", folder, tmpID())
974
                today = datetime.date.today()
975
                certificate_expire_date = today.strftime('%d/%m') + '/' + str(today.year+1) \
976
                    if row.get('validto', '') == '' else row.get('validto')
977
                certificate_start_date = today.strftime('%d/%m/%Y') \
978
                    if row.get('validfrom', '') == '' else row.get('validfrom')
979
                obj.edit(
980
                    title=row['title'],
981
                    AssetNumber=row.get('assetnumber', ''),
982
                    Date=row.get('date', ''),
983
                    ValidFrom=certificate_start_date,
984
                    ValidTo=certificate_expire_date,
985
                    Agency=row.get('agency', ''),
986
                    Remarks=row.get('remarks', ''),
987
                )
988
                # Attaching the Report Certificate if exists
989 View Code Duplication
                if row.get('report', None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
990
                    path = resource_filename(
991
                        self.dataset_project,
992
                        "setupdata/%s/%s" % (self.dataset_name,
993
                                             row['report'])
994
                    )
995
                    try:
996
                        file_data = read_file(path)
997
                        obj.setDocument(file_data)
998
                    except Exception as msg:
999
                        file_data = None
1000
                        logger.warning(
1001
                            msg[0] + " Error on sheet: " + self.sheetname)
1002
1003
                # Getting lab contacts
1004
                bsc = getToolByName(self.context, SETUP_CATALOG)
1005
                lab_contacts = [o.getObject() for o in bsc(
1006
                    portal_type="LabContact", nactive_state='active')]
1007
                for contact in lab_contacts:
1008
                    if contact.getFullname() == row.get('preparedby', ''):
1009
                        obj.setPreparator(contact.UID())
1010
                    if contact.getFullname() == row.get('approvedby', ''):
1011
                        obj.setValidator(contact.UID())
1012
                obj.unmarkCreationFlag()
1013
                renameAfterCreation(obj)
1014
                notify(ObjectInitializedEvent(obj))
1015
1016
1017
class Instrument_Documents(WorksheetImporter):
1018
1019
    def Import(self):
1020
        bsc = getToolByName(self.context, SETUP_CATALOG)
1021
        for row in self.get_rows(3):
1022
            if not row.get("instrument", ""):
1023
                continue
1024
            folder = self.get_object(
1025
                bsc, "Instrument", row.get("instrument", ""))
1026
            addDocument(self, row, folder)
1027
1028
1029
def addDocument(self, row_dict, folder):
1030
    """This function adds a multifile object to the instrument folder
1031
1032
    :param row_dict: the dictionary which contains the document information
1033
    :param folder: the instrument object
1034
    """
1035
    if folder:
1036
        # This content type need a file
1037
        if row_dict.get("File", None):
1038
            path = resource_filename(
1039
                self.dataset_project,
1040
                "setupdata/%s/%s" % (self.dataset_name,
1041
                                     row_dict["File"])
1042
            )
1043
            try:
1044
                file_data = read_file(path)
1045
            except Exception as msg:
1046
                file_data = None
1047
                logger.warning(msg[0] + " Error on sheet: " + self.sheetname)
1048
1049
            # Obtain all created instrument documents content type
1050
            documents_brains = api.search({"portal_type": "Multifile"})
1051
            # If a the new document has the same DocumentID as a created
1052
            # document, this object won't be created.
1053
            idAlreadyInUse = False
1054
            for brain in documents_brains:
1055
                obj = api.get_object(brain)
1056
                instrument = obj.aq_parent
1057
                doc_id = obj.getDocumentID()
1058
                new_doc_id = row_dict.get("DocumentID", "")
1059
                if doc_id == new_doc_id:
1060
                    msg = "The ID '%s' used for this document is already in " \
1061
                          "use on instrument '%s', consequently the file " \
1062
                          "hasn't been upload." % (
1063
                              doc_id, api.get_title(instrument))
1064
                    self.context.plone_utils.addPortalMessage(msg)
1065
                    idAlreadyInUse = True
1066
            if not idAlreadyInUse:
1067
                obj = api.create(folder, "Multifile", **dict(
1068
                    document_id=u(row_dict.get("DocumentID", "")),
1069
                    document_version=u(row_dict.get("DocumentVersion", "")),
1070
                    document_location=u(row_dict.get("DocumentLocation", "")),
1071
                    document_type=u(row_dict.get("DocumentType", "")),
1072
                    file=file_data
1073
                ))
1074
1075
1076
class Instrument_Maintenance_Tasks(WorksheetImporter):
1077
1078
    def Import(self):
1079
        bsc = getToolByName(self.context, SETUP_CATALOG)
1080
        for row in self.get_rows(3):
1081
            if not row['instrument'] or not row['title'] or not row['type']:
1082
                continue
1083
1084
            folder = self.get_object(bsc, 'Instrument', row.get('instrument'))
1085
            if folder:
1086
                obj = _createObjectByType(
1087
                    "InstrumentMaintenanceTask", folder, tmpID())
1088
                try:
1089
                    cost = "%.2f" % (row.get('cost', 0))
1090
                except Exception:
1091
                    cost = row.get('cost', '0.0')
1092
1093
                obj.edit(
1094
                    title=row['title'],
1095
                    description=row['description'],
1096
                    Type=row['type'],
1097
                    DownFrom=row.get('downfrom', ''),
1098
                    DownTo=row.get('downto', ''),
1099
                    Maintainer=row.get('maintaner', ''),
1100
                    Considerations=row.get('considerations', ''),
1101
                    WorkPerformed=row.get('workperformed', ''),
1102
                    Remarks=row.get('remarks', ''),
1103
                    Cost=cost,
1104
                    Closed=self.to_bool(row.get('closed'))
1105
                )
1106
                obj.unmarkCreationFlag()
1107
                renameAfterCreation(obj)
1108
                notify(ObjectInitializedEvent(obj))
1109
1110
1111
class Instrument_Schedule(WorksheetImporter):
1112
1113
    def Import(self):
1114
        bsc = getToolByName(self.context, SETUP_CATALOG)
1115
        for row in self.get_rows(3):
1116
            if not row['instrument'] or not row['title'] or not row['type']:
1117
                continue
1118
            folder = self.get_object(bsc, 'Instrument', row.get('instrument'))
1119
            if folder:
1120
                obj = _createObjectByType(
1121
                    "InstrumentScheduledTask", folder, tmpID())
1122
                criteria = [
1123
                    {'fromenabled': row.get('date', None) is not None,
1124
                     'fromdate': row.get('date', ''),
1125
                     'repeatenabled': ((row['numrepeats'] and
1126
                                        row['numrepeats'] > 1) or
1127
                                       (row['repeatuntil'] and
1128
                                        len(row['repeatuntil']) > 0)),
1129
                     'repeatunit': row.get('numrepeats', ''),
1130
                     'repeatperiod': row.get('periodicity', ''),
1131
                     'repeatuntilenabled': (row['repeatuntil'] and
1132
                                            len(row['repeatuntil']) > 0),
1133
                     'repeatuntil': row.get('repeatuntil')}
1134
                ]
1135
                obj.edit(
1136
                    title=row['title'],
1137
                    Type=row['type'],
1138
                    ScheduleCriteria=criteria,
1139
                    Considerations=row.get('considerations', ''),
1140
                )
1141
                obj.unmarkCreationFlag()
1142
                renameAfterCreation(obj)
1143
                notify(ObjectInitializedEvent(obj))
1144
1145
1146
class Sample_Matrices(WorksheetImporter):
1147
1148
    def Import(self):
1149
        container = self.context.setup.samplematrices
1150
        for row in self.get_rows(3):
1151
            title = row.get("title")
1152
            if not title:
1153
                continue
1154
            api.create(container, "SampleMatrix",
1155
                       title=title, description=row.get("description"))
1156
1157
1158
class Batch_Labels(WorksheetImporter):
1159
1160
    def Import(self):
1161
        container = self.context.setup.batchlabels
1162
        for row in self.get_rows(3):
1163
            title = row.get("title")
1164
            if not title:
1165
                continue
1166
            api.create(container, "BatchLabel", title=title)
1167
1168
1169
class Sample_Types(WorksheetImporter):
1170
1171
    def Import(self):
1172
        container = self.context.setup.sampletypes
1173
        sc = api.get_tool(SETUP_CATALOG)
1174
1175
        for row in self.get_rows(3):
1176
            title = row.get("title")
1177
            if not title:
1178
                continue
1179
1180
            obj = api.create(container, "SampleType", title=title,
1181
                             description=row.get("description"))
1182
1183
            samplematrix = self.get_object(
1184
                sc, 'SampleMatrix', row.get('SampleMatrix_title'))
1185
            containertype = self.get_object(
1186
                sc, 'ContainerType', row.get('ContainerType_title'))
1187
1188
            if samplematrix:
1189
                obj.setSampleMatrix(samplematrix)
1190
            if containertype:
1191
                obj.setContainerType(containertype)
1192
1193
            obj.setHazardous(self.to_bool(row['Hazardous']))
1194
            obj.setPrefix(row['Prefix'])
1195
            obj.setMinimumVolume(row['MinimumVolume'])
1196
            obj.setRetentionPeriod({
1197
                'days': row['RetentionPeriod'] or 0,
1198
                'hours': 0,
1199
                'minutes': 0})
1200
1201
            samplepoint = self.get_object(sc, 'SamplePoint',
1202
                                          row.get('SamplePoint_title'))
1203
            if samplepoint:
1204
                samplepoint.setSampleTypes([obj, ])
1205
            obj.reindexObject()
1206
1207
1208
class Sample_Points(WorksheetImporter):
1209
1210
    def Import(self):
1211
        setup_folder = self.context.setup.samplepoints
1212
        bsc = getToolByName(self.context, SETUP_CATALOG)
1213
        cat = api.get_tool(CLIENT_CATALOG)
1214
        for row in self.get_rows(3):
1215
            if not row['title']:
1216
                continue
1217
            if row['Client_title']:
1218
                client_title = row['Client_title']
1219
                client = cat(portal_type="Client", getName=client_title)
1220
                if len(client) == 0:
1221
                    error = "Sample Point %s: Client invalid: '%s'. The Sample point will not be uploaded."
1222
                    logger.error(error, row['title'], client_title)
1223
                    continue
1224
                folder = client[0].getObject()
1225
            else:
1226
                folder = setup_folder
1227
1228
            if row['Latitude']:
1229
                logger.log("Ignored SamplePoint Latitude", 'error')
1230
            if row['Longitude']:
1231
                logger.log("Ignored SamplePoint Longitude", 'error')
1232
1233
            obj = api.create(folder, "SamplePoint", title=row['title'],
1234
                             description=row.get('description', ''))
1235
            obj.setComposite(self.to_bool(row["Composite"]))
1236
            obj.setElevation(row["Elevation"])
1237
            sampletype = self.get_object(bsc, 'SampleType',
1238
                                         row.get('SampleType_title'))
1239
            if sampletype:
1240
                obj.setSampleTypes([sampletype, ])
1241
                obj.reindexObject()
1242
1243
1244
class Sample_Point_Sample_Types(WorksheetImporter):
1245
1246
    def Import(self):
1247
        bsc = getToolByName(self.context, SETUP_CATALOG)
1248
        for row in self.get_rows(3):
1249
            sampletype = self.get_object(bsc,
1250
                                         'SampleType',
1251
                                         row.get('SampleType_title'))
1252
            samplepoint = self.get_object(bsc,
1253
                                          'SamplePoint',
1254
                                          row['SamplePoint_title'])
1255
            if samplepoint:
1256
                sampletypes = samplepoint.getSampleTypes()
1257
                if sampletype not in sampletypes:
1258
                    sampletypes.append(sampletype)
1259
                    samplepoint.setSampleTypes(sampletypes)
1260
1261
1262
class Storage_Locations(WorksheetImporter):
1263
1264
    def Import(self):
1265
        container = self.context.setup.storagelocations
1266
        for row in self.get_rows(3):
1267
            address = row.get('Address')
1268
            if not address:
1269
                continue
1270
1271
            api.create(container, "StorageLocation",
1272
                       title=address,
1273
                       SiteTitle=row.get('SiteTitle'),
1274
                       SiteCode=row.get('SiteCode'),
1275
                       SiteDescription=row.get('SiteDescription'),
1276
                       LocationTitle=row.get('LocationTitle'),
1277
                       LocationCode=row.get('LocationCode'),
1278
                       LocationDescription=row.get('LocationDescription'),
1279
                       LocationType=row.get('LocationType'),
1280
                       ShelfTitle=row.get('ShelfTitle'),
1281
                       ShelfCode=row.get('ShelfCode'),
1282
                       ShelfDescription=row.get('ShelfDescription'))
1283
1284
1285
class Sample_Conditions(WorksheetImporter):
1286
1287
    def Import(self):
1288
        container = self.context.setup.sampleconditions
1289
        for row in self.get_rows(3):
1290
            title = row.get("title")
1291
            if not title:
1292
                continue
1293
1294
            description = row.get("description")
1295
            api.create(container, "SampleCondition",
1296
                       title=title,
1297
                       description=description)
1298
1299
1300
class Analysis_Categories(WorksheetImporter):
1301
1302
    def Import(self):
1303
        container = self.context.setup.analysiscategories
1304
        setup_tool = getToolByName(self.context, SETUP_CATALOG)
1305
        for row in self.get_rows(3):
1306
            title = row.get("title")
1307
            if not title:
1308
                logger.warning("Error in in {}. Missing Title field."
1309
                               .format(self.sheetname))
1310
                continue
1311
1312
            department_title = row.get("Department_title", None)
1313
            if not department_title:
1314
                logger.warning("Error in {}. Department field missing."
1315
                               .format(self.sheetname))
1316
                continue
1317
1318
            department = self.get_object(setup_tool, "Department",
1319
                                         title=department_title)
1320
            if not department:
1321
                logger.warning("Error in {}. Department '{}' is wrong."
1322
                               .format(self.sheetname, department_title))
1323
                continue
1324
1325
            description = row.get("description", "")
1326
            comments = row.get("comments", "")
1327
            api.create(container, "AnalysisCategory",
1328
                       title=title,
1329
                       description=description,
1330
                       comments=comments,
1331
                       department=department)
1332
1333
1334
class Methods(WorksheetImporter):
1335
1336
    def Import(self):
1337
        folder = self.context.methods
1338
        bsc = getToolByName(self.context, SETUP_CATALOG)
1339
        for row in self.get_rows(3):
1340
            if row['title']:
1341
                calculation = self.get_object(
1342
                    bsc, 'Calculation', row.get('Calculation_title'))
1343
                obj = _createObjectByType("Method", folder, tmpID())
1344
                obj.edit(
1345
                    title=row['title'],
1346
                    description=row.get('description', ''),
1347
                    Instructions=row.get('Instructions', ''),
1348
                    ManualEntryOfResults=row.get('ManualEntryOfResults', True),
1349
                    Calculation=calculation,
1350
                    MethodID=row.get('MethodID', ''),
1351
                    Accredited=row.get('Accredited', True),
1352
                )
1353
                # Obtain all created methods
1354
                methods_brains = bsc.searchResults({'portal_type': 'Method'})
1355
                # If a the new method has the same MethodID as a created method, remove MethodID value.
1356
                for methods in methods_brains:
1357
                    if methods.getObject().get('MethodID', '') != '' and methods.getObject.get('MethodID', '') == obj['MethodID']:
1358
                        obj.edit(MethodID='')
1359
1360 View Code Duplication
                if row['MethodDocument']:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1361
                    path = resource_filename(
1362
                        self.dataset_project,
1363
                        "setupdata/%s/%s" % (self.dataset_name,
1364
                                             row['MethodDocument'])
1365
                    )
1366
                    try:
1367
                        file_data = read_file(path)
1368
                        obj.setMethodDocument(file_data)
1369
                    except Exception as msg:
1370
                        logger.warning(
1371
                            msg[0] + " Error on sheet: " + self.sheetname)
1372
1373
                obj.unmarkCreationFlag()
1374
                renameAfterCreation(obj)
1375
                notify(ObjectInitializedEvent(obj))
1376
1377
1378
class Sampling_Deviations(WorksheetImporter):
1379
1380
    def Import(self):
1381
        container = self.context.setup.samplingdeviations
1382
        for row in self.get_rows(3):
1383
            title = row.get("title")
1384
            if not title:
1385
                continue
1386
            api.create(container, "SamplingDeviation",
1387
                       title=title, description=row.get("description"))
1388
1389
1390
class Calculations(WorksheetImporter):
1391
1392
    def get_interim_fields(self):
1393
        # preload Calculation Interim Fields sheet
1394
        sheetname = 'Calculation Interim Fields'
1395
        worksheet = self.workbook[sheetname]
1396
        if not worksheet:
1397
            return
1398
        self.interim_fields = {}
1399
        rows = self.get_rows(3, worksheet=worksheet)
1400
        for row in rows:
1401
            calc_title = row['Calculation_title']
1402
            if calc_title not in self.interim_fields.keys():
1403
                self.interim_fields[calc_title] = []
1404
            self.interim_fields[calc_title].append({
1405
                'keyword': row['keyword'],
1406
                'title': row.get('title', ''),
1407
                'type': 'int',
1408
                'hidden': ('hidden' in row and row['hidden']) and True or False,
1409
                'value': row['value'],
1410
                'unit': row['unit'] and row['unit'] or ''})
1411
1412
    def Import(self):
1413
        self.get_interim_fields()
1414
        folder = self.context.bika_setup.bika_calculations
1415
        for row in self.get_rows(3):
1416
            if not row['title']:
1417
                continue
1418
            calc_title = row['title']
1419
            calc_interims = self.interim_fields.get(calc_title, [])
1420
            formula = row['Formula']
1421
            # scan formula for dep services
1422
            keywords = re.compile(r"\[([^\.^\]]+)\]").findall(formula)
1423
            # remove interims from deps
1424
            interim_keys = [k['keyword'] for k in calc_interims]
1425
            dep_keywords = [k for k in keywords if k not in interim_keys]
1426
1427
            obj = _createObjectByType("Calculation", folder, tmpID())
1428
            obj.edit(
1429
                title=calc_title,
1430
                description=row.get('description', ''),
1431
                InterimFields=calc_interims,
1432
                Formula=str(row['Formula'])
1433
            )
1434
            for kw in dep_keywords:
1435
                self.defer(src_obj=obj,
1436
                           src_field='DependentServices',
1437
                           dest_catalog=SETUP_CATALOG,
1438
                           dest_query={'portal_type': 'AnalysisService',
1439
                                       'getKeyword': kw}
1440
                           )
1441
            obj.unmarkCreationFlag()
1442
            renameAfterCreation(obj)
1443
            notify(ObjectInitializedEvent(obj))
1444
1445
        # Now we have the calculations registered, try to assign default calcs
1446
        # to methods
1447
        sheet = self.workbook["Methods"]
1448
        bsc = getToolByName(self.context, SETUP_CATALOG)
1449
        for row in self.get_rows(3, sheet):
1450
            if row.get('title', '') and row.get('Calculation_title', ''):
1451
                meth = self.get_object(bsc, "Method", row.get('title'))
1452
                if meth and not meth.getCalculation():
1453
                    calctit = safe_unicode(
1454
                        row['Calculation_title']).encode('utf-8')
1455
                    calc = self.get_object(bsc, "Calculation", calctit)
1456
                    if calc:
1457
                        meth.setCalculation(calc.UID())
1458
1459
1460
class Analysis_Services(WorksheetImporter):
1461
1462
    def load_interim_fields(self):
1463
        # preload AnalysisService InterimFields sheet
1464
        sheetname = 'AnalysisService InterimFields'
1465
        worksheet = self.workbook[sheetname]
1466
        if not worksheet:
1467
            return
1468
        self.service_interims = {}
1469
        rows = self.get_rows(3, worksheet=worksheet)
1470
        for row in rows:
1471
            service_title = row['Service_title']
1472
            if service_title not in self.service_interims.keys():
1473
                self.service_interims[service_title] = []
1474
            self.service_interims[service_title].append({
1475
                'keyword': row['keyword'],
1476
                'title': row.get('title', ''),
1477
                'type': 'int',
1478
                'value': row['value'],
1479
                'unit': row['unit'] and row['unit'] or ''})
1480
1481
    def load_result_options(self):
1482
        bsc = getToolByName(self.context, SETUP_CATALOG)
1483
        sheetname = 'AnalysisService ResultOptions'
1484
        worksheet = self.workbook[sheetname]
1485
        if not worksheet:
1486
            return
1487
        for row in self.get_rows(3, worksheet=worksheet):
1488
            service = self.get_object(bsc, 'AnalysisService',
1489
                                      row.get('Service_title'))
1490
            if not service:
1491
                return
1492
            sro = service.getResultOptions()
1493
            sro.append({'ResultValue': row['ResultValue'],
1494
                        'ResultText': row['ResultText']})
1495
            service.setResultOptions(sro)
1496
1497
    def load_service_uncertainties(self):
1498
        bsc = getToolByName(self.context, SETUP_CATALOG)
1499
        sheetname = 'Analysis Service Uncertainties'
1500
        worksheet = self.workbook[sheetname]
1501
        if not worksheet:
1502
            return
1503
1504
        bucket = {}
1505
        count = 0
1506
        for row in self.get_rows(3, worksheet=worksheet):
1507
            count += 1
1508
            service = self.get_object(bsc, 'AnalysisService',
1509
                                      row.get('Service_title'))
1510
            if not service:
1511
                warning = "Unable to load an Analysis Service uncertainty. Service '%s' not found." % row.get(
1512
                    'Service_title')
1513
                logger.warning(warning)
1514
                continue
1515
            service_uid = service.UID()
1516
            if service_uid not in bucket:
1517
                bucket[service_uid] = []
1518
            bucket[service_uid].append(
1519
                {'intercept_min': row['Range Min'],
1520
                 'intercept_max': row['Range Max'],
1521
                 'errorvalue': row['Uncertainty Value']}
1522
            )
1523
            if count > 500:
1524
                self.write_bucket(bucket)
1525
                bucket = {}
1526
        if bucket:
1527
            self.write_bucket(bucket)
1528
1529
    def get_methods(self, service_title, default_method):
1530
        """ Return an array of objects of the type Method in accordance to the
1531
            methods listed in the 'AnalysisService Methods' sheet and service
1532
            set in the parameter service_title.
1533
            If default_method is set, it will be included in the returned
1534
            array.
1535
        """
1536
        return self.get_relations(service_title,
1537
                                  default_method,
1538
                                  'Method',
1539
                                  SETUP_CATALOG,
1540
                                  'AnalysisService Methods',
1541
                                  'Method_title')
1542
1543
    def get_instruments(self, service_title, default_instrument):
1544
        """ Return an array of objects of the type Instrument in accordance to
1545
            the instruments listed in the 'AnalysisService Instruments' sheet
1546
            and service set in the parameter 'service_title'.
1547
            If default_instrument is set, it will be included in the returned
1548
            array.
1549
        """
1550
        return self.get_relations(service_title,
1551
                                  default_instrument,
1552
                                  'Instrument',
1553
                                  SETUP_CATALOG,
1554
                                  'AnalysisService Instruments',
1555
                                  'Instrument_title')
1556
1557
    def get_relations(self, service_title, default_obj, obj_type, catalog_name, sheet_name, column):
1558
        """ Return an array of objects of the specified type in accordance to
1559
            the object titles defined in the sheet specified in 'sheet_name' and
1560
            service set in the paramenter 'service_title'.
1561
            If a default_obj is set, it will be included in the returned array.
1562
        """
1563
        out_objects = [default_obj] if default_obj else []
1564
        cat = getToolByName(self.context, catalog_name)
1565
        worksheet = self.workbook[sheet_name]
1566
        if not worksheet:
1567
            return out_objects
1568
        for row in self.get_rows(3, worksheet=worksheet):
1569
            row_as_title = row.get('Service_title')
1570
            if not row_as_title:
1571
                return out_objects
1572
            elif row_as_title != service_title:
1573
                continue
1574
            obj = self.get_object(cat, obj_type, row.get(column))
1575
            if obj:
1576
                if default_obj and default_obj.UID() == obj.UID():
1577
                    continue
1578
                out_objects.append(obj)
1579
        return out_objects
1580
1581
    def write_bucket(self, bucket):
1582
        bsc = getToolByName(self.context, SETUP_CATALOG)
1583
        for service_uid, uncertainties in bucket.items():
1584
            obj = bsc(UID=service_uid)[0].getObject()
1585
            _uncert = list(obj.getUncertainties())
1586
            _uncert.extend(uncertainties)
1587
            obj.setUncertainties(_uncert)
1588
1589
    def Import(self):
1590
        self.load_interim_fields()
1591
        folder = self.context.bika_setup.bika_analysisservices
1592
        bsc = getToolByName(self.context, SETUP_CATALOG)
1593
        for row in self.get_rows(3):
1594
            if not row['title']:
1595
                continue
1596
1597
            obj = _createObjectByType("AnalysisService", folder, tmpID())
1598
            MTA = {
1599
                'days': self.to_int(row.get('MaxTimeAllowed_days', 0), 0),
1600
                'hours': self.to_int(row.get('MaxTimeAllowed_hours', 0), 0),
1601
                'minutes': self.to_int(row.get('MaxTimeAllowed_minutes', 0), 0),
1602
            }
1603
            category = self.get_object(
1604
                bsc, 'AnalysisCategory', row.get('AnalysisCategory_title'))
1605
            department = self.get_object(
1606
                bsc, 'Department', row.get('Department_title'))
1607
            container = self.get_object(
1608
                bsc, 'SampleContainer', row.get('Container_title'))
1609
            preservation = self.get_object(
1610
                bsc, 'SamplePreservation', row.get('Preservation_title'))
1611
1612
            # Analysis Service - Method considerations:
1613
            # One Analysis Service can have 0 or n Methods associated (field
1614
            # 'Methods' from the Schema).
1615
            # If the Analysis Service has at least one method associated, then
1616
            # one of those methods can be set as the defualt method (field
1617
            # '_Method' from the Schema).
1618
            #
1619
            # To make it easier, if a DefaultMethod is declared in the
1620
            # Analysis_Services spreadsheet, but the same AS has no method
1621
            # associated in the Analysis_Service_Methods spreadsheet, then make
1622
            # the assumption that the DefaultMethod set in the former has to be
1623
            # associated to the AS although the relation is missing.
1624
            defaultmethod = self.get_object(
1625
                bsc, 'Method', row.get('DefaultMethod_title'))
1626
            methods = self.get_methods(row['title'], defaultmethod)
1627
            if not defaultmethod and methods:
1628
                defaultmethod = methods[0]
1629
1630
            # Analysis Service - Instrument considerations:
1631
            # By default, an Analysis Services will be associated automatically
1632
            # with several Instruments due to the Analysis Service - Methods
1633
            # relation (an Instrument can be assigned to a Method and one Method
1634
            # can have zero or n Instruments associated). There is no need to
1635
            # set this assignment directly, the AnalysisService object will
1636
            # find those instruments.
1637
            # Besides this 'automatic' behavior, an Analysis Service can also
1638
            # have 0 or n Instruments manually associated ('Instruments' field).
1639
            # In this case, the attribute 'AllowInstrumentEntryOfResults' should
1640
            # be set to True.
1641
            #
1642
            # To make it easier, if a DefaultInstrument is declared in the
1643
            # Analysis_Services spreadsheet, but the same AS has no instrument
1644
            # associated in the AnalysisService_Instruments spreadsheet, then
1645
            # make the assumption the DefaultInstrument set in the former has
1646
            # to be associated to the AS although the relation is missing and
1647
            # the option AllowInstrumentEntryOfResults will be set to True.
1648
            defaultinstrument = self.get_object(
1649
                bsc, 'Instrument', row.get('DefaultInstrument_title'))
1650
            instruments = self.get_instruments(row['title'], defaultinstrument)
1651
            allowinstrentry = True if instruments else False
1652
            if not defaultinstrument and instruments:
1653
                defaultinstrument = instruments[0]
1654
1655
            # The manual entry of results can only be set to false if the value
1656
            # for the attribute "InstrumentEntryOfResults" is False.
1657
            allowmanualentry = True if not allowinstrentry else row.get(
1658
                'ManualEntryOfResults', True)
1659
1660
            # Analysis Service - Calculation considerations:
1661
            # By default, the AnalysisService will use the Calculation associated
1662
            # to the Default Method (the field "UseDefaultCalculation"==True).
1663
            # If the Default Method for this AS doesn't have any Calculation
1664
            # associated and the field "UseDefaultCalculation" is True, no
1665
            # Calculation will be used for this AS ("_Calculation" field is
1666
            # reserved and should not be set directly).
1667
            #
1668
            # To make it easier, if a Calculation is set by default in the
1669
            # spreadsheet, then assume the UseDefaultCalculation has to be set
1670
            # to False.
1671
            deferredcalculation = self.get_object(
1672
                bsc, 'Calculation', row.get('Calculation_title'))
1673
            usedefaultcalculation = False if deferredcalculation else True
1674
            _calculation = deferredcalculation if deferredcalculation else \
1675
                (defaultmethod.getCalculation() if defaultmethod else None)
1676
1677
            obj.edit(
1678
                title=row['title'],
1679
                ShortTitle=row.get('ShortTitle', row['title']),
1680
                description=row.get('description', ''),
1681
                Keyword=row['Keyword'],
1682
                PointOfCapture=row['PointOfCapture'].lower(),
1683
                Category=category,
1684
                Department=department,
1685
                Unit=row['Unit'] and row['Unit'] or None,
1686
                Precision=row['Precision'] and str(row['Precision']) or '0',
1687
                ExponentialFormatPrecision=str(self.to_int(
1688
                    row.get('ExponentialFormatPrecision', 7), 7)),
1689
                LowerDetectionLimit='%06f' % self.to_float(
1690
                    row.get('LowerDetectionLimit', '0.0'), 0),
1691
                UpperDetectionLimit='%06f' % self.to_float(
1692
                    row.get('UpperDetectionLimit', '1000000000.0'), 1000000000.0),
1693
                DetectionLimitSelector=self.to_bool(
1694
                    row.get('DetectionLimitSelector', 0)),
1695
                MaxTimeAllowed=MTA,
1696
                Price="%02f" % Float(row['Price']),
1697
                BulkPrice="%02f" % Float(row['BulkPrice']),
1698
                VAT="%02f" % Float(row['VAT']),
1699
                _Method=defaultmethod,
1700
                Methods=methods,
1701
                ManualEntryOfResults=allowmanualentry,
1702
                InstrumentEntryOfResults=allowinstrentry,
1703
                Instruments=instruments,
1704
                Calculation=_calculation,
1705
                UseDefaultCalculation=usedefaultcalculation,
1706
                DuplicateVariation="%02f" % Float(row['DuplicateVariation']),
1707
                Accredited=self.to_bool(row['Accredited']),
1708
                InterimFields=hasattr(self, 'service_interims') and self.service_interims.get(
1709
                    row['title'], []) or [],
1710
                Separate=self.to_bool(row.get('Separate', False)),
1711
                Container=container,
1712
                Preservation=preservation,
1713
                CommercialID=row.get('CommercialID', ''),
1714
                ProtocolID=row.get('ProtocolID', '')
1715
            )
1716
            obj.unmarkCreationFlag()
1717
            renameAfterCreation(obj)
1718
            notify(ObjectInitializedEvent(obj))
1719
        self.load_result_options()
1720
        self.load_service_uncertainties()
1721
1722
1723
class Analysis_Specifications(WorksheetImporter):
1724
1725
    def resolve_service(self, row):
1726
        bsc = getToolByName(self.context, SETUP_CATALOG)
1727
        service = bsc(
1728
            portal_type="AnalysisService",
1729
            title=safe_unicode(row["service"])
1730
        )
1731
        if not service:
1732
            service = bsc(
1733
                portal_type="AnalysisService",
1734
                getKeyword=safe_unicode(row["service"])
1735
            )
1736
        service = service[0].getObject()
1737
        return service
1738
1739
    def Import(self):
1740
        bucket = {}
1741
        client_catalog = getToolByName(self.context, CLIENT_CATALOG)
1742
        setup_catalog = getToolByName(self.context, SETUP_CATALOG)
1743
        # collect up all values into the bucket
1744
        for row in self.get_rows(3):
1745
            title = row.get("Title", False)
1746
            if not title:
1747
                title = row.get("title", False)
1748
                if not title:
1749
                    continue
1750
            parent = row["Client_title"] if row["Client_title"] else "lab"
1751
            st = row["SampleType_title"] if row["SampleType_title"] else ""
1752
            service = self.resolve_service(row)
1753
1754
            if parent not in bucket:
1755
                bucket[parent] = {}
1756
            if title not in bucket[parent]:
1757
                bucket[parent][title] = {"sampletype": st, "resultsrange": []}
1758
            bucket[parent][title]["resultsrange"].append({
1759
                "keyword": service.getKeyword(),
1760
                "min": row["min"] if row["min"] else "0",
1761
                "max": row["max"] if row["max"] else "0",
1762
            })
1763
        # write objects.
1764
        for parent in bucket.keys():
1765
            for title in bucket[parent]:
1766
                if parent == "lab":
1767
                    folder = self.context.bika_setup.bika_analysisspecs
1768
                else:
1769
                    proxy = client_catalog(
1770
                        portal_type="Client", getName=safe_unicode(parent))[0]
1771
                    folder = proxy.getObject()
1772
                st = bucket[parent][title]["sampletype"]
1773
                resultsrange = bucket[parent][title]["resultsrange"]
1774
                if st:
1775
                    st_uid = setup_catalog(
1776
                        portal_type="SampleType", title=safe_unicode(st))[0].UID
1777
                obj = _createObjectByType("AnalysisSpec", folder, tmpID())
1778
                obj.edit(title=title)
1779
                obj.setResultsRange(resultsrange)
1780
                if st:
1781
                    obj.setSampleType(st_uid)
0 ignored issues
show
introduced by
The variable st_uid does not seem to be defined for all execution paths.
Loading history...
1782
                obj.unmarkCreationFlag()
1783
                renameAfterCreation(obj)
1784
                notify(ObjectInitializedEvent(obj))
1785
1786
1787
class Analysis_Profiles(WorksheetImporter):
1788
1789
    def load_analysis_profile_services(self):
1790
        sheetname = 'Analysis Profile Services'
1791
        worksheet = self.workbook[sheetname]
1792
        self.profile_services = {}
1793
        if not worksheet:
1794
            return
1795
        bsc = getToolByName(self.context, SETUP_CATALOG)
1796
        for row in self.get_rows(3, worksheet=worksheet):
1797
            if not row.get('Profile', '') or not row.get('Service', ''):
1798
                continue
1799
            if row['Profile'] not in self.profile_services.keys():
1800
                self.profile_services[row['Profile']] = []
1801
            # Here we match againts Keyword or Title.
1802
            # XXX We need a utility for this kind of thing.
1803
            service = self.get_object(
1804
                bsc, 'AnalysisService', row.get('Service'))
1805
            if not service:
1806
                service = bsc(portal_type='AnalysisService',
1807
                              getKeyword=row['Service'])[0].getObject()
1808
            self.profile_services[row['Profile']].append(service)
1809
1810
    def Import(self):
1811
        self.load_analysis_profile_services()
1812
        folder = self.context.setup.analysisprofiles
1813
        for row in self.get_rows(3):
1814
            title = row.get("title", "")
1815
            description = row.get("description", "")
1816
            profile_key = row.get("ProfileKey", "")
1817
            commercial_id = row.get("CommercialID", "")
1818
            analysis_profile_price = row.get("AnalysisProfilePrice")
1819
            analysis_profile_vat = row.get("AnalysisProfileVAT")
1820
            use_analysis_profile_price = row.get("UseAnalysisProfilePrice")
1821
            if title:
1822
                obj = api.create(folder, "AnalysisProfile")
1823
                api.edit(obj,
1824
                         title=api.safe_unicode(title),
1825
                         description=api.safe_unicode(description),
1826
                         profile_key=api.safe_unicode(profile_key),
1827
                         commercial_id=api.safe_unicode(commercial_id),
1828
                         analysis_profile_price=api.to_float(
1829
                             analysis_profile_price, 0.0),
1830
                         analysis_profile_vat=api.to_float(
1831
                             analysis_profile_vat, 0.0),
1832
                         use_analysis_profile_price=bool(
1833
                             use_analysis_profile_price))
1834
                # set the services
1835
                obj.setServices(self.profile_services[row["title"]])
1836
1837
1838
class Sample_Templates(WorksheetImporter):
1839
1840
    def load_sampletemplate_services(self):
1841
        sheetname = "Sample Template Services"
1842
        worksheet = self.workbook[sheetname]
1843
        if not worksheet:
1844
            return
1845
        sc = api.get_tool(SETUP_CATALOG)
1846
        self.services = {}
1847
        for row in self.get_rows(3, worksheet=worksheet):
1848
            keyword = row.get("keyword")
1849
            service = self.get_object(sc, "AnalysisService", keyword)
1850
            part_id = row.get("part_id", "")
1851
            title = row.get("SampleTemplate")
1852
            if title not in self.services:
1853
                self.services[title] = []
1854
            self.services[title].append({
1855
                "uid": api.get_uid(service),
1856
                "part_id": part_id,
1857
            })
1858
1859
    def load_sampletemplate_partitions(self):
1860
        sheetname = "Sample Template Partitions"
1861
        worksheet = self.workbook[sheetname]
1862
        if not worksheet:
1863
            return
1864
1865
        sc = api.get_tool(SETUP_CATALOG)
1866
        self.partitions = {}
1867
        for row in self.get_rows(3, worksheet=worksheet):
1868
            title = row.get("SampleTemplate")
1869
            container = row.get("container")
1870
            preservation = row.get("preservation")
1871
            sampletype = row.get("sampletype")
1872
            part_id = row.get("part_id")
1873
            if title not in self.partitions:
1874
                self.partitions[title] = []
1875
            container = self.get_object(sc, "SampleContainer", container)
1876
            preservation = self.get_object(
1877
                sc, "SamplePreservation", preservation)
1878
            sampletype = self.get_object(sc, "SampleType", sampletype)
1879
            self.partitions[title].append({
1880
                "part_id": part_id,
1881
                "container": api.get_uid(container) if container else "",
1882
                "preservation": api.get_uid(preservation) if preservation else "",
1883
                "sampletype": api.get_uid(sampletype) if sampletype else "",
1884
            })
1885
1886
    def Import(self):
1887
        self.load_sampletemplate_services()
1888
        self.load_sampletemplate_partitions()
1889
1890
        setup = api.get_senaite_setup()
1891
        folder = setup.sampletemplates
1892
        sc = api.get_tool(SETUP_CATALOG)
1893
1894
        for row in self.get_rows(3):
1895
            title = row.get("title")
1896
            if not title:
1897
                continue
1898
            services = self.services.get(title)
1899
            client_title = row.get("Client_title") or "lab"
1900
            partitions = self.partitions.get(title, [])
1901
            if client_title == "lab":
1902
                folder = setup.sampletemplates
1903
            else:
1904
                client = api.search({
1905
                    "portal_type": "Client",
1906
                    "getName": client_title
1907
                }, CLIENT_CATALOG)
1908
                if len(client) == 1:
1909
                    folder = api.get_object(client[0])
1910
1911
            sampletype = self.get_object(
1912
                sc, 'SampleType', row.get('SampleType_title'))
1913
            samplepoint = self.get_object(
1914
                sc, 'SamplePoint', row.get('SamplePoint_title'))
1915
1916
            obj = api.create(folder, "SampleTemplate", title=title)
1917
            obj.setSampleType(sampletype)
1918
            obj.setSamplePoint(samplepoint)
1919
            obj.setPartitions(partitions)
1920
            obj.setServices(services)
1921
1922
1923
class Reference_Definitions(WorksheetImporter):
1924
1925
    def load_reference_definition_results(self):
1926
        sheetname = 'Reference Definition Results'
1927
        worksheet = self.workbook[sheetname]
1928
        if not worksheet:
1929
            sheetname = 'Reference Definition Values'
1930
            worksheet = self.workbook[sheetname]
1931
            if not worksheet:
1932
                return
1933
        self.results = {}
1934
        if not worksheet:
1935
            return
1936
        bsc = getToolByName(self.context, SETUP_CATALOG)
1937
        for row in self.get_rows(3, worksheet=worksheet):
1938
            if row['ReferenceDefinition_title'] not in self.results.keys():
1939
                self.results[row['ReferenceDefinition_title']] = []
1940
            service = self.get_object(bsc, 'AnalysisService',
1941
                                      row.get('service'))
1942
            self.results[
1943
                row['ReferenceDefinition_title']].append({
1944
                    'uid': service.UID(),
1945
                    'result': row['result'] if row['result'] else '0',
1946
                    'min': row['min'] if row['min'] else '0',
1947
                    'max': row['max'] if row['max'] else '0'})
1948
1949
    def Import(self):
1950
        self.load_reference_definition_results()
1951
        folder = self.context.bika_setup.bika_referencedefinitions
1952
        for row in self.get_rows(3):
1953
            if not row['title']:
1954
                continue
1955
            obj = _createObjectByType("ReferenceDefinition", folder, tmpID())
1956
            obj.edit(
1957
                title=row['title'],
1958
                description=row.get('description', ''),
1959
                Blank=self.to_bool(row['Blank']),
1960
                ReferenceResults=self.results.get(row['title'], []),
1961
                Hazardous=self.to_bool(row['Hazardous']))
1962
            obj.unmarkCreationFlag()
1963
            renameAfterCreation(obj)
1964
            notify(ObjectInitializedEvent(obj))
1965
1966
1967
class Worksheet_Templates(WorksheetImporter):
1968
1969
    def __init__(self, context):
1970
        super(Worksheet_Templates, self).__init__(context)
1971
        self.wst_layouts = {}
1972
        self.wst_services = {}
1973
1974
    def load_definitions(self):
1975
        reference_query = {
1976
            "portal_type": "ReferenceDefinition",
1977
            "is_active": True,
1978
        }
1979
        definitions = {}
1980
        brains = api.search(reference_query, SETUP_CATALOG)
1981
        for brain in brains:
1982
            definitions[api.get_title(brain)] = api.get_uid(brain)
1983
        return definitions
1984
1985
    def load_wst_layouts(self):
1986
        definitions = self.load_definitions()
1987
        sheetname = "Worksheet Template Layouts"
1988
        worksheet = self.workbook[sheetname]
1989
        if not worksheet:
1990
            return
1991
        for row in self.get_rows(3, worksheet=worksheet):
1992
            wst_title = row.get("WorksheetTemplate_title")
1993
            if wst_title not in self.wst_layouts.keys():
1994
                self.wst_layouts[wst_title] = []
1995
1996
            ref_proxy = None
1997
            dup = row.get("dup", None)
1998
            dup = int(dup) if dup else None
1999
            analysis_type = row.get("type", "a")
2000
            blank_uid = None
2001
            control_uid = None
2002
2003
            # check control/blank fields if it 'Title' or 'UID'
2004
            if analysis_type in ["b", "Blank"]:
2005
                blank_ref = row.get("blank_ref", "")
2006
                if api.is_uid(blank_ref):
2007
                    blank_uid = blank_ref
2008
                else:
2009
                    blank_uid = definitions.get(blank_ref, None)
2010
                ref_proxy = blank_uid or None
2011
            elif analysis_type in ["c", "Control"]:
2012
                control_ref = row.get("control_ref", "")
2013
                if api.is_uid(control_ref):
2014
                    control_uid = control_ref
2015
                else:
2016
                    control_uid = definitions.get(control_ref, None)
2017
                ref_proxy = control_uid or None
2018
                
2019
            if analysis_type not in ["d", "Duplicate"]:
2020
                dup = None
2021
2022
            self.wst_layouts[wst_title].append(
2023
                {
2024
                    "pos": int(row["pos"]),
2025
                    "type": analysis_type[0].lower(), # if 'type' is full word
2026
                    "blank_ref": [blank_uid] if blank_uid else [],
2027
                    "control_ref": [control_uid] if control_uid else [],
2028
                    "reference_proxy": ref_proxy,
2029
                    "dup": dup,
2030
                    "dup_proxy": dup,
2031
                }
2032
            )
2033
2034
    def load_wst_services(self):
2035
        sheetname = "Worksheet Template Services"
2036
        worksheet = self.workbook[sheetname]
2037
        if not worksheet:
2038
            return
2039
        bsc = getToolByName(self.context, SETUP_CATALOG)
2040
        for row in self.get_rows(3, worksheet=worksheet):
2041
            wst_title = row.get("WorksheetTemplate_title")
2042
            if wst_title not in self.wst_services.keys():
2043
                self.wst_services[wst_title] = []
2044
            service = self.get_object(bsc, "AnalysisService",
2045
                                      row.get("service"))
2046
            if service:
2047
                self.wst_services[wst_title].append(service.UID())
2048
2049
    def Import(self):
2050
        self.load_wst_services()
2051
        self.load_wst_layouts()
2052
        folder = self.context.setup.worksheettemplates
2053
        for row in self.get_rows(3):
2054
            title = row.get("title")
2055
            if not title:
2056
                continue
2057
2058
            obj = api.create(folder, "WorksheetTemplate",
2059
                             title=title,
2060
                             description=row.get("description", ""))
2061
            if title in self.wst_layouts.keys():
2062
                obj.setTemplateLayout(self.wst_layouts[title])
2063
            if title in self.wst_services.keys():
2064
                obj.setServices(self.wst_services[title])
2065
2066
2067
class Setup(WorksheetImporter):
2068
2069
    def get_field_value(self, field, value):
2070
        if value is None:
2071
            return None
2072
        converters = {
2073
            "integer": self.to_integer_value,
2074
            "fixedpoint": self.to_fixedpoint_value,
2075
            "boolean": self.to_boolean_value,
2076
            "string": self.to_string_value,
2077
            "reference": self.to_reference_value,
2078
            "duration": self.to_duration_value
2079
        }
2080
        try:
2081
            return converters.get(field.type, None)(field, value)
2082
        except Exception:
2083
            logger.error("No valid type for Setup.{} ({}): {}"
2084
                         .format(field.getName(), field.type, value))
2085
2086
    def to_integer_value(self, field, value):
2087
        return str(int(value))
2088
2089
    def to_fixedpoint_value(self, field, value):
2090
        return str(float(value))
2091
2092
    def to_boolean_value(self, field, value):
2093
        return self.to_bool(value)
2094
2095
    def to_string_value(self, field, value):
2096
        if field.vocabulary:
2097
            return self.to_string_vocab_value(field, value)
2098
        return value and str(value) or ""
2099
2100
    def to_reference_value(self, field, value):
2101
        if not value:
2102
            return None
2103
2104
        brains = api.search({"title": to_unicode(value)})
2105
        if brains:
2106
            return api.get_uid(brains[0])
2107
2108
        msg = "No object found for Setup.{0} ({1}): {2}"
2109
        msg = msg.format(field.getName(), field.type, value)
2110
        logger.error(msg)
2111
        raise ValueError(msg)
2112
2113
    def to_string_vocab_value(self, field, value):
2114
        vocabulary = field.vocabulary
2115
        if type(vocabulary) is str:
2116
            # Use bika_setup for vocabulary access (has subfolders)
2117
            bika_setup = api.get_portal().get("bika_setup")
2118
            vocabulary = getFromString(bika_setup, vocabulary)
2119
        else:
2120
            vocabulary = vocabulary.items()
2121
2122
        if not vocabulary:
2123
            raise ValueError("Empty vocabulary for {}".format(field.getName()))
2124
2125
        if type(vocabulary) in (tuple, list):
2126
            vocabulary = {item[0]: item[1] for item in vocabulary}
2127
2128
        for key, val in vocabulary.items():
2129
            key_low = str(to_utf8(key)).lower()
2130
            val_low = str(to_utf8(val)).lower()
2131
            value_low = str(value).lower()
2132
            if key_low == value_low or val_low == value_low:
2133
                return key
2134
        raise ValueError("Vocabulary entry not found")
2135
2136
    def to_duration_value(self, field, values):
2137
        duration = ["days", "hours", "minutes"]
2138
        duration = map(lambda d: "{}_{}".format(field.getName(), d), duration)
2139
        return dict(
2140
            days=api.to_int(values.get(duration[0], 0), 0),
2141
            hours=api.to_int(values.get(duration[1], 0), 0),
2142
            minutes=api.to_int(values.get(duration[2], 0), 0))
2143
2144
    def Import(self):
2145
        values = {}
2146
        for row in self.get_rows(3):
2147
            values[row['Field']] = row['Value']
2148
2149
        bsetup = self.context.bika_setup
2150
        bschema = bsetup.Schema()
2151
        for field in bschema.fields():
2152
            value = None
2153
            field_name = field.getName()
2154
            if field_name in values:
2155
                value = self.get_field_value(field, values[field_name])
2156
            elif field.type == "duration":
2157
                value = self.get_field_value(field, values)
2158
2159
            if value is None:
2160
                continue
2161
            try:
2162
                obj_field = bsetup.getField(field_name)
2163
                obj_field.set(bsetup, str(value))
2164
            except Exception:
2165
                logger.error("No valid type for Setup.{} ({}): {}"
2166
                             .format(field_name, field.type, value))
2167
2168
2169
class ID_Prefixes(WorksheetImporter):
2170
2171
    def Import(self):
2172
        prefixes = self.context.bika_setup.getIDFormatting()
2173
        for row in self.get_rows(3):
2174
            # remove existing prefix from list
2175
            prefixes = [p for p in prefixes
2176
                        if p['portal_type'] != row['portal_type']]
2177
            # The spreadsheet will contain 'none' for user's visual stuff, but it means 'no separator'
2178
            separator = row.get('separator', '-')
2179
            separator = '' if separator == 'none' else separator
2180
            # add new prefix to list
2181
            prefixes.append({'portal_type': row['portal_type'],
2182
                             'padding': row['padding'],
2183
                             'prefix': row['prefix'],
2184
                             'separator': separator})
2185
        # self.context.bika_setup.setIDFormatting(prefixes)
2186
2187
2188
class Attachment_Types(WorksheetImporter):
2189
2190
    def Import(self):
2191
        container = self.context.setup.attachmenttypes
2192
        for row in self.get_rows(3):
2193
            title = row.get("title")
2194
            if not title:
2195
                continue
2196
2197
            api.create(container, "AttachmentType",
2198
                       title=title, description=row.get("description"))
2199
2200
2201
class Reference_Samples(WorksheetImporter):
2202
2203
    def load_reference_sample_results(self, sample):
2204
        sheetname = 'Reference Sample Results'
2205
        if not hasattr(self, 'results_worksheet'):
2206
            worksheet = self.workbook[sheetname]
2207
            if not worksheet:
2208
                return
2209
            self.results_worksheet = worksheet
2210
        results = []
2211
        bsc = getToolByName(self.context, SETUP_CATALOG)
2212
        for row in self.get_rows(3, worksheet=self.results_worksheet):
2213
            if row['ReferenceSample_id'] != sample.getId():
2214
                continue
2215
            service = self.get_object(bsc, 'AnalysisService',
2216
                                      row.get('AnalysisService_title'))
2217
            if not service:
2218
                warning = "Unable to load a reference sample result. Service %s not found."
2219
                logger.warning(warning, sheetname)
2220
                continue
2221
            results.append({
2222
                'uid': service.UID(),
2223
                'result': row['result'],
2224
                'min': row['min'],
2225
                'max': row['max']})
2226
        sample.setReferenceResults(results)
2227
2228
    def load_reference_analyses(self, sample):
2229
        sheetname = 'Reference Analyses'
2230
        if not hasattr(self, 'analyses_worksheet'):
2231
            worksheet = self.workbook[sheetname]
2232
            if not worksheet:
2233
                return
2234
            self.analyses_worksheet = worksheet
2235
        bsc = getToolByName(self.context, SETUP_CATALOG)
2236
        for row in self.get_rows(3, worksheet=self.analyses_worksheet):
2237
            if row['ReferenceSample_id'] != sample.getId():
2238
                continue
2239
            service = self.get_object(bsc, 'AnalysisService',
2240
                                      row.get('AnalysisService_title'))
2241
            # Analyses are keyed/named by service keyword
2242
            obj = _createObjectByType("ReferenceAnalysis", sample, row['id'])
2243
            obj.edit(title=row['id'],
2244
                     ReferenceType=row['ReferenceType'],
2245
                     Result=row['Result'],
2246
                     Analyst=row['Analyst'],
2247
                     Instrument=row['Instrument'],
2248
                     Retested=row['Retested']
2249
                     )
2250
            obj.setService(service)
2251
            # obj.setCreators(row['creator'])
2252
            # obj.setCreationDate(row['created'])
2253
            # self.set_wf_history(obj, row['workflow_history'])
2254
            obj.unmarkCreationFlag()
2255
2256
            self.load_reference_analysis_interims(obj)
2257
2258 View Code Duplication
    def load_reference_analysis_interims(self, analysis):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2259
        sheetname = 'Reference Analysis Interims'
2260
        if not hasattr(self, 'interim_worksheet'):
2261
            worksheet = self.workbook[sheetname]
2262
            if not worksheet:
2263
                return
2264
            self.interim_worksheet = worksheet
2265
        interims = []
2266
        for row in self.get_rows(3, worksheet=self.interim_worksheet):
2267
            if row['ReferenceAnalysis_id'] != analysis.getId():
2268
                continue
2269
            interims.append({
2270
                'keyword': row['keyword'],
2271
                'title': row['title'],
2272
                'value': row['value'],
2273
                'unit': row['unit'],
2274
                'hidden': row['hidden']})
2275
        analysis.setInterimFields(interims)
2276
2277
    def Import(self):
2278
        bsc = getToolByName(self.context, SETUP_CATALOG)
2279
        for row in self.get_rows(3):
2280
            if not row['id']:
2281
                continue
2282
            supplier = self.get_object(bsc, 'Supplier',
2283
                                       row.get('Supplier_title', ''))
2284
            obj = _createObjectByType("ReferenceSample", supplier, row['id'])
2285
            ref_def = self.get_object(bsc, 'ReferenceDefinition',
2286
                                      row.get('ReferenceDefinition_title'))
2287
            ref_man = self.get_object(bsc, 'Manufacturer',
2288
                                      row.get('Manufacturer_title'))
2289
            obj.edit(title=row['id'],
2290
                     description=row.get('description', ''),
2291
                     Blank=self.to_bool(row['Blank']),
2292
                     Hazardous=self.to_bool(row['Hazardous']),
2293
                     CatalogueNumber=row['CatalogueNumber'],
2294
                     LotNumber=row['LotNumber'],
2295
                     Remarks=row['Remarks'],
2296
                     ExpiryDate=row['ExpiryDate'],
2297
                     DateSampled=row['DateSampled'],
2298
                     DateReceived=row['DateReceived'],
2299
                     DateOpened=row['DateOpened'],
2300
                     DateExpired=row['DateExpired'],
2301
                     DateDisposed=row['DateDisposed']
2302
                     )
2303
            obj.setReferenceDefinition(ref_def)
2304
            obj.setManufacturer(ref_man)
2305
            obj.unmarkCreationFlag()
2306
2307
            self.load_reference_sample_results(obj)
2308
            self.load_reference_analyses(obj)
2309
2310
2311
class Analysis_Requests(WorksheetImporter):
2312
2313
    def load_analyses(self, sample):
2314
        sheetname = 'Analyses'
2315
        if not hasattr(self, 'analyses_worksheet'):
2316
            worksheet = self.workbook[sheetname]
2317
            if not worksheet:
2318
                return
2319
            self.analyses_worksheet = worksheet
2320
        bsc = getToolByName(self.context, SETUP_CATALOG)
2321
        bc = getToolByName(self.context, SENAITE_CATALOG)
2322
        for row in self.get_rows(3, worksheet=self.analyses_worksheet):
2323
            service = bsc(portal_type='AnalysisService',
2324
                          title=row['AnalysisService_title'])[0].getObject()
2325
            # analyses are keyed/named by keyword
2326
            ar = bc(portal_type='AnalysisRequest',
2327
                    id=row['AnalysisRequest_id'])[0].getObject()
2328
            obj = create_analysis(
2329
                ar, service,
2330
                Result=row['Result'],
2331
                ResultCaptureDate=row['ResultCaptureDate'],
2332
                Analyst=row['Analyst'],
2333
                Instrument=row['Instrument'],
2334
                Retested=self.to_bool(row['Retested']),
2335
                MaxTimeAllowed={
2336
                    'days': int(row.get('MaxTimeAllowed_days', 0)),
2337
                    'hours': int(row.get('MaxTimeAllowed_hours', 0)),
2338
                    'minutes': int(row.get('MaxTimeAllowed_minutes', 0)),
2339
                },
2340
            )
2341
2342
            analyses = ar.objectValues('Analyses')
2343
            analyses = list(analyses)
2344
            analyses.append(obj)
2345
            ar.setAnalyses(analyses)
2346
            obj.unmarkCreationFlag()
2347
2348
            self.load_analysis_interims(obj)
2349
2350 View Code Duplication
    def load_analysis_interims(self, analysis):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2351
        sheetname = 'Reference Analysis Interims'
2352
        if not hasattr(self, 'interim_worksheet'):
2353
            worksheet = self.workbook[sheetname]
2354
            if not worksheet:
2355
                return
2356
            self.interim_worksheet = worksheet
2357
        interims = []
2358
        for row in self.get_rows(3, worksheet=self.interim_worksheet):
2359
            if row['ReferenceAnalysis_id'] != analysis.getId():
2360
                continue
2361
            interims.append({
2362
                'keyword': row['keyword'],
2363
                'title': row['title'],
2364
                'value': row['value'],
2365
                'unit': row['unit'],
2366
                'hidden': row['hidden']})
2367
        analysis.setInterimFields(interims)
2368
2369
    def Import(self):
2370
        client_cat = api.get_tool(CLIENT_CATALOG)
2371
        contact_cat = api.get_tool(CONTACT_CATALOG)
2372
        setup_cat = api.get_tool(SETUP_CATALOG)
2373
        for row in self.get_rows(3):
2374
            if not row['id']:
2375
                continue
2376
            client = client_cat(portal_type="Client",
2377
                                getName=row['Client_title'])[0].getObject()
2378
            obj = _createObjectByType("AnalysisRequest", client, row['id'])
2379
            contact = contact_cat(portal_type="Contact",
2380
                                  getFullname=row['Contact_Fullname'])[0].getObject()
2381
            obj.edit(
2382
                RequestID=row['id'],
2383
                Contact=contact,
2384
                CCEmails=row['CCEmails'],
2385
                ClientOrderNumber=row['ClientOrderNumber'],
2386
                InvoiceExclude=row['InvoiceExclude'],
2387
                DateReceived=row['DateReceived'],
2388
                DatePublished=row['DatePublished'],
2389
                Remarks=row['Remarks']
2390
            )
2391
            if row['CCContact_Fullname']:
2392
                contact = contact_cat(portal_type="Contact",
2393
                                      getFullname=row['CCContact_Fullname'])[0].getObject()
2394
                obj.setCCContact(contact)
2395
            if row['AnalysisProfile_title']:
2396
                profiles = setup_cat(portal_type="AnalysisProfile",
2397
                                     title=row['AnalysisProfile_title'])[0].getObject()
2398
                obj.setProfiles([profiles])
2399
            if row['ARTemplate_title']:
2400
                template = setup_cat(portal_type="ARTemplate",
2401
                                     title=row['ARTemplate_title'])[0].getObject()
2402
                obj.setTemplate(template)
2403
2404
            obj.unmarkCreationFlag()
2405
2406
            self.load_analyses(obj)
2407
2408
2409
class Invoice_Batches(WorksheetImporter):
2410
2411
    def Import(self):
2412
        folder = self.context.invoices
2413
        for row in self.get_rows(3):
2414
            obj = _createObjectByType("InvoiceBatch", folder, tmpID())
2415
            if not row['title']:
2416
                message = _("InvoiceBatch has no Title")
2417
                raise Exception(t(message))
2418
            if not row['start']:
2419
                message = _("InvoiceBatch has no Start Date")
2420
                raise Exception(t(message))
2421
            if not row['end']:
2422
                message = _("InvoiceBatch has no End Date")
2423
                raise Exception(t(message))
2424
            obj.edit(
2425
                title=row['title'],
2426
                BatchStartDate=row['start'],
2427
                BatchEndDate=row['end'],
2428
            )
2429
            renameAfterCreation(obj)
2430
            notify(ObjectInitializedEvent(obj))
2431