kalenislims/lims/results_report.py

3143 lines
129 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of lims module for Tryton.
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
import operator
from io import BytesIO
from datetime import datetime
from PyPDF2 import PdfFileMerger
from trytond.model import ModelView, ModelSQL, fields
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
Button
from trytond.pool import Pool
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, And, Or, If
from trytond.transaction import Transaction
from trytond.report import Report
from trytond.rpc import RPC
from trytond.exceptions import UserError
from trytond.i18n import gettext
__all__ = ['ResultsReport', 'ResultsReportVersion',
'ResultsReportVersionDetail', 'ResultsReportVersionDetailLine',
'DivideReportsResult', 'DivideReportsDetail',
'DivideReportsProcess', 'DivideReports',
'GenerateResultsReportStart', 'GenerateResultsReportEmpty',
'GenerateResultsReportResultAut', 'GenerateResultsReportResultMan',
'GenerateResultsReportResultAutNotebook',
'GenerateResultsReportResultAutNotebookLine',
'GenerateResultsReportResultAutExcludedNotebook',
'GenerateResultsReportResultAutExcludedNotebookLine',
'GenerateResultsReport', 'PrintResultsReport', 'ServiceResultsReport',
'FractionResultsReport', 'SampleResultsReport', 'ResultsReportSample',
'ResultsReportAnnulationStart', 'ResultsReportAnnulation', 'ResultReport',
'GlobalResultReport', 'ResultReportTranscription']
def get_print_date():
Company = Pool().get('company.company')
date = datetime.now()
company_id = Transaction().context.get('company')
if company_id:
date = Company(company_id).convert_timezone_datetime(date)
return date
class ResultsReport(ModelSQL, ModelView):
'Results Report'
__name__ = 'lims.results_report'
_rec_name = 'number'
number = fields.Char('Number', select=True, readonly=True)
versions = fields.One2Many('lims.results_report.version',
'results_report', 'Laboratories')
report_grouper = fields.Integer('Report Grouper')
generation_type = fields.Char('Generation type')
cie_fraction_type = fields.Boolean('QA', readonly=True)
party = fields.Many2One('party.party', 'Party', readonly=True)
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook')
report_cache = fields.Binary('Report cache', readonly=True,
file_id='report_cache_id', store_prefix='results_report')
report_cache_id = fields.Char('Report cache id', readonly=True)
report_format = fields.Char('Report format', readonly=True)
report_cache_eng = fields.Binary('Report cache', readonly=True,
file_id='report_cache_eng_id', store_prefix='results_report')
report_cache_eng_id = fields.Char('Report cache id', readonly=True)
report_format_eng = fields.Char('Report format', readonly=True)
single_sending_report = fields.Function(fields.Boolean(
'Single sending'), 'get_single_sending_report',
searcher='search_single_sending_report')
single_sending_report_ready = fields.Function(fields.Boolean(
'Single sending Ready'), 'get_single_sending_report_ready')
state = fields.Function(fields.Selection([
('draft', 'Draft'),
('revised', 'Revised'),
], 'State'), 'get_state', searcher='search_state')
english_report = fields.Boolean('English report')
create_date2 = fields.Function(fields.DateTime('Create Date'),
'get_create_date2', searcher='search_create_date2')
write_date2 = fields.DateTime('Write Date', readonly=True)
attachments = fields.One2Many('ir.attachment', 'resource', 'Attachments')
@classmethod
def __setup__(cls):
super(ResultsReport, cls).__setup__()
cls._order.insert(0, ('number', 'DESC'))
@staticmethod
def default_report_grouper():
return 0
@classmethod
def create(cls, vlist):
pool = Pool()
LabWorkYear = pool.get('lims.lab.workyear')
Sequence = pool.get('ir.sequence')
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
sequence = workyear.get_sequence('results_report')
if not sequence:
raise UserError(gettext('lims.msg_no_sequence',
work_year=workyear.rec_name))
vlist = [x.copy() for x in vlist]
for values in vlist:
values['number'] = Sequence.get_id(sequence.id)
return super(ResultsReport, cls).create(vlist)
@classmethod
def write(cls, *args):
actions = iter(args)
for reports, vals in zip(actions, actions):
fields_check = cls._get_modified_fields()
for field in fields_check:
if field in vals:
vals['write_date2'] = datetime.now()
break
super(ResultsReport, cls).write(*args)
@staticmethod
def _get_modified_fields():
return [
'number',
'versions',
'report_grouper',
'generation_type',
'cie_fraction_type',
'party',
'notebook',
'english_report',
'attachments',
]
def get_single_sending_report(self, name):
pool = Pool()
Notebook = pool.get('lims.notebook')
if self.notebook:
with Transaction().set_user(0):
notebook = Notebook(self.notebook.id)
return notebook.fraction.sample.entry.single_sending_report
return False
@classmethod
def search_single_sending_report(cls, name, clause):
return [('notebook.fraction.sample.entry.' + name,) +
tuple(clause[1:])]
def get_single_sending_report_ready(self, name):
pool = Pool()
Notebook = pool.get('lims.notebook')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
if not self.single_sending_report:
return False
with Transaction().set_user(0):
notebook = Notebook(self.notebook.id)
if EntryDetailAnalysis.search([
('fraction', '=', notebook.fraction.id),
('report', '=', True),
('report_grouper', '=', self.report_grouper),
('state', '!=', 'reported'),
]):
return False
return True
@classmethod
def get_state(cls, reports, name=None):
result = {}
for r in reports:
revised = True
for version in r.versions:
valid_detail = version.details and version.details[0] or None
if not valid_detail:
revised = False
break
elif valid_detail.state != 'revised':
revised = False
break
result[r.id] = revised and 'revised' or 'draft'
return result
@classmethod
def search_state(cls, name, domain=None):
operator_funcs = {
'=': operator.eq,
'!=': operator.ne,
'in': lambda v, l: v in l,
}
_, op, operand = domain
reports = cls.search([])
result = cls.get_state(reports)
record_ids = [k for k, v in result.items()
if operator_funcs[op](v, operand)]
return [('id', 'in', record_ids)]
def get_create_date2(self, name):
return self.create_date.replace(microsecond=0)
@classmethod
def search_create_date2(cls, name, clause):
cursor = Transaction().connection.cursor()
operator_ = clause[1:2][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE create_date' + operator_ + ' %s',
clause[2:3])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
def order_create_date2(cls, tables):
return cls.create_date.convert_order('create_date', tables, cls)
class ResultsReportVersion(ModelSQL, ModelView):
'Results Report Version'
__name__ = 'lims.results_report.version'
_rec_name = 'number'
results_report = fields.Many2One('lims.results_report', 'Results Report',
required=True, ondelete='CASCADE', select=True)
number = fields.Char('Number', select=True, readonly=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
required=True, readonly=True)
details = fields.One2Many('lims.results_report.version.detail',
'report_version', 'Detail lines')
report_type = fields.Function(fields.Char('Report type'),
'get_report_type')
@classmethod
def __setup__(cls):
super(ResultsReportVersion, cls).__setup__()
cls._order.insert(0, ('number', 'DESC'))
def get_report_type(self, name):
ResultsReportVersionDetail = Pool().get(
'lims.results_report.version.detail')
valid_detail = ResultsReportVersionDetail.search([
('report_version.id', '=', self.id),
], order=[('id', 'DESC')], limit=1)
if valid_detail:
return valid_detail[0].report_type
return None
@classmethod
def get_number(cls, results_report_id, laboratory_id):
pool = Pool()
ResultsReport = pool.get('lims.results_report')
Laboratory = pool.get('lims.laboratory')
results_reports = ResultsReport.search([
('id', '=', results_report_id),
])
report_number = results_reports[0].number
laboratories = Laboratory.search([
('id', '=', laboratory_id),
])
laboratory_code = laboratories[0].code
return '%s-%s' % (report_number, laboratory_code)
@classmethod
def create(cls, vlist):
vlist = [x.copy() for x in vlist]
for values in vlist:
values['number'] = cls.get_number(values['results_report'],
values['laboratory'])
return super(ResultsReportVersion, cls).create(vlist)
class ResultsReportVersionDetail(ModelSQL, ModelView):
'Results Report Version Detail'
__name__ = 'lims.results_report.version.detail'
report_version = fields.Many2One('lims.results_report.version',
'Report', required=True, readonly=True,
ondelete='CASCADE', select=True)
laboratory = fields.Function(fields.Many2One('lims.laboratory',
'Laboratory'), 'get_version_field', searcher='search_version_field')
number = fields.Char('Version', select=True, readonly=True)
valid = fields.Boolean('Active', readonly=True)
state = fields.Selection([
('draft', 'Draft'),
('revised', 'Revised'),
('annulled', 'Annulled'),
], 'State', readonly=True)
notebook_lines = fields.One2Many('lims.results_report.version.detail.line',
'report_version_detail', 'Lines', depends=['state'],
states={'readonly': Eval('state') != 'draft'})
report_section = fields.Function(fields.Char('Section'),
'get_report_section')
report_type_forced = fields.Selection([
('none', 'None'),
('normal', 'Normal'),
('polisample', 'Polisample'),
], 'Forced Report type', sort=False, depends=['state'],
states={'readonly': Eval('state') != 'draft'})
report_type = fields.Function(fields.Selection([
('normal', 'Normal'),
('polisample', 'Polisample'),
], 'Report type', sort=False), 'on_change_with_report_type')
report_result_type_forced = fields.Selection([
('none', 'None'),
('result', 'Result'),
('both', 'Both'),
('result_range', 'Result and Ranges'),
('both_range', 'Both and Ranges'),
], 'Forced Result type', sort=False, depends=['state'],
states={'readonly': Eval('state') != 'draft'})
report_result_type = fields.Function(fields.Selection([
('result', 'Result'),
('both', 'Both'),
('result_range', 'Result and Ranges'),
('both_range', 'Both and Ranges'),
], 'Result type', sort=False), 'on_change_with_report_result_type')
english_report = fields.Function(fields.Boolean('English report'),
'get_report_field', searcher='search_report_field')
signer = fields.Many2One('lims.laboratory.professional', 'Signer',
states={'readonly': Eval('state') != 'draft'},
domain=[('id', 'in', Eval('signer_domain'))],
depends=['state', 'signer_domain'])
signer_domain = fields.Function(fields.Many2Many(
'lims.laboratory.professional', None, None, 'Signer domain'),
'on_change_with_signer_domain')
comments = fields.Text('Comments', translate=True, depends=['state'],
states={'readonly': ~Eval('state').in_(['draft', 'revised'])})
report_cache = fields.Binary('Report cache', readonly=True,
file_id='report_cache_id', store_prefix='results_report')
report_cache_id = fields.Char('Report cache id', readonly=True)
report_format = fields.Char('Report format', readonly=True)
report_cache_eng = fields.Binary('Report cache', readonly=True,
file_id='report_cache_eng_id', store_prefix='results_report')
report_cache_eng_id = fields.Char('Report cache id', readonly=True)
report_format_eng = fields.Char('Report format', readonly=True)
report_cache_odt = fields.Binary('Transcription Report cache',
readonly=True, file_id='report_cache_odt_id',
store_prefix='results_report')
report_cache_odt_id = fields.Char('Transcription Report cache id',
readonly=True)
report_format_odt = fields.Char('Transcription Report format',
readonly=True)
report_cache_odt_eng = fields.Binary('Transcription Report cache',
readonly=True, file_id='report_cache_odt_eng_id',
store_prefix='results_report')
report_cache_odt_eng_id = fields.Char('Transcription Report cache id',
readonly=True)
report_format_odt_eng = fields.Char('Transcription Report format',
readonly=True)
annulment_reason = fields.Text('Annulment reason', translate=True,
states={'readonly': Eval('state') != 'annulled'}, depends=['state'])
annulment_date = fields.DateTime('Annulment date', readonly=True)
annulment_reason_print = fields.Boolean('Print annulment reason',
states={'readonly': Eval('state') != 'annulled'}, depends=['state'])
date = fields.Function(fields.Date('Date'), 'get_date',
searcher='search_date')
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_report_field', searcher='search_report_field')
cie_fraction_type = fields.Function(fields.Boolean('QA'),
'get_report_field', searcher='search_report_field')
create_date2 = fields.Function(fields.DateTime('Create Date'),
'get_create_date2', searcher='search_create_date2')
write_date2 = fields.Function(fields.DateTime('Write Date'),
'get_write_date2', searcher='search_write_date2')
resultrange_origin = fields.Many2One('lims.range.type', 'Origin',
domain=[('use', '=', 'result_range')],
depends=['report_result_type', 'state'], states={
'invisible': Not(Eval('report_result_type').in_([
'result_range', 'both_range'])),
'required': Eval('report_result_type').in_([
'result_range', 'both_range']),
'readonly': Eval('state') != 'draft',
})
fraction_comments = fields.Function(fields.Text('Fraction comments'),
'get_fraction_comments')
icon = fields.Function(fields.Char("Icon"), 'get_icon')
@classmethod
def __setup__(cls):
super(ResultsReportVersionDetail, cls).__setup__()
cls._order.insert(0, ('report_version', 'DESC'))
cls._order.insert(1, ('number', 'DESC'))
cls._buttons.update({
'revise': {
'invisible': (Eval('state') != 'draft'),
},
'annul': {
'invisible': Or(Eval('state') != 'revised', ~Eval('valid')),
},
'revise_all_lang': {
'invisible': Not(If(Bool(Eval('english_report')),
Bool(And(
~Bool(Eval('report_cache_eng')),
Bool(Eval('report_cache')),
)),
Bool(And(
~Bool(Eval('report_cache')),
Bool(Eval('report_cache_eng')),
)),
)),
},
})
@staticmethod
def default_report_type_forced():
return 'none'
@staticmethod
def default_report_result_type_forced():
return 'none'
@staticmethod
def default_annulment_reason_print():
return True
@classmethod
def get_next_number(cls, report_version_id, d_count):
detail_number = cls.search_count([
('report_version', '=', report_version_id),
])
detail_number += d_count
return '%s' % detail_number
@classmethod
def create(cls, vlist):
vlist = [x.copy() for x in vlist]
d_count = {}
for values in vlist:
key = values['report_version']
if key not in d_count:
d_count[key] = 0
d_count[key] += 1
values['number'] = cls.get_next_number(key, d_count[key])
return super(ResultsReportVersionDetail, cls).create(vlist)
@staticmethod
def default_valid():
return False
@staticmethod
def default_state():
return 'draft'
def get_report_section(self, name):
if self.laboratory:
return self.laboratory.section
return None
@fields.depends('report_type_forced')
def on_change_with_report_type(self, name=None):
if self.report_type_forced != 'none':
return self.report_type_forced
report_type = {
'normal': 0,
'polisample': 0,
}
cursor = Transaction().connection.cursor()
cursor.execute('SELECT COUNT(*), t.report_type '
'FROM lims_results_report_version_detail_l d, '
'lims_notebook_line l, lims_typification t, '
'lims_notebook n, lims_fraction f, lims_sample s '
'WHERE d.report_version_detail = %s '
'AND d.notebook_line = l.id '
'AND s.product_type = t.product_type '
'AND s.matrix = t.matrix '
'AND l.analysis = t.analysis '
'AND l.method = t.method '
'AND t.valid = true '
'AND l.notebook = n.id '
'AND n.fraction = f.id '
'AND f.sample = s.id '
'GROUP BY t.report_type',
(self.id, ))
res = cursor.fetchall()
for type_ in res:
if type_[0]:
report_type[type_[1]] = type_[0]
if report_type['polisample'] > report_type['normal']:
return 'polisample'
return 'normal'
@fields.depends('report_result_type_forced')
def on_change_with_report_result_type(self, name=None):
if self.report_result_type_forced != 'none':
return self.report_result_type_forced
report_res_type = {
'result': 0,
'both': 0,
}
cursor = Transaction().connection.cursor()
cursor.execute('SELECT COUNT(*), t.report_result_type '
'FROM lims_results_report_version_detail_l d, '
'lims_notebook_line l, lims_typification t, '
'lims_notebook n, lims_fraction f, lims_sample s '
'WHERE d.report_version_detail = %s '
'AND d.notebook_line = l.id '
'AND s.product_type = t.product_type '
'AND s.matrix = t.matrix '
'AND l.analysis = t.analysis '
'AND l.method = t.method '
'AND t.valid = true '
'AND l.notebook = n.id '
'AND n.fraction = f.id '
'AND f.sample = s.id '
'GROUP BY t.report_result_type',
(self.id, ))
res = cursor.fetchall()
for type_ in res:
if type_[0]:
report_res_type[type_[1]] = type_[0]
if report_res_type['both'] > report_res_type['result']:
return 'both'
return 'result'
@fields.depends('report_result_type_forced', 'resultrange_origin')
def on_change_report_result_type_forced(self):
pool = Pool()
RangeType = pool.get('lims.range.type')
if ((self.report_result_type_forced == 'result_range' or
self.report_result_type_forced == 'both_range') and
not self.resultrange_origin):
ranges = RangeType.search([
('use', '=', 'result_range'),
('by_default', '=', True),
])
if ranges:
self.resultrange_origin = ranges[0].id
@fields.depends('laboratory')
def on_change_with_signer_domain(self, name=None):
pool = Pool()
UserLaboratory = pool.get('lims.user-laboratory')
LaboratoryProfessional = pool.get('lims.laboratory.professional')
if not self.laboratory:
return []
users = UserLaboratory.search([
('laboratory', '=', self.laboratory.id),
])
if not users:
return []
professionals = LaboratoryProfessional.search([
('party.lims_user', 'in', [u.user.id for u in users]),
('role', '!=', ''),
])
if not professionals:
return []
return [p.id for p in professionals]
@classmethod
@ModelView.button
def revise(cls, details):
ResultsReportVersionDetailLine = Pool().get(
'lims.results_report.version.detail.line')
cls.revise_notebook_lines(details)
for detail in details:
defaults = {
'state': 'revised',
'valid': True,
}
valid_details = cls.search([
('report_version', '=', detail.report_version.id),
('valid', '=', True),
])
if valid_details:
vd_ids = []
notebook_lines = []
for vd in valid_details:
vd_ids.append(vd.id)
for nline in vd.notebook_lines:
notebook_lines.append({
'notebook_line': nline.notebook_line.id,
})
if notebook_lines:
defaults['notebook_lines'] = [('create', notebook_lines)]
cls.write(valid_details, {
'valid': False,
})
old_lines = ResultsReportVersionDetailLine.search([
('report_version_detail.id', 'in', vd_ids),
])
ResultsReportVersionDetailLine.delete(old_lines)
cls.write([detail], defaults)
detail.generate_report()
@classmethod
def revise_notebook_lines(cls, details):
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
for detail in details:
revised_lines = []
revised_entry_details = []
for nline in detail.notebook_lines:
revised_lines.append(nline.notebook_line.id)
revised_entry_details.append(
nline.notebook_line.analysis_detail.id)
notebook_lines = NotebookLine.search([
('id', 'in', revised_lines),
])
if notebook_lines:
NotebookLine.write(notebook_lines, {
'results_report': detail.report_version.results_report.id,
})
entry_details = EntryDetailAnalysis.search([
('id', 'in', revised_entry_details),
])
if entry_details:
EntryDetailAnalysis.write(entry_details, {
'state': 'reported',
})
@classmethod
@ModelView.button_action('lims.wiz_lims_results_report_annulation')
def annul(cls, details):
pass
@classmethod
def annul_notebook_lines(cls, details):
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
for detail in details:
annulled_lines = []
annulled_entry_details = []
for nline in detail.notebook_lines:
annulled_lines.append(nline.notebook_line.id)
annulled_entry_details.append(
nline.notebook_line.analysis_detail.id)
notebook_lines = NotebookLine.search([
('id', 'in', annulled_lines),
('results_report', '=',
detail.report_version.results_report.id),
])
if notebook_lines:
NotebookLine.write(notebook_lines, {
'results_report': None,
})
entry_details = EntryDetailAnalysis.search([
('id', 'in', annulled_entry_details),
])
if entry_details:
EntryDetailAnalysis.write(entry_details, {
'state': 'done',
})
@classmethod
@ModelView.button
def revise_all_lang(cls, details):
for detail in details:
detail.generate_report()
def generate_report(self):
pool = Pool()
ResultReport = pool.get('lims.result_report', type='report')
ResultReportTranscription = pool.get(
'lims.result_report.transcription', type='report')
ResultReport.execute([self.id], {
'english_report': self.english_report,
})
ResultReportTranscription.execute([self.id], {
'english_report': self.english_report,
})
def get_date(self, name):
pool = Pool()
Company = pool.get('company.company')
date = self.write_date if self.write_date else self.create_date
company_id = Transaction().context.get('company')
if company_id:
date = Company(company_id).convert_timezone_datetime(date)
return date.date()
@classmethod
def search_date(cls, name, clause):
pool = Pool()
Company = pool.get('company.company')
cursor = Transaction().connection.cursor()
timezone = None
company_id = Transaction().context.get('company')
if company_id:
timezone = Company(company_id).timezone
timezone_datetime = ('COALESCE(write_date, create_date)::timestamp'
' AT TIME ZONE \'UTC\'')
if timezone:
timezone_datetime += ' AT TIME ZONE \'' + timezone + '\''
operator_ = clause[1:2][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE (' + timezone_datetime + ')::date ' +
operator_ + ' %s::date', clause[2:3])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
def get_version_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
for d in details:
field = getattr(d.report_version, name, None)
result[name][d.id] = field.id if field else None
return result
@classmethod
def search_version_field(cls, name, clause):
return [('report_version.' + name,) + tuple(clause[1:])]
@classmethod
def get_report_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name in ('cie_fraction_type', 'english_report'):
for d in details:
field = getattr(d.report_version.results_report, name,
False)
result[name][d.id] = field
else:
for d in details:
field = getattr(d.report_version.results_report, name,
None)
result[name][d.id] = field.id if field else None
return result
@classmethod
def search_report_field(cls, name, clause):
return [('report_version.results_report.' + name,) + tuple(clause[1:])]
@classmethod
def get_create_date2(cls, details, name):
result = {}
for d in details:
create_date = getattr(d, 'create_date', None)
result[d.id] = (create_date.replace(microsecond=0)
if create_date else None)
return result
@classmethod
def search_create_date2(cls, name, clause):
cursor = Transaction().connection.cursor()
operator_ = clause[1:2][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE create_date' + operator_ + ' %s',
clause[2:3])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
def order_create_date2(cls, tables):
return cls.create_date.convert_order('create_date', tables, cls)
@classmethod
def get_write_date2(cls, details, name):
result = {}
for d in details:
write_date = getattr(d, 'write_date', None)
result[d.id] = (write_date.replace(microsecond=0)
if write_date else None)
return result
@classmethod
def search_write_date2(cls, name, clause):
cursor = Transaction().connection.cursor()
operator_ = clause[1:2][0]
cursor.execute('SELECT id '
'FROM "' + cls._table + '" '
'WHERE write_date' + operator_ + ' %s',
clause[2:3])
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
@classmethod
def order_write_date2(cls, tables):
return cls.write_date.convert_order('write_date', tables, cls)
@classmethod
def delete(cls, details):
cls.check_delete(details)
super(ResultsReportVersionDetail, cls).delete(details)
@classmethod
def check_delete(cls, details):
for detail in details:
if detail.state != 'draft':
raise UserError(gettext('lims.msg_delete_detail_not_draft'))
@classmethod
def get_fraction_comments(cls, details, name):
result = {}
for d in details:
result[d.id] = None
notebook = getattr(d.report_version.results_report,
'notebook', None)
if notebook:
result[d.id] = getattr(notebook, 'fraction_comments')
return result
def get_icon(self, name):
if self.fraction_comments:
return 'lims-blue'
return 'lims-white'
class ResultsReportVersionDetailLine(ModelSQL, ModelView):
'Results Report Version Detail Line'
__name__ = 'lims.results_report.version.detail.line'
_table = 'lims_results_report_version_detail_l'
report_version_detail = fields.Many2One(
'lims.results_report.version.detail', 'Report Detail',
required=True, ondelete='CASCADE', select=True)
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
required=True, select=True)
notebook = fields.Function(fields.Many2One('lims.notebook',
'Laboratory notebook'), 'get_nline_field')
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_nline_field')
analysis = fields.Function(fields.Many2One('lims.analysis', 'Analysis'),
'get_nline_field')
repetition = fields.Function(fields.Integer('Repetition'),
'get_nline_field')
start_date = fields.Function(fields.Date('Start date'), 'get_nline_field')
end_date = fields.Function(fields.Date('End date'), 'get_nline_field')
laboratory = fields.Function(fields.Many2One('lims.laboratory',
'Laboratory'), 'get_nline_field')
method = fields.Function(fields.Many2One('lims.lab.method', 'Method'),
'get_nline_field')
device = fields.Function(fields.Many2One('lims.lab.device', 'Device'),
'get_nline_field')
analysis_origin = fields.Function(fields.Char('Analysis origin'),
'get_nline_field')
urgent = fields.Function(fields.Boolean('Urgent'), 'get_nline_field')
priority = fields.Function(fields.Integer('Priority'), 'get_nline_field')
report_date = fields.Function(fields.Date('Date agreed for result'),
'get_nline_field')
result_modifier = fields.Function(fields.Selection([
('eq', '='),
('low', '<'),
('d', 'Detected'),
('nd', 'nd'),
('na', 'na'),
('pos', 'Positive'),
('neg', 'Negative'),
('ni', 'ni'),
('abs', 'Absence'),
('pre', 'Presence'),
], 'Result modifier'), 'get_nline_field')
converted_result_modifier = fields.Function(fields.Selection([
('eq', '='),
('low', '<'),
('nd', 'nd'),
('na', 'na'),
('pos', 'Positive'),
('neg', 'Negative'),
('ni', 'ni'),
], 'Converted result modifier'), 'get_nline_field')
result = fields.Function(fields.Char('Result'), 'get_nline_field')
converted_result = fields.Function(fields.Char('Converted result'),
'get_nline_field')
initial_unit = fields.Function(fields.Many2One('product.uom',
'Initial unit'), 'get_nline_field')
final_unit = fields.Function(fields.Many2One('product.uom',
'Final unit'), 'get_nline_field')
comments = fields.Function(fields.Text('Entry comments'),
'get_nline_field')
literal_result = fields.Function(fields.Char('Literal result'),
'get_nline_field')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_nline_field')
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
'get_nline_field')
@classmethod
def get_nline_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name in ('notebook', 'party', 'analysis', 'laboratory',
'method', 'device', 'initial_unit', 'final_unit',
'product_type', 'matrix'):
for d in details:
field = getattr(d.notebook_line, name, None)
result[name][d.id] = field.id if field else None
else:
for d in details:
result[name][d.id] = getattr(d.notebook_line, name, None)
return result
class DivideReportsResult(ModelView):
'Divide Reports'
__name__ = 'lims.divide_reports.result'
services = fields.Many2Many('lims.service', None, None, 'Services')
total = fields.Integer('Total')
index = fields.Integer('Index')
class DivideReportsDetail(ModelSQL, ModelView):
'Analysis Detail'
__name__ = 'lims.divide_reports.detail'
detail_id = fields.Integer('Detail ID')
analysis_origin = fields.Char('Analysis origin', readonly=True)
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
readonly=True)
report_grouper = fields.Integer('Report Grouper')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(DivideReportsDetail,
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
class DivideReportsProcess(ModelView):
'Divide Reports'
__name__ = 'lims.divide_reports.process'
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
service = fields.Many2One('lims.service', 'Service', readonly=True)
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
analysis_detail = fields.One2Many('lims.divide_reports.detail',
None, 'Analysis detail')
class DivideReports(Wizard):
'Divide Reports'
__name__ = 'lims.divide_reports'
start_state = 'search'
search = StateTransition()
result = StateView('lims.divide_reports.result',
'lims.lims_divide_reports_result_view_form', [])
next_ = StateTransition()
process = StateView('lims.divide_reports.process',
'lims.lims_divide_reports_process_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Next', 'next_', 'tryton-forward', default=True),
])
confirm = StateTransition()
def transition_search(self):
Service = Pool().get('lims.service')
services = Service.search([
('entry', '=', Transaction().context['active_id']),
('divide', '=', True),
])
if services:
self.result.services = services
self.result.total = len(self.result.services)
self.result.index = 0
return 'next_'
return 'end'
def transition_next_(self):
has_prev = (hasattr(self.process, 'analysis_detail') and
getattr(self.process, 'analysis_detail'))
if has_prev:
for detail in self.process.analysis_detail:
detail.save()
if self.result.index < self.result.total:
service = self.result.services[self.result.index]
self.process.service = service.id
self.process.analysis_detail = None
self.result.index += 1
return 'process'
return 'confirm'
def default_process(self, fields):
DivideReportsDetail = Pool().get(
'lims.divide_reports.detail')
if not self.process.service:
return {}
default = {}
default['fraction'] = self.process.service.fraction.id
default['service'] = self.process.service.id
default['analysis'] = self.process.service.analysis.id
details = DivideReportsDetail.create([{
'detail_id': d.id,
'analysis_origin': d.analysis_origin,
'analysis': d.analysis.id,
'laboratory': d.laboratory.id,
'report_grouper': d.report_grouper,
'session_id': self._session_id,
} for d in self.process.service.analysis_detail])
if details:
default['analysis_detail'] = [d.id for d in details]
return default
def transition_confirm(self):
pool = Pool()
DivideReportsDetail = pool.get(
'lims.divide_reports.detail')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
details = DivideReportsDetail.search([
('session_id', '=', self._session_id),
])
for detail in details:
analysis_detail = EntryDetailAnalysis(detail.detail_id)
analysis_detail.report_grouper = detail.report_grouper
analysis_detail.save()
return 'end'
class GenerateResultsReportStart(ModelView):
'Generate Results Report'
__name__ = 'lims.generate_results_report.start'
date_from = fields.Date('Date from', required=True)
date_to = fields.Date('Date to', required=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
required=True)
party = fields.Many2One('party.party', 'Party')
generation_type = fields.Selection([
('aut', 'Automatic'),
('man', 'Manual'),
], 'Generation type', sort=False)
class GenerateResultsReportEmpty(ModelView):
'Generate Results Report'
__name__ = 'lims.generate_results_report.empty'
class GenerateResultsReportResultAut(ModelView):
'Generate Results Report'
__name__ = 'lims.generate_results_report.result_aut'
notebooks = fields.One2Many(
'lims.generate_results_report.aut.notebook', None, 'Results',
readonly=True)
notebook_lines = fields.Many2Many('lims.notebook.line', None, None,
'Results', readonly=True)
excluded_notebooks = fields.One2Many(
'lims.generate_results_report.aut.excluded_notebook', None,
'Excluded Fractions', readonly=True)
reports_details = fields.One2Many('lims.results_report.version.detail',
None, 'Reports details')
class GenerateResultsReportResultAutNotebook(ModelSQL, ModelView):
'Notebook'
__name__ = 'lims.generate_results_report.aut.notebook'
notebook = fields.Many2One('lims.notebook', 'Notebook', readonly=True)
fraction = fields.Function(fields.Many2One('lims.fraction', 'Fraction'),
'get_notebook_field')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_notebook_field')
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
'get_notebook_field')
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_notebook_field')
label = fields.Function(fields.Char('Label'), 'get_notebook_field')
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_notebook_field')
date = fields.Function(fields.DateTime('Date'), 'get_notebook_field')
lines = fields.Many2Many(
'lims.generate_results_report.aut.notebook-line', 'notebook',
'line', 'Lines', readonly=True)
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(GenerateResultsReportResultAutNotebook,
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def get_notebook_field(cls, notebooks, names):
result = {}
for name in names:
result[name] = {}
if name in ('label', 'date'):
for n in notebooks:
result[name][n.id] = getattr(n.notebook, name, None)
else:
for n in notebooks:
field = getattr(n.notebook, name, None)
result[name][n.id] = field.id if field else None
return result
class GenerateResultsReportResultAutNotebookLine(ModelSQL, ModelView):
'Notebook Line'
__name__ = 'lims.generate_results_report.aut.notebook-line'
notebook = fields.Many2One(
'lims.generate_results_report.aut.notebook', 'Notebook',
ondelete='CASCADE', select=True, required=True)
line = fields.Many2One('lims.notebook.line', 'Notebook Line',
ondelete='CASCADE', select=True, required=True)
class GenerateResultsReportResultAutExcludedNotebook(ModelSQL, ModelView):
'Excluded Notebook'
__name__ = 'lims.generate_results_report.aut.excluded_notebook'
_table = 'lims_generate_results_report_aut_excluded_nb'
notebook = fields.Many2One('lims.notebook', 'Notebook', readonly=True)
fraction = fields.Function(fields.Many2One('lims.fraction', 'Fraction'),
'get_notebook_field')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_notebook_field')
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
'get_notebook_field')
party = fields.Function(fields.Many2One('party.party', 'Party'),
'get_notebook_field')
label = fields.Function(fields.Char('Label'), 'get_notebook_field')
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_notebook_field')
date = fields.Function(fields.DateTime('Date'), 'get_notebook_field')
lines = fields.Many2Many(
'lims.generate_results_report.aut.excluded_notebook-line', 'notebook',
'line', 'Lines', readonly=True)
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(GenerateResultsReportResultAutExcludedNotebook,
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def get_notebook_field(cls, notebooks, names):
result = {}
for name in names:
result[name] = {}
if name in ('label', 'date'):
for n in notebooks:
result[name][n.id] = getattr(n.notebook, name, None)
else:
for n in notebooks:
field = getattr(n.notebook, name, None)
result[name][n.id] = field.id if field else None
return result
class GenerateResultsReportResultAutExcludedNotebookLine(ModelSQL,
ModelView):
'Excluded Notebook Line'
__name__ = 'lims.generate_results_report.aut.excluded_notebook-line'
_table = 'lims_generate_results_report_aut_excluded_nb-line'
notebook = fields.Many2One(
'lims.generate_results_report.aut.excluded_notebook',
'Notebook', ondelete='CASCADE', select=True, required=True)
line = fields.Many2One('lims.notebook.line', 'Notebook Line',
ondelete='CASCADE', select=True, required=True)
class GenerateResultsReportResultMan(ModelView):
'Generate Results Report'
__name__ = 'lims.generate_results_report.result_man'
notebook_lines = fields.Many2Many('lims.notebook.line', None, None,
'Results', required=True, depends=['notebook_lines_domain', 'party',
'report_type', 'notebook', 'report_grouper', 'cie_fraction_type'],
domain=[('id', 'in', Eval('notebook_lines_domain')),
If(Bool(Eval('party')),
('notebook.party', '=', Eval('party')), ('id', '!=', -1)),
If(Bool(Equal(Eval('report_type'), 'normal')),
('notebook', '=', Eval('notebook')), ('id', '!=', -1)),
If(Bool(Eval('report_grouper')),
('analysis_detail.report_grouper', '=',
Eval('report_grouper')), ('id', '!=', -1)),
If(Bool(Eval('cie_fraction_type')),
('notebook.fraction.cie_fraction_type', '=',
Eval('cie_fraction_type')), ('id', '!=', -1)),
])
notebook_lines_domain = fields.One2Many('lims.notebook.line', None,
'Results domain', readonly=True)
report = fields.Many2One('lims.results_report', 'Report',
states={'readonly': Bool(Eval('notebook_lines'))},
domain=[('id', 'in', Eval('report_domain'))],
depends=['notebook_lines', 'report_domain'])
report_domain = fields.One2Many('lims.results_report', None,
'Reports domain')
report_type_forced = fields.Selection([
('none', 'None'),
('normal', 'Normal'),
('polisample', 'Polisample'),
], 'Force report type', sort=False, depends=['report'],
states={'invisible': Bool(Eval('report'))})
party = fields.Many2One('party.party', 'Party')
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook')
report_grouper = fields.Integer('Report Grouper')
report_type = fields.Char('Report type')
cie_fraction_type = fields.Boolean('QA')
laboratory = fields.Many2One('lims.laboratory', 'Laboratory')
reports_details = fields.One2Many('lims.results_report.version.detail',
None, 'Reports details')
@fields.depends('report', '_parent_report.party')
def on_change_with_party(self, name=None):
if self.report:
return self.report.party.id
return None
@fields.depends('report', '_parent_report.notebook')
def on_change_with_notebook(self, name=None):
if self.report and self.report.notebook:
return self.report.notebook.id
return None
@fields.depends('report', '_parent_report.report_grouper')
def on_change_with_report_grouper(self, name=None):
if self.report:
return self.report.report_grouper
return None
@fields.depends('report', 'laboratory')
def on_change_with_report_type(self, name=None):
if self.report:
ResultsReportVersion = Pool().get(
'lims.results_report.version')
version = ResultsReportVersion.search([
('results_report.id', '=', self.report.id),
('laboratory.id', '=', self.laboratory.id),
], limit=1)
if version:
return version[0].report_type
version = ResultsReportVersion.search([
('results_report.id', '=', self.report.id),
], order=[('id', 'DESC')], limit=1)
if version:
return version[0].report_type
return None
@fields.depends('report', '_parent_report.cie_fraction_type')
def on_change_with_cie_fraction_type(self, name=None):
if self.report:
with Transaction().set_context(_check_access=False):
return self.report.cie_fraction_type
return False
class GenerateResultsReport(Wizard):
'Generate Results Report'
__name__ = 'lims.generate_results_report'
start = StateView('lims.generate_results_report.start',
'lims.lims_generate_results_report_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Search', 'search', 'tryton-forward', default=True),
])
search = StateTransition()
empty = StateView('lims.generate_results_report.empty',
'lims.lims_generate_results_report_empty_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Search again', 'start', 'tryton-forward', default=True),
])
result_aut = StateView('lims.generate_results_report.result_aut',
'lims.lims_generate_results_report_result_aut_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Generate', 'generate', 'tryton-ok', default=True),
])
result_man = StateView('lims.generate_results_report.result_man',
'lims.lims_generate_results_report_result_man_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Generate', 'generate', 'tryton-ok', default=True),
])
generate = StateTransition()
open = StateAction('lims.act_lims_results_report_version_detail')
def default_start(self, fields):
res = {}
for field in ('date_from', 'date_to', 'generation_type'):
if (hasattr(self.start, field) and getattr(self.start, field)):
res[field] = getattr(self.start, field)
for field in ('laboratory', 'party'):
if (hasattr(self.start, field) and getattr(self.start, field)):
res[field] = getattr(self.start, field).id
if 'generation_type' not in res:
res['generation_type'] = 'aut'
if 'laboratory' not in res:
res['laboratory'] = Transaction().context.get('laboratory', None)
return res
def transition_search(self):
if self.start.generation_type == 'aut':
return self._search_aut()
return self._search_man()
def _get_notebook_lines(self, generation_type, excluded_notebooks=[]):
cursor = Transaction().connection.cursor()
pool = Pool()
ResultsReportVersionDetailLine = pool.get(
'lims.results_report.version.detail.line')
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
clause = [
('notebook.fraction.type.report', '=', True),
('notebook.date2', '>=', self.start.date_from),
('notebook.date2', '<=', self.start.date_to),
('laboratory', '=', self.start.laboratory.id),
('report', '=', True),
('annulled', '=', False),
]
if self.start.party:
clause.append(('notebook.party', '=', self.start.party.id))
draft_lines_ids = []
draft_lines = ResultsReportVersionDetailLine.search([
('report_version_detail.state', '=', 'draft'),
])
if draft_lines:
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
clause.extend([
('accepted', '=', True),
('results_report', '=', None),
('id', 'not in', draft_lines_ids),
])
if generation_type == 'aut':
for n_id, grouper in excluded_notebooks:
cursor.execute('SELECT nl.id '
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + EntryDetailAnalysis._table + '" d '
'ON d.id = nl.analysis_detail '
'WHERE nl.notebook = %s AND d.report_grouper = %s',
(n_id, grouper))
excluded_notebook_lines = [x[0] for x in cursor.fetchall()]
clause.append(('id', 'not in', excluded_notebook_lines))
return NotebookLine.search(clause)
def _get_excluded_notebooks(self):
cursor = Transaction().connection.cursor()
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
Notebook = pool.get('lims.notebook')
Fraction = pool.get('lims.fraction')
Sample = pool.get('lims.sample')
Entry = pool.get('lims.entry')
FractionType = pool.get('lims.fraction.type')
party_clause = ''
if self.start.party:
party_clause = 'AND e.party = ' + str(self.start.party.id)
cursor.execute('SELECT nl.notebook, nl.analysis, nl.accepted, '
'd.report_grouper '
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + EntryDetailAnalysis._table + '" d '
'ON d.id = nl.analysis_detail '
'INNER JOIN "' + Notebook._table + '" n '
'ON n.id = nl.notebook '
'INNER JOIN "' + Fraction._table + '" f '
'ON f.id = n.fraction '
'INNER JOIN "' + Sample._table + '" s '
'ON s.id = f.sample '
'INNER JOIN "' + Entry._table + '" e '
'ON e.id = s.entry '
'INNER JOIN "' + FractionType._table + '" ft '
'ON ft.id = f.type '
'WHERE ft.report = TRUE '
'AND s.date::date >= %s::date '
'AND s.date::date <= %s::date '
'AND nl.laboratory = %s '
'AND nl.report = TRUE '
'AND nl.annulled = FALSE ' +
party_clause,
(self.start.date_from, self.start.date_to,
self.start.laboratory.id,))
notebook_lines = cursor.fetchall()
# Check accepted repetitions
to_check = []
oks = []
accepted_notebooks = []
for line in notebook_lines:
key = (line[0], line[1], line[3])
if not line[2]:
to_check.append(key)
else:
oks.append(key)
accepted_notebooks.append(line[0])
to_check = list(set(to_check) - set(oks))
accepted_notebooks = list(set(accepted_notebooks))
excluded_notebooks = {}
for n_id, a_id, grouper in to_check:
if n_id not in accepted_notebooks:
continue
key = (n_id, grouper)
if key not in excluded_notebooks:
excluded_notebooks[key] = []
excluded_notebooks[key].append(a_id)
return excluded_notebooks
def _search_aut(self):
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
GenerateResultsReportResultAutNotebook = pool.get(
'lims.generate_results_report.aut.notebook')
GenerateResultsReportResultAutExcludedNotebook = pool.get(
'lims.generate_results_report.aut.excluded_notebook')
self.result_aut.excluded_notebooks = None
self.result_aut.notebooks = None
self.result_aut.notebook_lines = None
excluded_notebooks = self._get_excluded_notebooks()
if excluded_notebooks:
notebooks = {}
for (n_id, grouper), a_ids in excluded_notebooks.items():
clause = [
('notebook.id', '=', n_id),
('analysis_detail.report_grouper', '=', grouper),
('analysis', 'in', a_ids),
('laboratory', '=', self.start.laboratory.id),
('report', '=', True),
('annulled', '=', False),
]
excluded_lines = NotebookLine.search(clause)
if excluded_lines:
notebooks[n_id] = [line.id for line in excluded_lines]
to_create = [{
'session_id': self._session_id,
'notebook': k,
'lines': [('add', v)],
} for k, v in notebooks.items()]
self.result_aut.excluded_notebooks = (
GenerateResultsReportResultAutExcludedNotebook.create(
to_create))
notebook_lines = self._get_notebook_lines('aut',
list(excluded_notebooks.keys()))
if notebook_lines:
notebooks = {}
for line in notebook_lines:
if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = []
notebooks[line.notebook.id].append(line.id)
to_create = []
for k, v in notebooks.items():
to_create.append({
'session_id': self._session_id,
'notebook': k,
'lines': [('add', v)],
})
self.result_aut.notebooks = (
GenerateResultsReportResultAutNotebook.create(to_create))
self.result_aut.notebook_lines = [l.id for l in
notebook_lines]
if notebook_lines or excluded_notebooks:
return 'result_aut'
return 'empty'
def default_result_aut(self, fields):
ret = {
'notebooks': [],
'notebook_lines': [],
'excluded_notebooks': [],
}
if self.result_aut.notebooks:
ret['notebooks'] = [n.id for n in self.result_aut.notebooks]
if self.result_aut.notebook_lines:
ret['notebook_lines'] = [l.id for l in
self.result_aut.notebook_lines]
if self.result_aut.excluded_notebooks:
sorted_notebooks = sorted(self.result_aut.excluded_notebooks,
key=lambda n: n.fraction.number)
ret['excluded_notebooks'] = [n.id for n in sorted_notebooks]
self.result_aut.notebooks = None
self.result_aut.excluded_notebooks = None
return ret
def _search_man(self):
ResultsReport = Pool().get('lims.results_report')
notebook_lines = self._get_notebook_lines('man')
if notebook_lines:
self.result_man.notebook_lines_domain = [l.id for l in
notebook_lines]
self.result_man.report_domain = []
clause = [
('generation_type', '=', 'man'),
]
if self.start.party:
clause.append(('party', '=', self.start.party.id))
reports = ResultsReport.search(clause)
if reports:
self.result_man.report_domain = [r.id for r in reports]
return 'result_man'
return 'empty'
def default_result_man(self, fields):
notebook_lines = [l.id for l in self.result_man.notebook_lines_domain]
self.result_man.notebook_lines_domain = None
reports = []
if self.result_man.report_domain:
reports = [r.id for r in self.result_man.report_domain]
return {
'notebook_lines': [],
'notebook_lines_domain': notebook_lines,
'report': None,
'report_domain': reports,
'report_type_forced': 'none',
'laboratory': self.start.laboratory.id,
}
def transition_generate(self):
if self.start.generation_type == 'aut':
if self.result_aut.notebook_lines:
return self._generate_aut()
else:
return 'empty'
return self._generate_man()
def _generate_aut(self):
NotebookLine = Pool().get('lims.notebook.line')
notebooks = {}
for line in self.result_aut.notebook_lines:
if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = {
'party': line.notebook.party.id,
'notebook': line.notebook.id,
'divided_report': line.notebook.divided_report,
'english_report': (
line.notebook.fraction.entry.english_report),
'notebook_lines': [],
'cie_fraction_type': (
line.notebook.fraction.cie_fraction_type),
}
notebooks[line.notebook.id]['notebook_lines'].append({
'notebook_line': line.id,
})
reports_details = []
for notebook in notebooks.values():
if not notebook['divided_report']:
details = {
'notebook_lines': [('create', notebook['notebook_lines'])],
'signer': self.start.laboratory.default_signer.id,
}
versions = {
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}
reports = {
'party': notebook['party'],
'notebook': notebook['notebook'],
'report_grouper': 0,
'generation_type': 'aut',
'cie_fraction_type': notebook['cie_fraction_type'],
'english_report': notebook['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(reports, versions,
details)
reports_details.extend(report_detail)
else:
grouped_reports = {}
for line in notebook['notebook_lines']:
nline = NotebookLine(line['notebook_line'])
report_grouper = nline.analysis_detail.report_grouper
if report_grouper not in grouped_reports:
grouped_reports[report_grouper] = []
grouped_reports[report_grouper].append(line)
for grouper, notebook_lines in grouped_reports.items():
details = {
'notebook_lines': [('create', notebook_lines)],
'signer': self.start.laboratory.default_signer.id,
}
versions = {
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}
reports = {
'party': notebook['party'],
'notebook': notebook['notebook'],
'report_grouper': grouper,
'generation_type': 'aut',
'cie_fraction_type': notebook['cie_fraction_type'],
'english_report': notebook['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(reports, versions,
details)
reports_details.extend(report_detail)
self.result_aut.reports_details = reports_details
return 'open'
def _generate_man(self):
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
ResultsReportVersion = pool.get('lims.results_report.version')
ResultsReportVersionDetail = pool.get(
'lims.results_report.version.detail')
if self.result_man.report: # Result report selected
report_type_forced = self.result_man.report_type
if report_type_forced == 'polisample': # Polisample report
notebook_lines = []
for line in self.result_man.notebook_lines:
notebook_lines.append({
'notebook_line': line.id,
})
details = {
'notebook_lines': [('create', notebook_lines)],
'report_type_forced': 'polisample',
'signer': self.start.laboratory.default_signer.id,
}
actual_version = ResultsReportVersion.search([
('results_report', '=', self.result_man.report.id),
('laboratory', '=', self.start.laboratory.id),
], limit=1)
if not actual_version:
version, = ResultsReportVersion.create([{
'results_report': self.result_man.report.id,
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}])
reports_details = [d.id for d in version.details]
else:
draft_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('state', '=', 'draft'),
], limit=1)
if not draft_detail:
details['report_version'] = actual_version[0].id
valid_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('valid', '=', True),
], limit=1)
if valid_detail:
details['report_type_forced'] = (
valid_detail[0].report_type_forced)
details['report_result_type_forced'] = (
valid_detail[0].report_result_type_forced)
if valid_detail[0].signer:
details['signer'] = valid_detail[0].signer.id
if valid_detail[0].resultrange_origin:
details['resultrange_origin'] = (
valid_detail[0].resultrange_origin.id)
details['comments'] = str(
valid_detail[0].comments or '')
detail, = ResultsReportVersionDetail.create([
details])
reports_details = [detail.id]
else:
del details['signer']
ResultsReportVersionDetail.write(draft_detail,
details)
reports_details = [draft_detail[0].id]
else: # Normal report
notebook_lines = []
for line in self.result_man.notebook_lines:
notebook_lines.append({
'notebook_line': line.id,
})
details = {
'notebook_lines': [('create', notebook_lines)],
'signer': self.start.laboratory.default_signer.id,
}
actual_version = ResultsReportVersion.search([
('results_report', '=', self.result_man.report.id),
('laboratory', '=', self.start.laboratory.id),
], limit=1)
if not actual_version:
version, = ResultsReportVersion.create([{
'results_report': self.result_man.report.id,
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}])
reports_details = [d.id for d in version.details]
else:
draft_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('state', '=', 'draft'),
], limit=1)
if not draft_detail:
details['report_version'] = actual_version[0].id
valid_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('valid', '=', True),
], limit=1)
if valid_detail:
details['report_type_forced'] = (
valid_detail[0].report_type_forced)
details['report_result_type_forced'] = (
valid_detail[0].report_result_type_forced)
if valid_detail[0].signer:
details['signer'] = valid_detail[0].signer.id
if valid_detail[0].resultrange_origin:
details['resultrange_origin'] = (
valid_detail[0].resultrange_origin.id)
details['comments'] = str(
valid_detail[0].comments or '')
detail, = ResultsReportVersionDetail.create([
details])
reports_details = [detail.id]
else:
del details['signer']
ResultsReportVersionDetail.write(draft_detail,
details)
reports_details = [draft_detail[0].id]
self.result_man.reports_details = reports_details
else: # Not Result report selected
report_type_forced = self.result_man.report_type_forced
if report_type_forced == 'polisample': # Polisample report
parties = {}
for line in self.result_man.notebook_lines:
key = (line.notebook.party.id,
line.notebook.fraction.cie_fraction_type)
if key not in parties:
parties[key] = {
'party': line.notebook.party.id,
'english_report': (
line.notebook.fraction.entry.english_report),
'notebook_lines': [],
'cie_fraction_type': (
line.notebook.fraction.cie_fraction_type),
}
parties[key]['notebook_lines'].append({
'notebook_line': line.id,
})
reports_details = []
for party in parties.values():
grouped_reports = {}
for line in party['notebook_lines']:
nline = NotebookLine(line['notebook_line'])
report_grouper = nline.analysis_detail.report_grouper
if report_grouper not in grouped_reports:
grouped_reports[report_grouper] = []
grouped_reports[report_grouper].append(line)
for grouper, notebook_lines in grouped_reports.items():
details = {
'notebook_lines': [('create', notebook_lines)],
'report_type_forced': report_type_forced,
'signer': self.start.laboratory.default_signer.id,
}
versions = {
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}
reports = {
'party': party['party'],
'notebook': None,
'report_grouper': grouper,
'generation_type': 'man',
'cie_fraction_type': party['cie_fraction_type'],
'english_report': party['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(reports,
versions, details, append=False)
reports_details.extend(report_detail)
else: # Normal report
notebooks = {}
for line in self.result_man.notebook_lines:
if line.notebook.id not in notebooks:
notebooks[line.notebook.id] = {
'party': line.notebook.party.id,
'notebook': line.notebook.id,
'divided_report': line.notebook.divided_report,
'english_report': (
line.notebook.fraction.entry.english_report),
'notebook_lines': [],
'cie_fraction_type': (
line.notebook.fraction.cie_fraction_type),
}
notebooks[line.notebook.id]['notebook_lines'].append({
'notebook_line': line.id,
})
reports_details = []
for notebook in notebooks.values():
if not notebook['divided_report']:
details = {
'notebook_lines': [('create',
notebook['notebook_lines'])],
'report_type_forced': report_type_forced,
'signer': self.start.laboratory.default_signer.id,
}
versions = {
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}
reports = {
'party': notebook['party'],
'notebook': notebook['notebook'],
'report_grouper': 0,
'generation_type': 'man',
'cie_fraction_type': notebook['cie_fraction_type'],
'english_report': notebook['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(reports,
versions, details, append=False)
reports_details.extend(report_detail)
else:
grouped_reports = {}
for line in notebook['notebook_lines']:
nline = NotebookLine(line['notebook_line'])
report_grouper = (
nline.analysis_detail.report_grouper)
if report_grouper not in grouped_reports:
grouped_reports[report_grouper] = []
grouped_reports[report_grouper].append(line)
for grouper, notebook_lines in \
grouped_reports.items():
details = {
'notebook_lines': [('create', notebook_lines)],
'report_type_forced': report_type_forced,
'signer': (
self.start.laboratory.default_signer.id),
}
versions = {
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}
reports = {
'party': notebook['party'],
'notebook': notebook['notebook'],
'report_grouper': grouper,
'generation_type': 'man',
'cie_fraction_type': (
notebook['cie_fraction_type']),
'english_report': notebook['english_report'],
'versions': [('create', [versions])],
}
report_detail = self._get_results_report(reports,
versions, details, append=False)
reports_details.extend(report_detail)
self.result_man.reports_details = reports_details
return 'open'
def _get_results_report(self, reports, versions, details, append=True):
pool = Pool()
ResultsReport = pool.get('lims.results_report')
ResultsReportVersion = pool.get('lims.results_report.version')
ResultsReportVersionDetail = pool.get(
'lims.results_report.version.detail')
if not append:
report, = ResultsReport.create([reports])
reports_details = [d.id for d in report.versions[0].details]
return reports_details
actual_report = ResultsReport.search([
('party', '=', reports['party']),
('notebook', '=', reports['notebook']),
('report_grouper', '=', reports['report_grouper']),
('generation_type', '=', reports['generation_type']),
('cie_fraction_type', '=', reports['cie_fraction_type']),
], limit=1)
if not actual_report:
report, = ResultsReport.create([reports])
reports_details = [d.id for d in report.versions[0].details]
return reports_details
actual_version = ResultsReportVersion.search([
('results_report', '=', actual_report[0].id),
('laboratory', '=', self.start.laboratory.id),
], limit=1)
if not actual_version:
version, = ResultsReportVersion.create([{
'results_report': actual_report[0].id,
'laboratory': self.start.laboratory.id,
'details': [('create', [details])],
}])
reports_details = [d.id for d in version.details]
else:
draft_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('state', '=', 'draft'),
], limit=1)
if not draft_detail:
details['report_version'] = actual_version[0].id
valid_detail = ResultsReportVersionDetail.search([
('report_version', '=', actual_version[0].id),
('valid', '=', True),
], limit=1)
if valid_detail:
if details.get('report_type_forced') != 'none':
details['report_type_forced'] = (
valid_detail[0].report_type_forced)
details['report_result_type_forced'] = (
valid_detail[0].report_result_type_forced)
if valid_detail[0].signer:
details['signer'] = valid_detail[0].signer.id
if valid_detail[0].resultrange_origin:
details['resultrange_origin'] = (
valid_detail[0].resultrange_origin.id)
details['comments'] = str(
valid_detail[0].comments or '')
detail, = ResultsReportVersionDetail.create([details])
reports_details = [detail.id]
else:
if 'report_type_forced' in details:
del details['report_type_forced']
del details['signer']
ResultsReportVersionDetail.write(draft_detail, details)
reports_details = [draft_detail[0].id]
return reports_details
def do_open(self, action):
if self.start.generation_type == 'aut':
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', [r.id for r in self.result_aut.reports_details]),
])
self.result_aut.reports_details = None
else:
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', [r.id for r in self.result_man.reports_details]),
])
self.result_man.reports_details = None
return action, {}
class PrintResultsReport(Wizard):
'Print Results Report'
__name__ = 'lims.print_results_report'
start = StateTransition()
print_ = StateAction('lims.report_global_results_report')
def transition_start(self):
pool = Pool()
ResultsReport = pool.get('lims.results_report')
ResultsReportVersionDetail = pool.get(
'lims.results_report.version.detail')
for active_id in Transaction().context['active_ids']:
results_report = ResultsReport(active_id)
format_field = 'report_format'
if results_report.english_report:
format_field = 'report_format_eng'
with Transaction().set_user(0):
details = ResultsReportVersionDetail.search([
('report_version.results_report.id', '=',
results_report.id),
('valid', '=', True),
(format_field, '=', 'pdf'),
])
if not details:
raise UserError(gettext('lims.msg_empty_report'))
if results_report.english_report:
results_report.report_format_eng = 'pdf'
results_report.report_cache_eng = self._get_global_report(
details, True)
else:
results_report.report_format = 'pdf'
results_report.report_cache = self._get_global_report(
details, False)
results_report.save()
return 'print_'
def _get_global_report(self, details, english_report=False):
merger = PdfFileMerger()
if english_report:
for detail in details:
filedata = BytesIO(detail.report_cache_eng)
merger.append(filedata)
else:
for detail in details:
filedata = BytesIO(detail.report_cache)
merger.append(filedata)
output = BytesIO()
merger.write(output)
return bytearray(output.getvalue())
def do_print_(self, action):
data = {}
data['id'] = Transaction().context['active_ids'].pop()
data['ids'] = [data['id']]
return action, data
def transition_print_(self):
if Transaction().context['active_ids']:
return 'print_'
return 'end'
class ServiceResultsReport(Wizard):
'Service Results Report'
__name__ = 'lims.service.results_report'
start = StateAction('lims.act_lims_results_report')
def do_start(self, action):
pool = Pool()
Service = pool.get('lims.service')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
service = Service(Transaction().context['active_id'])
results_report_ids = []
details = EntryDetailAnalysis.search([
('service', '=', service.id),
])
if details:
results_report_ids = [d.results_report.id for d in details
if d.results_report]
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', results_report_ids),
])
action['name'] += ' (%s)' % service.rec_name
return action, {}
class FractionResultsReport(Wizard):
'Fraction Results Report'
__name__ = 'lims.fraction.results_report'
start = StateAction('lims.act_lims_results_report')
def do_start(self, action):
pool = Pool()
Fraction = pool.get('lims.fraction')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
fraction = Fraction(Transaction().context['active_id'])
results_report_ids = []
details = EntryDetailAnalysis.search([
('fraction', '=', fraction.id),
])
if details:
results_report_ids = [d.results_report.id for d in details
if d.results_report]
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', results_report_ids),
])
action['name'] += ' (%s)' % fraction.rec_name
return action, {}
class SampleResultsReport(Wizard):
'Sample Results Report'
__name__ = 'lims.sample.results_report'
start = StateAction('lims.act_lims_results_report')
def do_start(self, action):
pool = Pool()
Sample = pool.get('lims.sample')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
active_ids = Transaction().context['active_ids']
samples = Sample.browse(active_ids)
results_report_ids = []
details = EntryDetailAnalysis.search([('sample', 'in', active_ids)])
if details:
results_report_ids = [d.results_report.id for d in details
if d.results_report]
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', results_report_ids),
])
action['name'] += ' (%s)' % ', '.join(
s.rec_name for s in samples)
return action, {}
class ResultsReportSample(Wizard):
'Results Report Sample'
__name__ = 'lims.results_report.sample'
start = StateAction('lims.act_lims_sample_list')
def do_start(self, action):
pool = Pool()
ResultsReport = pool.get('lims.results_report')
NotebookLine = pool.get('lims.notebook.line')
active_ids = Transaction().context['active_ids']
results_reports = ResultsReport.browse(active_ids)
samples_ids = []
lines = NotebookLine.search([
('results_report', 'in', active_ids),
])
if lines:
samples_ids = [l.fraction.sample.id for l in lines]
action['pyson_domain'] = PYSONEncoder().encode([
('id', 'in', samples_ids),
])
action['name'] += ' (%s)' % ', '.join(
r.rec_name for r in results_reports)
return action, {}
class ResultsReportAnnulationStart(ModelView):
'Report Annulation'
__name__ = 'lims.results_report_annulation.start'
annulment_reason = fields.Text('Annulment reason', required=True,
translate=True)
annulment_reason_print = fields.Boolean(
'Print annulment reason in next version')
@staticmethod
def default_annulment_reason_print():
return True
class ResultsReportAnnulation(Wizard):
'Report Annulation'
__name__ = 'lims.results_report_annulation'
start = StateView('lims.results_report_annulation.start',
'lims.lims_results_report_annulation_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Annul', 'annul', 'tryton-ok', default=True),
])
annul = StateTransition()
def transition_annul(self):
ResultsReportVersionDetail = Pool().get(
'lims.results_report.version.detail')
details = ResultsReportVersionDetail.search([
('id', 'in', Transaction().context['active_ids']),
])
if details:
ResultsReportVersionDetail.annul_notebook_lines(details)
ResultsReportVersionDetail.write(details, {
'state': 'annulled',
'valid': False,
'report_cache': None,
'report_format': None,
'report_cache_eng': None,
'report_format_eng': None,
'annulment_reason': self.start.annulment_reason,
'annulment_date': datetime.now(),
})
return 'end'
class ResultReport(Report):
'Results Report'
__name__ = 'lims.result_report'
@classmethod
def __setup__(cls):
super(ResultReport, cls).__setup__()
cls.__rpc__['execute'] = RPC(False)
@classmethod
def execute(cls, ids, data):
ResultsReport = Pool().get('lims.results_report.version.detail')
if len(ids) > 1:
raise UserError(gettext('lims.msg_multiple_reports'))
results_report = ResultsReport(ids[0])
if results_report.state == 'annulled':
raise UserError(gettext('lims.msg_annulled_report'))
if data is None:
data = {}
current_data = data.copy()
current_data['alt_lang'] = None
result_orig = super(ResultReport, cls).execute(ids, current_data)
current_data['alt_lang'] = 'en'
result_eng = super(ResultReport, cls).execute(ids, current_data)
save = False
if results_report.english_report:
if results_report.report_cache_eng:
result = (results_report.report_format_eng,
results_report.report_cache_eng) + result_eng[2:]
else:
result = result_eng
if ('english_report' in current_data and
current_data['english_report']):
results_report.report_format_eng, \
results_report.report_cache_eng = result_eng[:2]
save = True
else:
if results_report.report_cache:
result = (results_report.report_format,
results_report.report_cache) + result_orig[2:]
else:
result = result_orig
if ('english_report' in current_data and
not current_data['english_report']):
results_report.report_format, \
results_report.report_cache = result_orig[:2]
save = True
if save:
results_report.save()
return result
@classmethod
def get_context(cls, records, data):
pool = Pool()
Company = pool.get('company.company')
ResultsReport = pool.get('lims.results_report.version.detail')
ResultsReportLine = pool.get('lims.results_report.version.detail.line')
NotebookLine = pool.get('lims.notebook.line')
Sample = pool.get('lims.sample')
RangeType = pool.get('lims.range.type')
report_context = super(ResultReport, cls).get_context(
records, data)
if data.get('alt_lang'):
lang_code = data['alt_lang']
else:
lang_code = report_context['user'].language.code
report_context['alt_lang'] = lang_code
with Transaction().set_context(language=lang_code):
if 'id' in data:
report = ResultsReport(data['id'])
else:
report = ResultsReport(records[0].id)
company = Company(Transaction().context.get('company'))
report_context['company'] = company
report_context['number'] = "%s-%s" % (report.report_version.number,
report.number)
report_context['replace_number'] = ''
if report.number != '1':
with Transaction().set_context(language=lang_code):
prev_number = "%s-%s" % (report.report_version.number,
int(report.number) - 1)
report_context['replace_number'] = (
gettext('lims.msg_replace_number', report=prev_number))
report_context['print_date'] = get_print_date()
report_context['party'] = (
report.report_version.results_report.party.rec_name)
party_address = (
report.report_version.results_report.party.address_get(
type='invoice'))
report_context['party_address'] = party_address.full_address.replace(
'\n', ' - ')
report_context['report_section'] = report.report_section
report_context['report_type'] = report.report_type
report_context['report_result_type'] = report.report_result_type
group_field = ('final_concentration' if
report.report_result_type in ('both', 'both_range') else
'initial_concentration')
report_context['signer'] = ''
report_context['signer_role'] = ''
report_context['signature'] = ''
report_context['headquarters'] = report.laboratory.headquarters
if report.signer:
report_context['signer'] = report.signer.rec_name
report_context['signer_role'] = report.signer.role
if report.signer.signature:
report_context['signature'] = report.signer.signature
enac = False
enac_all_acredited = True
initial_unit = None
min_start_date = None
max_end_date = None
min_confirmation_date = None
obs_ql = False
obs_dl = False
obs_uncert = False
obs_result_range = False
report_context['range_title'] = ''
if report.report_result_type in ('result_range', 'both_range'):
obs_result_range = True
with Transaction().set_context(language=lang_code):
range_type = RangeType(report.resultrange_origin.id)
report_context['range_title'] = range_type.resultrange_title
obs_rm_c_f = False
tas_project = False
stp_project = False
stp_polisample_project = False
water_project = False
alcohol = False
dry_matter = False
comments = {}
fractions = {}
methods = {}
pnt_methods = {}
notebook_lines = ResultsReportLine.search([
('report_version_detail.report_version.id', '=',
report.report_version.id), ['OR',
('report_version_detail.id', '=', report.id),
('report_version_detail.valid', '=', True)],
], order=[('report_version_detail', 'ASC')])
if not notebook_lines:
raise UserError(gettext('lims.msg_empty_report'))
with Transaction().set_context(language=lang_code):
reference_sample = Sample(
notebook_lines[0].notebook_line.fraction.sample.id)
if (report_context['report_section'] == 'rp' and
hasattr(reference_sample.entry, 'project_type') and
getattr(reference_sample.entry, 'project_type') ==
'study_plan'):
if report.report_type == 'normal' and not stp_project:
stp_project = True
if (report.report_type == 'polisample' and not
stp_polisample_project):
stp_polisample_project = True
if (hasattr(reference_sample.entry, 'project_type') and
getattr(reference_sample.entry, 'project_type') ==
'tas'):
tas_project = True
if (hasattr(reference_sample.entry, 'project_type') and
getattr(reference_sample.entry, 'project_type') ==
'water'):
water_project = True
for line in notebook_lines:
with Transaction().set_context(language=lang_code):
t_line = NotebookLine(line.notebook_line.id)
sample = Sample(line.notebook_line.fraction.sample.id)
key = t_line.fraction.id
if key not in fractions:
fractions[key] = {
'fraction': sample.number,
'date': sample.date2,
'client_description': (
sample.sample_client_description),
'number': sample.number,
'label': '(%s - %s)' % (sample.number,
sample.label),
'packages_quantity': sample.packages_quantity,
'package_type': (sample.package_type.description
if sample.package_type else ''),
'package_state': (
sample.package_state.description
if sample.package_state else ''),
'producer': (sample.producer.rec_name
if sample.producer else
gettext('lims.msg_data_not_specified')),
'obj_description': (sample.obj_description.description
if sample.obj_description else
sample.obj_description_manual),
'concentrations': {},
}
if (report.report_section == 'rp' and
report.report_type == 'polisample'):
fractions[key]['label'] = sample.label
if stp_polisample_project:
fractions[key]['stp_code'] = sample.entry.project.code
fractions[key]['stp_application_date'] = (
sample.application_date)
fractions[key]['stp_sampling_date'] = sample.sampling_date
fractions[key]['stp_zone'] = (sample.cultivation_zone
if sample.cultivation_zone else '')
fractions[key]['stp_after_application_days'] = (
sample.after_application_days)
fractions[key]['stp_treatment'] = sample.treatment
fractions[key]['stp_dosis'] = sample.dosis
fractions[key]['stp_repetition'] = sample.glp_repetitions
fractions[key]['stp_z_senasa_protocol'] = (
sample.z_senasa_protocol)
fractions[key]['stp_variety'] = (
sample.variety.description if
sample.variety else '')
if water_project:
if sample.sampling_datetime:
fractions[key]['water_sampling_date'] = (
cls.format_date(sample.sampling_datetime.date(),
report_context['user'].language))
else:
fractions[key]['water_sampling_date'] = gettext(
'lims.msg_not_done')
record = {
'order': t_line.analysis.order or 9999,
'acredited': cls.get_accreditation(
t_line.notebook.product_type,
t_line.notebook.matrix,
t_line.analysis,
t_line.method),
'pnt': t_line.method.pnt,
}
record['analysis'] = cls.get_analysis(
report_context['report_section'], t_line, language=lang_code)
record['result'], obs_ql = cls.get_result(
report_context['report_section'], t_line, obs_ql,
language=lang_code)
record['rp_order'] = float(2)
try:
record['rp_order'] = float(record['result']) * -1
except (TypeError, ValueError):
try:
if str(record['result']).startswith('<'):
record['rp_order'] = float(1)
except UnicodeEncodeError:
pass
record['converted_result'], obs_ql = cls.get_converted_result(
report_context['report_section'],
report_context['report_result_type'], t_line, obs_ql,
language=lang_code)
record['initial_unit'], obs_dl, obs_uncert = cls.get_initial_unit(
report_context['report_section'],
report_context['report_result_type'], t_line, obs_dl,
obs_uncert, language=lang_code)
record['final_unit'], obs_dl, obs_uncert = cls.get_final_unit(
report_context['report_section'],
report_context['report_result_type'], t_line, obs_dl,
obs_uncert, language=lang_code)
record['detection_limit'] = cls.get_detection_limit(
report_context['report_section'],
report_context['report_result_type'],
report_context['report_type'], t_line,
language=lang_code)
record['reference'] = ''
if obs_result_range:
record['reference'] = str(cls.get_reference(range_type,
t_line, lang_code, report_context['report_section']))
if (t_line.rm_correction_formula and (record['result'] or
(record['converted_result'] and
report_context['report_result_type'] in (
'both', 'both_range')))):
obs_rm_c_f = True
record['corrected'] = ''
else:
record['corrected'] = ''
conc = getattr(t_line, group_field)
if conc not in fractions[key]['concentrations']:
fractions[key]['concentrations'][conc] = []
fractions[key]['concentrations'][conc].append(record)
if not enac and record['acredited'] == 'True':
enac = True
if enac_all_acredited and record['acredited'] == 'False':
enac_all_acredited = False
if not initial_unit and t_line.initial_unit:
initial_unit = t_line.initial_unit.rec_name
entry_id = t_line.fraction.sample.entry.id
if entry_id not in comments:
comments[entry_id] = {
'report_comments': (
t_line.fraction.sample.entry.report_comments),
'samples': {},
}
if sample.id not in comments[entry_id]['samples']:
comments[entry_id]['samples'][sample.id] = (
sample.report_comments)
method_id = t_line.method.id
if method_id not in methods:
methods[method_id] = {
'method': t_line.method.name,
'analysis': [],
}
methods[method_id]['analysis'].append(record['analysis'])
if record['pnt'] not in pnt_methods:
pnt_methods[record['pnt']] = {
'pnt': record['pnt'],
'method': t_line.method.name,
}
if not reference_sample or sample.date < reference_sample.date:
with Transaction().set_context(language=lang_code):
reference_sample = Sample(sample.id)
if (not min_start_date or
t_line.start_date < min_start_date):
min_start_date = t_line.start_date
if (not max_end_date or
t_line.end_date > max_end_date):
max_end_date = t_line.end_date
if (not min_confirmation_date or
(t_line.analysis_detail.confirmation_date and
t_line.analysis_detail.confirmation_date <
min_confirmation_date)):
min_confirmation_date = (
t_line.analysis_detail.confirmation_date)
with Transaction().set_context(language=lang_code):
report_context['sample_producer'] = (
reference_sample.producer.rec_name if reference_sample.producer
else gettext('lims.msg_data_not_specified'))
report_context['sample_obj_description'] = (
reference_sample.obj_description.description
if reference_sample.obj_description
else reference_sample.obj_description_manual)
report_context['sample_date'] = reference_sample.date2
report_context['sample_confirmation_date'] = min_confirmation_date
report_context['min_start_date'] = min_start_date
report_context['max_end_date'] = max_end_date
report_context['sample_packages_quantity'] = (
reference_sample.packages_quantity)
report_context['sample_package_type'] = (
reference_sample.package_type.description
if reference_sample.package_type else '')
report_context['sample_package_state'] = (
reference_sample.package_state.description
if reference_sample.package_state else '')
if report.report_type == 'normal':
report_context['sample_label'] = (
reference_sample.label)
report_context['sample_client_description'] = (
reference_sample.sample_client_description)
report_context['sample_number'] = reference_sample.number
if report_context['report_section'] == 'for':
report_context['sample_prodct_type'] = (
reference_sample.product_type.description)
report_context['sample_matrix'] = (
reference_sample.matrix.description)
if tas_project:
report_context['tas_code'] = reference_sample.entry.project.code
if stp_project:
report_context['stp_code'] = reference_sample.entry.project.code
report_context['stp_application_date'] = (
reference_sample.application_date)
report_context['stp_sampling_date'] = (
reference_sample.sampling_date)
report_context['stp_zone'] = (reference_sample.cultivation_zone
if reference_sample.cultivation_zone else '')
report_context['stp_after_application_days'] = (
reference_sample.after_application_days)
report_context['stp_treatment'] = reference_sample.treatment
report_context['stp_dosis'] = reference_sample.dosis
report_context['stp_repetition'] = reference_sample.glp_repetitions
report_context['stp_z_senasa_protocol'] = (
reference_sample.z_senasa_protocol)
report_context['stp_variety'] = (
reference_sample.variety.description if
reference_sample.variety else '')
if stp_polisample_project:
report_context['stp_code'] = reference_sample.entry.project.code
if water_project:
if reference_sample.sampling_datetime:
report_context['water_sampling_date'] = (
cls.format_date(reference_sample.sampling_datetime.date(),
report_context['user'].language))
else:
report_context['water_sampling_date'] = gettext(
'lims.msg_not_done')
report_context['tas_project'] = 'True' if tas_project else 'False'
report_context['stp_project'] = 'True' if stp_project else 'False'
report_context['stp_polisample_project'] = ('True' if
stp_polisample_project else 'False')
report_context['water_project'] = 'True' if water_project else 'False'
if 'VINO' in reference_sample.product_type.code:
alcohol = True
if (report_context['report_section'] in ('amb', 'sq') and
reference_sample.matrix.code in ('SUELO', 'LODO')):
dry_matter = True
sorted_fractions = sorted(list(fractions.values()),
key=lambda x: x['fraction'])
with Transaction().set_context(language=lang_code):
for fraction in sorted_fractions:
for conc, lines in fraction['concentrations'].items():
if report_context['report_section'] == 'rp':
sorted_lines = sorted(lines, key=lambda x: (
x['rp_order'], x['analysis']))
else:
sorted_lines = sorted(lines, key=lambda x: (
x['order'], x['analysis']))
fraction['concentrations'][conc] = {
'label': '',
'unit_label': '',
'lines': sorted_lines,
}
conc_is_numeric = True
try:
numeric_conc = float(conc)
except (TypeError, ValueError):
conc_is_numeric = False
hide_concentration_label = (
report_context['report_section'] in ('amb', 'sq') and
report_context['report_result_type'] in (
'both', 'both_range'))
if conc and conc != '-' and not hide_concentration_label:
if conc == 'Muestra Recibida':
fraction['concentrations'][conc]['label'] = ''
elif conc_is_numeric and numeric_conc < 100:
fraction['concentrations'][conc]['label'] = (
gettext(
'lims.msg_concentration_label_2',
concentration=conc
))
else:
fraction['concentrations'][conc]['label'] = (
gettext(
'lims.msg_concentration_label_3',
concentration=conc
))
show_unit_label = False
for line in sorted_lines:
if line['converted_result']:
show_unit_label = True
break
if show_unit_label:
if dry_matter:
fraction['concentrations'][conc][
'unit_label'] = (
gettext('lims.msg_final_unit_label_4'))
else:
if conc_is_numeric:
if alcohol:
fraction['concentrations'][conc][
'unit_label'] = (
gettext(
'lims.msg_final_unit_label_1',
concentration=conc))
else:
fraction['concentrations'][conc][
'unit_label'] = (
gettext(
'lims.msg_final_unit_label_3',
concentration=conc))
else:
fraction['concentrations'][conc][
'unit_label'] = (
gettext(
'lims.msg_final_unit_label_2',
concentration=conc))
report_context['fractions'] = sorted_fractions
report_context['methods'] = []
for method in methods.values():
concat_lines = ', '.join(list(set(method['analysis'])))
method['analysis'] = concat_lines
report_context['methods'].append(method)
report_context['pnt_methods'] = [m for m in pnt_methods.values()]
report_context['enac'] = 'True' if enac else 'False'
if enac:
with Transaction().set_context(language=lang_code):
if enac_all_acredited:
report_context['enac_label'] = (
gettext('lims.msg_enac_all_acredited'))
else:
report_context['enac_label'] = gettext(
'lims.msg_enac_acredited')
else:
report_context['enac_label'] = ''
report_context['initial_unit'] = initial_unit
report_context['comments'] = ''
for entry_comment in comments.values():
if entry_comment['report_comments']:
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += entry_comment['report_comments']
for sample_comment in entry_comment['samples'].values():
if sample_comment:
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += sample_comment
if report.comments:
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += report.comments
if obs_ql and report_context['report_section']:
with Transaction().set_context(language=lang_code):
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += gettext('lims.msg_obs_ql')
if obs_dl and report_context['report_section']:
with Transaction().set_context(language=lang_code):
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += gettext('lims.msg_obs_dl')
if obs_uncert:
with Transaction().set_context(language=lang_code):
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += gettext('lims.msg_obs_uncert')
if obs_result_range and range_type.resultrange_comments:
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += range_type.resultrange_comments
if obs_rm_c_f:
with Transaction().set_context(language=lang_code):
if report_context['comments']:
report_context['comments'] += '\n'
report_context['comments'] += gettext('lims.msg_obs_rm_c_f')
report_context['annulment_reason'] = ''
if report.number != '1':
with Transaction().set_context(language=lang_code):
prev_report = ResultsReport.search([
('report_version', '=', report.report_version.id),
('number', '=', str(int(report.number) - 1)),
])
if prev_report and prev_report[0].annulment_reason_print:
report_context['annulment_reason'] = (
prev_report[0].annulment_reason)
return report_context
@classmethod
def get_accreditation(cls, product_type, matrix, analysis, method):
pool = Pool()
Typification = pool.get('lims.typification')
typifications = Typification.search([
('product_type', '=', product_type),
('matrix', '=', matrix),
('analysis', '=', analysis),
('method', '=', method),
('valid', '=', True),
])
if typifications:
if typifications[0].technical_scope_versions:
for version in typifications[0].technical_scope_versions:
certification_type = (
version.technical_scope.certification_type)
if certification_type and certification_type.report:
return 'True'
return 'False'
@classmethod
def get_analysis(cls, report_section, notebook_line, language):
pool = Pool()
Analysis = pool.get('lims.analysis')
with Transaction().set_context(language=language):
analysis = Analysis(notebook_line.analysis.id)
res = analysis.description
if report_section == 'mi':
if analysis.gender_species:
res = analysis.gender_species
return res
@classmethod
def get_result(cls, report_section, notebook_line, obs_ql, language):
literal_result = notebook_line.literal_result
result = notebook_line.result
decimals = notebook_line.decimals
result_modifier = notebook_line.result_modifier
with Transaction().set_context(language=language):
res = ''
if report_section in ('amb', 'for', 'rp', 'sq'):
if literal_result:
res = literal_result
else:
if result:
res = round(float(result), decimals)
if decimals == 0:
res = int(res)
else:
res = ''
if result_modifier == 'eq':
res = res
elif result_modifier == 'low':
res = gettext('lims.msg_quantification_limit', loq=res)
obs_ql = True
elif result_modifier == 'd':
res = gettext('lims.msg_d')
elif result_modifier == 'nd':
res = gettext('lims.msg_nd')
elif result_modifier == 'ni':
res = ''
elif result_modifier == 'pos':
res = gettext('lims.msg_pos')
elif result_modifier == 'neg':
res = gettext('lims.msg_neg')
elif result_modifier == 'pre':
res = gettext('lims.msg_pre')
elif result_modifier == 'abs':
res = gettext('lims.msg_abs')
else:
res = result_modifier
elif report_section == 'mi':
if literal_result:
res = literal_result
else:
if result:
res = round(float(result), decimals)
if decimals == 0:
res = int(res)
else:
res = ''
if result_modifier == 'eq':
res = res
elif result_modifier == 'low':
res = '< %s' % res
elif result_modifier == 'd':
res = gettext('lims.msg_d')
elif result_modifier == 'nd':
res = gettext('lims.msg_nd')
elif result_modifier == 'pos':
res = gettext('lims.msg_pos')
elif result_modifier == 'neg':
res = gettext('lims.msg_neg')
elif result_modifier == 'pre':
res = gettext('lims.msg_pre')
elif result_modifier == 'abs':
res = gettext('lims.msg_abs')
return res, obs_ql
@classmethod
def get_converted_result(cls, report_section, report_result_type,
notebook_line, obs_ql, language):
if (report_section in ('for', 'mi', 'rp') or
report_result_type not in ('both', 'both_range')):
return '', obs_ql
literal_result = notebook_line.literal_result
converted_result = notebook_line.converted_result
analysis = notebook_line.analysis.code
decimals = notebook_line.decimals
converted_result_modifier = notebook_line.converted_result_modifier
with Transaction().set_context(language=language):
res = ''
if analysis != '0001' and not literal_result:
if converted_result_modifier == 'neg':
res = gettext('lims.msg_neg')
elif converted_result_modifier == 'pos':
res = gettext('lims.msg_pos')
elif converted_result_modifier == 'pre':
res = gettext('lims.msg_pre')
elif converted_result_modifier == 'abs':
res = gettext('lims.msg_abs')
elif converted_result_modifier == 'd':
res = gettext('lims.msg_d')
elif converted_result_modifier == 'nd':
res = gettext('lims.msg_nd')
else:
if converted_result and converted_result_modifier != 'ni':
res = round(float(converted_result), decimals)
if decimals == 0:
res = int(res)
if converted_result_modifier == 'low':
res = gettext(
'lims.msg_quantification_limit', loq=res)
obs_ql = True
return res, obs_ql
@classmethod
def get_initial_unit(cls, report_section, report_result_type,
notebook_line, obs_dl, obs_uncert, language):
if not notebook_line.initial_unit:
return '', obs_dl, obs_uncert
initial_unit = notebook_line.initial_unit.rec_name
literal_result = notebook_line.literal_result
result_modifier = notebook_line.result_modifier
detection_limit = notebook_line.detection_limit
converted_result = notebook_line.converted_result
uncertainty = notebook_line.uncertainty
decimals = notebook_line.decimals
with Transaction().set_context(language=language):
if report_section == 'rp':
res = ''
if (not literal_result and result_modifier == 'eq' and
uncertainty and float(uncertainty) != 0):
res = round(float(uncertainty), decimals)
if decimals == 0:
res = int(res)
res = gettext(
'lims.msg_uncertainty', res=res, initial_unit='')
obs_uncert = True
else:
res = initial_unit
if (literal_result or initial_unit == '-' or
result_modifier in ('pos', 'neg', 'ni')):
res = ''
else:
if result_modifier in ('nd', 'low'):
if report_section == 'mi':
res = initial_unit
else:
if (not detection_limit or detection_limit in (
'0', '0.0')):
res = initial_unit
else:
res = gettext('lims.msg_detection_limit',
detection_limit=detection_limit,
initial_unit=initial_unit)
obs_dl = True
else:
if (not converted_result and uncertainty and
float(uncertainty) != 0):
res = round(float(uncertainty), decimals)
if decimals == 0:
res = int(res)
res = gettext('lims.msg_uncertainty',
res=res, initial_unit=initial_unit)
obs_uncert = True
return res, obs_dl, obs_uncert
@classmethod
def get_final_unit(cls, report_section, report_result_type,
notebook_line, obs_dl, obs_uncert, language):
if (report_section in ('for', 'mi', 'rp') or
report_result_type not in ('both', 'both_range')):
return '', obs_dl, obs_uncert
if not notebook_line.final_unit:
return '', obs_dl, obs_uncert
final_unit = notebook_line.final_unit.rec_name
analysis = notebook_line.analysis.code
literal_result = notebook_line.literal_result
converted_result_modifier = notebook_line.converted_result_modifier
detection_limit = notebook_line.detection_limit
converted_result = notebook_line.converted_result
uncertainty = notebook_line.uncertainty
decimals = notebook_line.decimals
with Transaction().set_context(language=language):
res = final_unit
if (analysis == '0001' or literal_result or final_unit == '-' or
converted_result_modifier in ('pos', 'neg', 'ni')):
res = ''
else:
if converted_result_modifier in ('nd', 'low'):
if (not detection_limit or detection_limit in (
'0', '0.0')):
res = final_unit
else:
res = gettext('lims.msg_detection_limit',
detection_limit=detection_limit,
initial_unit=final_unit)
obs_dl = True
else:
if not converted_result:
res = ''
else:
if uncertainty and float(uncertainty) != 0:
res = round(float(uncertainty), decimals)
if decimals == 0:
res = int(res)
res = gettext('lims.msg_uncertainty',
res=res, initial_unit=final_unit)
obs_uncert = True
return res, obs_dl, obs_uncert
@classmethod
def get_detection_limit(cls, report_section, report_result_type,
report_type, notebook_line, language):
detection_limit = notebook_line.detection_limit
literal_result = notebook_line.literal_result
result_modifier = notebook_line.result_modifier
if report_section in ('amb', 'sq'):
res = ''
if report_type == 'polisample' and result_modifier == 'nd':
with Transaction().set_context(language=language):
res = gettext('lims.msg_detection_limit_2',
detection_limit=detection_limit)
else:
if (not detection_limit or detection_limit in ('0', '0.0') or
literal_result):
res = '-'
else:
res = detection_limit
return res
@classmethod
def get_reference(cls, range_type, notebook_line, language,
report_section):
pool = Pool()
Range = pool.get('lims.range')
with Transaction().set_context(language=language):
ranges = Range.search([
('range_type', '=', range_type.id),
('analysis', '=', notebook_line.analysis.id),
('product_type', '=', notebook_line.product_type.id),
('matrix', '=', notebook_line.matrix.id),
])
if not ranges:
return ''
range_ = ranges[0]
if range_.reference:
return range_.reference
elif report_section == 'mi':
return ''
res = ''
if range_.min:
with Transaction().set_context(language=language):
resf = float(range_.min)
resd = abs(resf) - abs(int(resf))
if resd > 0:
res1 = str(round(range_.min, 2))
else:
res1 = str(int(range_.min))
res = gettext('lims.msg_caa_min', min=res1)
if range_.max:
if res:
res += ' - '
with Transaction().set_context(language=language):
resf = float(range_.max)
resd = abs(resf) - abs(int(resf))
if resd > 0:
res1 = str(round(range_.max, 2))
else:
res1 = str(int(range_.max))
res += gettext('lims.msg_caa_max', max=res1)
return res
class ResultReportTranscription(ResultReport):
'Transcription Results Report'
__name__ = 'lims.result_report.transcription'
@classmethod
def execute(cls, ids, data):
ResultsReport = Pool().get('lims.results_report.version.detail')
if len(ids) > 1:
raise UserError(gettext('lims.msg_multiple_reports'))
results_report = ResultsReport(ids[0])
if results_report.state == 'annulled':
raise UserError(gettext('lims.msg_annulled_report'))
if data is None:
data = {}
current_data = data.copy()
current_data['alt_lang'] = None
result_orig = super(ResultReport, cls).execute(ids, current_data)
current_data['alt_lang'] = 'en'
result_eng = super(ResultReport, cls).execute(ids, current_data)
save = False
if results_report.english_report:
if results_report.report_cache_odt_eng:
result = (results_report.report_format_odt_eng,
results_report.report_cache_odt_eng) + result_eng[2:]
else:
result = result_eng
if ('english_report' in current_data and
current_data['english_report']):
results_report.report_format_odt_eng, \
results_report.report_cache_odt_eng = result_eng[:2]
save = True
else:
if results_report.report_cache_odt:
result = (results_report.report_format_odt,
results_report.report_cache_odt) + result_orig[2:]
else:
result = result_orig
if ('english_report' in current_data and
not current_data['english_report']):
results_report.report_format_odt, \
results_report.report_cache_odt = result_orig[:2]
save = True
if save:
results_report.save()
return result
class GlobalResultReport(Report):
'Global Results Report'
__name__ = 'lims.global_result_report'
@classmethod
def execute(cls, ids, data):
ResultsReport = Pool().get(
'lims.results_report')
result = super(GlobalResultReport, cls).execute(ids, data)
results_report = ResultsReport(ids[0])
if results_report.english_report:
if results_report.report_cache_eng:
result = (results_report.report_format_eng,
results_report.report_cache_eng) + result[2:]
else:
if results_report.report_cache:
result = (results_report.report_format,
results_report.report_cache) + result[2:]
report_name = '%s %s' % (result[3], results_report.number)
result = result[:3] + (report_name,)
return result