Passed
Push — master ( 31959a...a22ccc )
by Ramon
08:21 queued 04:06
created

bika.lims.browser.resultsimport.resultsimport   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 39
eloc 158
dl 0
loc 205
rs 9.28
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A ResultsImportView.__init__() 0 2 1
A ResultsImportView.add_to_log_file() 0 10 4
A ResultsImportView.format_log_data() 0 8 1
F ResultsImportView.__call__() 0 82 15
A ResultsImportView.getAlreadyImportedFiles() 0 13 4
A ResultsImportView.getInfoFromLog() 0 8 4
A ResultsImportView.add_to_logs() 0 13 3
A ConvertToUploadFile.__init__() 0 13 4
A ResultsImportView.insert_file_name() 0 7 3
1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE
4
#
5
# Copyright 2018 by it's authors.
6
# Some rights reserved. See LICENSE.rst, CONTRIBUTORS.rst.
7
8
import csv
9
from DateTime.DateTime import DateTime
10
from bika.lims.browser import BrowserView
11
from bika.lims.exportimport.instruments import get_automatic_parser
12
from bika.lims.utils import tmpID
13
from Products.CMFCore.utils import getToolByName
14
from bika.lims.exportimport.instruments.resultsimport import \
15
    AnalysisResultsImporter
16
import traceback
17
from os import listdir
18
from os.path import isfile, join
19
from bika.lims.idserver import renameAfterCreation
20
from bika.lims import logger
21
from datetime import datetime
22
23
24
class ResultsImportView(BrowserView):
25
26
    """
27
    This view will be called from any periodically running script to run
28
    auto-import process. Instruments which has interfaces, and also interfaces
29
    which auto-import folders assigned, will participate in this process.
30
    To import for specified Instrument or/and Interface, these parameters can
31
    be set in URL as well.
32
    """
33
    def __init__(self, context, request):
34
        super(ResultsImportView, self).__init__(context, request)
35
36
    def __call__(self):
37
        request = self.request
38
        bsc = getToolByName(self, 'bika_setup_catalog')
39
        # Getting instrumnets to run auto-import
40
        query = {'portal_type': 'Instrument',
41
                 'inactive_state': 'active'}
42
        if request.get('i_uid', ''):
43
            query['UID'] = request.get('i_uid')
44
        brains = bsc(query)
45
        interfaces = []
46
        for brain in brains:
47
            i = brain.getObject()
48
            logger.info('Auto import for ' + i.Title())
49
            # If Import Interface ID is specified in request, then auto-import
50
            # will run only that interface. Otherwise all available interfaces
51
            # of this instruments
52
            if request.get('interface', ''):
53
                interfaces.append(request.get('interface'))
54
            else:
55
                interfaces = [pairs.get('InterfaceName', '') for pairs
56
                              in i.getResultFilesFolder()]
57
            folder = ''
58
            for interface in interfaces:
59
                # Each interface must have its folder where result files are
60
                # saved. If not, then we will skip
61
                for pairs in i.getResultFilesFolder():
62
                    if pairs['InterfaceName'] == interface:
63
                        folder = pairs.get('Folder', '')
64
                if not folder:
65
                    continue
66
                logger.info('Auto import for ' + interface)
67
                all_files = [f for f in listdir(folder)
68
                             if isfile(join(folder, f))]
69
                imported_list = self.getAlreadyImportedFiles(folder)
70
                if not imported_list:
71
                    logger.warn('imported.csv file not found ' + interface)
72
                    self.add_to_logs(i, interface,
73
                                     'imported.csv File not found...', '')
74
                    continue
75
                for file_name in all_files:
76
                    if file_name in imported_list:
77
                        continue
78
                    temp_file = open(folder+'/'+file_name)
79
                    # Parsers work with UploadFile object from
80
                    # zope.HTTPRequest which has filename attribute.
81
                    # To add this attribute we convert the file.
82
                    # CHECK should we add headers too?
83
                    result_file = ConvertToUploadFile(temp_file)
84
                    parser = get_automatic_parser(interface, result_file)
85
                    if not parser:
86
                        self.add_to_logs(i, interface,
87
                                         'Parser not found...', file_name)
88
                        continue
89
                    # We will run import with some default parameters
90
                    # Expected to be modified in the future.
91
                    logger.info('Parsing ' + file_name)
92
                    importer = AnalysisResultsImporter(
93
                                parser=parser,
94
                                context=self.portal,
95
                                override=[False, False],
96
                                instrument_uid=i.UID())
97
                    tbex = ''
98
                    try:
99
                        importer.process()
100
                    except:
101
                        tbex = traceback.format_exc()
102
                    errors = importer.errors
103
                    logs = importer.logs
104
                    if tbex:
105
                        errors.append(tbex)
106
                    final_log = ''
107
                    success_log = self.getInfoFromLog(logs, 'Import finished')
108
                    if success_log:
109
                        final_log = success_log
110
                    else:
111
                        final_log = errors
112
                    self.insert_file_name(folder, file_name)
113
                    self.add_to_logs(i, interface, final_log, file_name)
114
                    self.add_to_log_file(i.Title(), interface, final_log,
115
                                         file_name, folder)
116
        logger.info('End of auto import...')
117
        return 'Auto-Import finished...'
118
119
    def getAlreadyImportedFiles(self, folder):
120
        try:
121
            with open(folder+'/imported.csv', 'r') as f:
122
                imported = f.readlines()
123
                imported = [i.strip() for i in imported]
124
                return imported
125
        except:
126
            with open(folder+'/imported.csv', 'w') as f:
127
                f.write('imported.csv\n')
128
                f.write('logs.log\n')
129
                f.close()
130
            return ['']
131
        return None
132
133
    def insert_file_name(self, folder, name):
134
        try:
135
            with open(folder+'/imported.csv', 'a') as fd:
136
                fd.write(name+'\n')
137
                fd.close()
138
        except:
139
            pass
140
141
    def getInfoFromLog(self, logs, keyword):
142
        try:
143
            for log in logs:
144
                if keyword in log:
145
                    return log
146
            return None
147
        except:
148
            return None
149
150
    def add_to_logs(self, instrument, interface, log, filename):
151
        if not log:
152
            return
153
        log = ''.join(log)
154
        log = log[:80]+'...' if len(log) > 80 else log
155
        _id = instrument.invokeFactory("AutoImportLog", id=tmpID(),
156
                                       Instrument=instrument,
157
                                       Interface=interface,
158
                                       Results=log,
159
                                       ImportedFile=filename)
160
        item = instrument[_id]
161
        item.unmarkCreationFlag()
162
        renameAfterCreation(item)
163
164
    def add_to_log_file(self, instrument, interface, log, filename, folder):
165
        log = self.format_log_data(instrument, interface, log, filename)
166
        try:
167
            with open(folder+'/logs.log', 'a') as fd:
168
                fd.write(log+'\n')
169
                fd.close()
170
        except:
171
            with open(folder+'/logs.log', 'w') as f:
172
                f.write(log+'\n')
173
                f.close()
174
175
    def format_log_data(self, instrument, interface, result, filename):
176
        log = DateTime.strftime(DateTime(), '%Y-%m-%d %H:%M:%S')
177
        log += ' - ' + instrument
178
        log += ' - ' + interface
179
        log += ' - ' + filename
180
        r = ''.join(result)
181
        log += ' - ' + r
182
        return log
183
184
185
class ConvertToUploadFile:
186
    """
187
    File objects don't have 'filename' and 'headers' attributes.
188
    Since Import step of different Interfaces checks if 'filename' is set
189
    to be sure that submitted form contains uploaded file, we also have to add
190
    this attribute to our File object.
191
    """
192
    def __init__(self, orig_file):
193
        if hasattr(orig_file, '__methods__'):
194
            methods = orig_file.__methods__
195
        else:
196
            methods = ['close', 'fileno', 'flush', 'isatty',
197
                       'read', 'readline', 'readlines', 'seek',
198
                       'tell', 'truncate', 'write', 'writelines',
199
                       '__iter__', 'next', 'name']
200
        d = self.__dict__
201
        for m in methods:
202
            if hasattr(orig_file, m):
203
                d[m] = getattr(orig_file, m)
204
        self.filename = orig_file.name
205