mirror of
https://github.com/Kalenis/kalenislims.git
synced 2023-12-14 07:13:04 +01:00
5749 lines
220 KiB
Python
5749 lines
220 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 collections import defaultdict
|
|
from datetime import datetime
|
|
from dateutil.relativedelta import relativedelta
|
|
from sql import Literal, Join
|
|
|
|
from trytond.model import ModelView, ModelSQL, fields
|
|
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
|
|
StateReport, Button
|
|
from trytond.pool import Pool
|
|
from trytond.pyson import PYSONEncoder, Eval, Bool, Not, Or
|
|
from trytond.transaction import Transaction
|
|
from trytond.report import Report
|
|
from trytond.exceptions import UserError
|
|
from trytond.i18n import gettext
|
|
from .configuration import get_print_date
|
|
from .formula_parser import FormulaParser
|
|
|
|
|
|
class Notebook(ModelSQL, ModelView):
|
|
'Laboratory Notebook'
|
|
__name__ = 'lims.notebook'
|
|
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', required=True,
|
|
readonly=True, ondelete='CASCADE', select=True)
|
|
lines = fields.One2Many('lims.notebook.line', 'notebook', 'Lines')
|
|
product_type = fields.Function(fields.Many2One('lims.product.type',
|
|
'Product type'), 'get_sample_field', searcher='search_sample_field')
|
|
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
|
|
'get_sample_field', searcher='search_sample_field')
|
|
party = fields.Function(fields.Many2One('party.party', 'Party'),
|
|
'get_sample_field', searcher='search_sample_field')
|
|
party_code = fields.Function(fields.Char('Party'), 'get_party_code',
|
|
searcher='search_party_code')
|
|
invoice_party = fields.Function(fields.Many2One('party.party',
|
|
'Invoice Party'),
|
|
'get_sample_field', searcher='search_sample_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
date = fields.Function(fields.DateTime('Date'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
date2 = fields.Function(fields.Date('Date'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
sample_comments = fields.Function(fields.Text('Sample Comments'),
|
|
'get_sample_field')
|
|
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
fraction_comments = fields.Function(fields.Text('Fraction Comments'),
|
|
'get_fraction_field')
|
|
shared = fields.Function(fields.Boolean('Shared'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
current_location = fields.Function(fields.Many2One('stock.location',
|
|
'Current Location'), 'get_current_location',
|
|
searcher='search_current_location')
|
|
divided_report = fields.Function(fields.Boolean('Divided report'),
|
|
'get_divided_report')
|
|
icon = fields.Function(fields.Char("Icon"), 'get_icon')
|
|
obj_description = fields.Function(fields.Char('Objective description',
|
|
translate=True), 'get_obj_description')
|
|
completion_percentage = fields.Function(fields.Numeric('Complete',
|
|
digits=(1, 4)), 'get_sample_field')
|
|
department = fields.Function(fields.Many2One('company.department',
|
|
'Department'), 'get_department', searcher='search_department')
|
|
state = fields.Function(fields.Char('State'), 'get_state',
|
|
searcher='search_state')
|
|
lines_pending_reporting = fields.Function(fields.One2Many(
|
|
'lims.notebook.line', 'notebook', 'Lines'),
|
|
'get_lines_pending_reporting')
|
|
acceptance_pending = fields.Function(fields.Boolean('Pending acceptance'),
|
|
'get_acceptance_pending', searcher='search_acceptance_pending')
|
|
urgent = fields.Function(fields.Boolean('Urgent'), 'get_urgent',
|
|
searcher='search_urgent')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._order.insert(0, ('fraction', 'DESC'))
|
|
|
|
def get_rec_name(self, name):
|
|
if self.fraction:
|
|
return self.fraction.rec_name
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
return [('fraction',) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_sample_field(cls, notebooks, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if cls._fields[name]._type == 'many2one':
|
|
for n in notebooks:
|
|
field = getattr(n.fraction.sample, name, None)
|
|
result[name][n.id] = field.id if field else None
|
|
elif name == 'sample_comments':
|
|
for n in notebooks:
|
|
result[name][n.id] = getattr(
|
|
n.fraction.sample, 'comments', None)
|
|
else:
|
|
for n in notebooks:
|
|
result[name][n.id] = getattr(n.fraction.sample, name, None)
|
|
return result
|
|
|
|
@classmethod
|
|
def search_sample_field(cls, name, clause):
|
|
return [('fraction.sample.' + name,) + tuple(clause[1:])]
|
|
|
|
def _order_sample_field(name):
|
|
def order_field(tables):
|
|
pool = Pool()
|
|
Sample = pool.get('lims.sample')
|
|
Fraction = pool.get('lims.fraction')
|
|
field = Sample._fields[name]
|
|
table, _ = tables[None]
|
|
fraction_tables = tables.get('fraction')
|
|
if fraction_tables is None:
|
|
fraction = Fraction.__table__()
|
|
fraction_tables = {
|
|
None: (fraction, fraction.id == table.fraction),
|
|
}
|
|
tables['fraction'] = fraction_tables
|
|
return field.convert_order(name, fraction_tables, Fraction)
|
|
return staticmethod(order_field)
|
|
order_date = _order_sample_field('date')
|
|
|
|
@classmethod
|
|
def get_obj_description(cls, notebooks, name):
|
|
result = {}
|
|
for n in notebooks:
|
|
field = getattr(n.fraction.sample, 'obj_description', None)
|
|
if field:
|
|
result[n.id] = field.description
|
|
else:
|
|
result[n.id] = getattr(n.fraction.sample,
|
|
'obj_description_manual', None)
|
|
return result
|
|
|
|
@classmethod
|
|
def get_party_code(cls, notebooks, name):
|
|
result = {}
|
|
for n in notebooks:
|
|
result[n.id] = n.party.code
|
|
return result
|
|
|
|
@classmethod
|
|
def search_party_code(cls, name, clause):
|
|
return [('fraction.sample.entry.party.code',) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, notebooks, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'fraction_type':
|
|
for n in notebooks:
|
|
field = getattr(n.fraction, 'type', None)
|
|
result[name][n.id] = field.id if field else None
|
|
elif name == 'fraction_comments':
|
|
for n in notebooks:
|
|
result[name][n.id] = getattr(n.fraction, 'comments', None)
|
|
else:
|
|
for n in notebooks:
|
|
result[name][n.id] = getattr(n.fraction, name, None)
|
|
return result
|
|
|
|
@classmethod
|
|
def search_fraction_field(cls, name, clause):
|
|
if name == 'fraction_type':
|
|
name = 'type'
|
|
return [('fraction.' + name,) + tuple(clause[1:])]
|
|
|
|
def get_divided_report(self, name):
|
|
if not self.fraction or not self.fraction.services:
|
|
return False
|
|
for s in self.fraction.services:
|
|
if s.divide:
|
|
return True
|
|
return False
|
|
|
|
@classmethod
|
|
def get_current_location(cls, notebooks, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Move = Pool().get('stock.move')
|
|
|
|
result = {}
|
|
for n in notebooks:
|
|
cursor.execute('SELECT to_location '
|
|
'FROM "' + Move._table + '" '
|
|
'WHERE fraction = %s '
|
|
'AND state IN (\'assigned\', \'done\') '
|
|
'ORDER BY effective_date DESC, id DESC '
|
|
'LIMIT 1', (n.fraction.id,))
|
|
location = cursor.fetchone()
|
|
result[n.id] = location[0] if location else None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_current_location(cls, name, domain=None):
|
|
|
|
def _search_current_location_eval_domain(line, domain):
|
|
operator_funcs = {
|
|
'=': operator.eq,
|
|
'>=': operator.ge,
|
|
'>': operator.gt,
|
|
'<=': operator.le,
|
|
'<': operator.lt,
|
|
'!=': operator.ne,
|
|
'in': lambda v, l: v in l,
|
|
'not in': lambda v, l: v not in l,
|
|
'ilike': lambda v, l: False,
|
|
}
|
|
field, op, operand = domain
|
|
value = line.get(field)
|
|
return operator_funcs[op](value, operand)
|
|
|
|
if domain and domain[1] == 'ilike':
|
|
Location = Pool().get('stock.location')
|
|
locations = Location.search([
|
|
('code', '=', domain[2]),
|
|
], order=[])
|
|
if not locations:
|
|
locations = Location.search([
|
|
('name',) + tuple(domain[1:]),
|
|
], order=[])
|
|
if not locations:
|
|
return []
|
|
domain = ('current_location', 'in', [l.id for l in locations])
|
|
|
|
all_notebooks = cls.search([])
|
|
current_locations = iter(
|
|
cls.get_current_location(all_notebooks).items())
|
|
|
|
processed_lines = [{
|
|
'fraction': fraction,
|
|
'current_location': location,
|
|
} for fraction, location in current_locations]
|
|
|
|
record_ids = [line['fraction'] for line in processed_lines
|
|
if _search_current_location_eval_domain(line, domain)]
|
|
return [('id', 'in', record_ids)]
|
|
|
|
def get_icon(self, name):
|
|
if self.fraction_comments:
|
|
return 'lims-blue'
|
|
return 'lims-white'
|
|
|
|
@classmethod
|
|
def get_department(cls, notebooks, name):
|
|
result = {}
|
|
for n in notebooks:
|
|
field = getattr(n.fraction.sample.product_type, name, None)
|
|
result[n.id] = field.id if field else None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_department(cls, name, clause):
|
|
return [('fraction.sample.product_type.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_state(cls, notebooks, name=None):
|
|
result = {}
|
|
laboratory_id = Transaction().context.get(
|
|
'samples_pending_reporting_laboratory', None)
|
|
if not laboratory_id:
|
|
for n in notebooks:
|
|
result[n.id] = None
|
|
return result
|
|
for n in notebooks:
|
|
result[n.id] = cls._get_notebook_state(n.id, laboratory_id)
|
|
return result
|
|
|
|
@classmethod
|
|
def _get_notebook_state(cls, notebook_id, laboratory_id):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
ResultsLine = pool.get('lims.results_report.version.detail.line')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
FractionType = pool.get('lims.fraction.type')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
draft_lines_ids = []
|
|
draft_lines = ResultsLine.search([
|
|
('detail_sample.notebook', '=', notebook_id),
|
|
('detail_sample.version_detail.laboratory', '=', laboratory_id),
|
|
('detail_sample.version_detail.state', 'in', ['draft', 'revised']),
|
|
('detail_sample.version_detail.type', '!=', 'preliminary'),
|
|
])
|
|
if draft_lines:
|
|
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
|
|
draft_lines_ids = ', '.join(str(l) for l in [0] + draft_lines_ids)
|
|
|
|
sql_query = ('SELECT COUNT(*) '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE nl.notebook = %s '
|
|
'AND nl.laboratory = %s '
|
|
'AND ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.results_report IS NULL '
|
|
'AND nl.accepted = TRUE '
|
|
'AND nl.id NOT IN (' + draft_lines_ids + ') ')
|
|
|
|
excluded_notebooks = cls._get_excluded_notebooks([notebook_id],
|
|
laboratory_id)
|
|
if excluded_notebooks:
|
|
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_lines = [x[0] for x in cursor.fetchall()]
|
|
excluded_lines_ids = ', '.join(str(l)
|
|
for l in [0] + excluded_lines)
|
|
sql_query += 'AND nl.id NOT IN (' + excluded_lines_ids + ') '
|
|
cursor.execute(sql_query, (notebook_id, laboratory_id))
|
|
if cursor.fetchone()[0] > 0:
|
|
return 'complete'
|
|
|
|
sql_query = ('SELECT COUNT(*) '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE nl.notebook = %s '
|
|
'AND nl.laboratory = %s '
|
|
'AND ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.results_report IS NULL '
|
|
'AND nl.id NOT IN (' + draft_lines_ids + ') ')
|
|
sql_query += cls._get_samples_in_progress_sql_clause()
|
|
|
|
cursor.execute(sql_query, (notebook_id, laboratory_id))
|
|
if cursor.fetchone()[0] > 0:
|
|
return 'in_progress'
|
|
|
|
return None
|
|
|
|
@classmethod
|
|
def search_state(cls, name, domain=None):
|
|
if domain[2] == 'complete':
|
|
notebooks_ids = cls._get_notebooks_complete()
|
|
return [('id', 'in', notebooks_ids)]
|
|
if domain[2] == 'in_progress':
|
|
notebooks_ids = cls._get_notebooks_in_progress()
|
|
return [('id', 'in', notebooks_ids)]
|
|
return [('id', '=', -1)]
|
|
|
|
@classmethod
|
|
def _get_notebooks_complete(cls):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
ResultsLine = pool.get('lims.results_report.version.detail.line')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
FractionType = pool.get('lims.fraction.type')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
laboratory_id = Transaction().context.get(
|
|
'samples_pending_reporting_laboratory', None)
|
|
if not laboratory_id:
|
|
return []
|
|
|
|
draft_lines_ids = []
|
|
draft_lines = ResultsLine.search([
|
|
('detail_sample.version_detail.laboratory', '=', laboratory_id),
|
|
('detail_sample.version_detail.state', 'in', ['draft', 'revised']),
|
|
('detail_sample.version_detail.type', '!=', 'preliminary'),
|
|
])
|
|
if draft_lines:
|
|
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
|
|
draft_lines_ids = ', '.join(str(l) for l in [0] + draft_lines_ids)
|
|
|
|
sql_query = ('SELECT nl.notebook '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE nl.laboratory = %s '
|
|
'AND ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.results_report IS NULL '
|
|
'AND nl.accepted = TRUE '
|
|
'AND nl.id NOT IN (' + draft_lines_ids + ') ')
|
|
cursor.execute(sql_query, (laboratory_id,))
|
|
notebooks_ids = [x[0] for x in cursor.fetchall()]
|
|
|
|
excluded_notebooks = cls._get_excluded_notebooks(notebooks_ids,
|
|
laboratory_id)
|
|
if excluded_notebooks:
|
|
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_lines = [x[0] for x in cursor.fetchall()]
|
|
excluded_lines_ids = ', '.join(str(l)
|
|
for l in [0] + excluded_lines)
|
|
sql_query += 'AND nl.id NOT IN (' + excluded_lines_ids + ') '
|
|
cursor.execute(sql_query, (laboratory_id,))
|
|
notebooks_ids = [x[0] for x in cursor.fetchall()]
|
|
return notebooks_ids
|
|
|
|
@classmethod
|
|
def _get_excluded_notebooks(cls, notebooks_ids, laboratory_id):
|
|
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')
|
|
|
|
notebooks_ids = '\', \''.join(str(n) for n in notebooks_ids + [0])
|
|
|
|
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 nl.laboratory = %s '
|
|
'AND ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND n.id IN (\'' + notebooks_ids + '\')',
|
|
(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 = set()
|
|
for n_id, a_id, grouper in to_check:
|
|
if n_id not in accepted_notebooks:
|
|
continue
|
|
key = (n_id, grouper)
|
|
excluded_notebooks.add(key)
|
|
return excluded_notebooks
|
|
|
|
@classmethod
|
|
def _get_notebooks_in_progress(cls):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
ResultsLine = pool.get('lims.results_report.version.detail.line')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
FractionType = pool.get('lims.fraction.type')
|
|
|
|
laboratory_id = Transaction().context.get(
|
|
'samples_pending_reporting_laboratory', None)
|
|
if not laboratory_id:
|
|
return []
|
|
|
|
draft_lines_ids = []
|
|
draft_lines = ResultsLine.search([
|
|
('detail_sample.version_detail.laboratory', '=', laboratory_id),
|
|
('detail_sample.version_detail.state', 'in', ['draft', 'revised']),
|
|
('detail_sample.version_detail.type', '!=', 'preliminary'),
|
|
])
|
|
if draft_lines:
|
|
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
|
|
draft_lines_ids = ', '.join(str(l) for l in [0] + draft_lines_ids)
|
|
|
|
sql_query = ('SELECT nl.notebook '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE nl.laboratory = %s '
|
|
'AND ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.results_report IS NULL '
|
|
'AND nl.id NOT IN (' + draft_lines_ids + ') ')
|
|
sql_query += cls._get_samples_in_progress_sql_clause()
|
|
|
|
cursor.execute(sql_query, (laboratory_id,))
|
|
notebooks_ids = [x[0] for x in cursor.fetchall()]
|
|
return notebooks_ids
|
|
|
|
@classmethod
|
|
def _get_samples_in_progress_clause(cls):
|
|
Config = Pool().get('lims.configuration')
|
|
samples_in_progress = Config(1).samples_in_progress
|
|
clause = []
|
|
if samples_in_progress == 'accepted':
|
|
clause = [('accepted', '=', True)]
|
|
elif samples_in_progress == 'result':
|
|
clause = [['OR',
|
|
('result', 'not in', [None, '']),
|
|
('literal_result', 'not in', [None, '']),
|
|
('result_modifier', 'in', [
|
|
'd', 'nd', 'pos', 'neg', 'ni', 'abs', 'pre', 'na']),
|
|
]]
|
|
return clause
|
|
|
|
@classmethod
|
|
def _get_samples_in_progress_sql_clause(cls):
|
|
Config = Pool().get('lims.configuration')
|
|
samples_in_progress = Config(1).samples_in_progress
|
|
sql_clause = ''
|
|
if samples_in_progress == 'accepted':
|
|
sql_clause = 'AND nl.accepted = TRUE '
|
|
elif samples_in_progress == 'result':
|
|
sql_clause = ('AND (nl.result IS NOT NULL '
|
|
'OR nl.literal_result IS NOT NULL '
|
|
'OR nl.result_modifier IN '
|
|
'(\'d\', \'nd\', \'pos\', \'neg\', '
|
|
'\'ni\', \'abs\', \'pre\', \'na\')) ')
|
|
return sql_clause
|
|
|
|
@classmethod
|
|
def view_toolbar_get(cls):
|
|
if not Transaction().context.get('samples_pending_reporting', False):
|
|
return super().view_toolbar_get()
|
|
|
|
# Samples Pending Reporting uses specific keywords
|
|
prints = cls.get_samples_pending_reporting_keyword('form_print')
|
|
actions = cls.get_samples_pending_reporting_keyword('form_action')
|
|
relates = cls.get_samples_pending_reporting_keyword('form_relate')
|
|
result = {
|
|
'print': prints,
|
|
'action': actions,
|
|
'relate': relates,
|
|
'exports': [],
|
|
}
|
|
return result
|
|
|
|
@classmethod
|
|
def get_samples_pending_reporting_keyword(cls, keyword):
|
|
"""
|
|
Method copied from ActionKeyword. It search for specific keywords
|
|
for Samples Pending Reporting: lims.notebook,-2
|
|
"""
|
|
pool = Pool()
|
|
ActionKeyword = pool.get('ir.action.keyword')
|
|
Action = pool.get('ir.action')
|
|
|
|
key = (keyword, ('lims.notebook', -2))
|
|
keywords = ActionKeyword._get_keyword_cache.get(key)
|
|
if keywords is not None:
|
|
return keywords
|
|
|
|
clause = [
|
|
('keyword', '=', keyword),
|
|
('model', '=', 'lims.notebook,-2'),
|
|
('action.active', '=', True),
|
|
]
|
|
action_keywords = ActionKeyword.search(clause, order=[])
|
|
types = defaultdict(list)
|
|
for action_keyword in action_keywords:
|
|
type_ = action_keyword.action.type
|
|
types[type_].append(action_keyword.action.id)
|
|
keywords = []
|
|
for type_, action_ids in types.items():
|
|
for value in Action.get_action_values(type_, action_ids):
|
|
value['keyword'] = keyword
|
|
keywords.append(value)
|
|
keywords.sort(key=operator.itemgetter('name'))
|
|
ActionKeyword._get_keyword_cache.set(key, keywords)
|
|
return keywords
|
|
|
|
def get_lines_pending_reporting(self, name=None):
|
|
laboratory_id = Transaction().context.get(
|
|
'samples_pending_reporting_laboratory', None)
|
|
if not laboratory_id:
|
|
return []
|
|
lines = self._get_lines_for_reporting(laboratory_id, 'complete')
|
|
return [l.id for l in lines]
|
|
|
|
def _get_lines_for_reporting(self, laboratory_id, state):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
ResultsLine = pool.get('lims.results_report.version.detail.line')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
draft_lines_ids = []
|
|
draft_lines = ResultsLine.search([
|
|
('detail_sample.notebook', '=', self.id),
|
|
('detail_sample.version_detail.laboratory', '=', laboratory_id),
|
|
('detail_sample.version_detail.state', 'in', ['draft', 'revised']),
|
|
('detail_sample.version_detail.type', '!=', 'preliminary'),
|
|
])
|
|
if draft_lines:
|
|
draft_lines_ids = [dl.notebook_line.id for dl in draft_lines]
|
|
|
|
clause = [
|
|
('notebook', '=', self.id),
|
|
('laboratory', '=', laboratory_id),
|
|
('notebook.fraction.type.report', '=', True),
|
|
('report', '=', True),
|
|
('annulled', '=', False),
|
|
('results_report', '=', None),
|
|
('id', 'not in', draft_lines_ids),
|
|
]
|
|
if state == 'in_progress':
|
|
clause.extend(self._get_samples_in_progress_clause())
|
|
else:
|
|
clause.append(('accepted', '=', True))
|
|
excluded_notebooks = self._get_excluded_notebooks(
|
|
[self.id], laboratory_id)
|
|
if excluded_notebooks:
|
|
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)
|
|
|
|
@classmethod
|
|
def get_acceptance_pending(cls, notebooks, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Fraction = pool.get('lims.fraction')
|
|
FractionType = pool.get('lims.fraction.type')
|
|
|
|
result = {}
|
|
for n in notebooks:
|
|
result[n.id] = False
|
|
cursor.execute('SELECT COUNT(*) '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + cls._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.accepted = FALSE '
|
|
'AND (nl.result IS NOT NULL '
|
|
'OR nl.literal_result IS NOT NULL '
|
|
'OR nl.result_modifier IN '
|
|
'(\'d\', \'nd\', \'pos\', \'neg\', '
|
|
'\'ni\', \'abs\', \'pre\', \'na\')) '
|
|
'AND nl.notebook = %s',
|
|
(n.id,))
|
|
if cursor.fetchone()[0] > 0:
|
|
result[n.id] = True
|
|
return result
|
|
|
|
@classmethod
|
|
def search_acceptance_pending(cls, name, domain=None):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Fraction = pool.get('lims.fraction')
|
|
FractionType = pool.get('lims.fraction.type')
|
|
|
|
cursor.execute('SELECT nl.notebook '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'INNER JOIN "' + cls._table + '" n '
|
|
'ON n.id = nl.notebook '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON f.id = n.fraction '
|
|
'INNER JOIN "' + FractionType._table + '" ft '
|
|
'ON ft.id = f.type '
|
|
'WHERE ft.report = TRUE '
|
|
'AND nl.report = TRUE '
|
|
'AND nl.annulled = FALSE '
|
|
'AND nl.accepted = FALSE '
|
|
'AND (nl.result IS NOT NULL '
|
|
'OR nl.literal_result IS NOT NULL '
|
|
'OR nl.result_modifier IN '
|
|
'(\'d\', \'nd\', \'pos\', \'neg\', '
|
|
'\'ni\', \'abs\', \'pre\', \'na\'))')
|
|
notebooks_ids = [x[0] for x in cursor.fetchall()]
|
|
field, op, operand = domain
|
|
if (op, operand) in (('=', True), ('!=', False)):
|
|
return [('id', 'in', notebooks_ids)]
|
|
elif (op, operand) in (('=', False), ('!=', True)):
|
|
return [('id', 'not in', notebooks_ids)]
|
|
return []
|
|
|
|
@classmethod
|
|
def get_urgent(cls, notebooks, name):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
result = {}
|
|
for n in notebooks:
|
|
lines = NotebookLine.search_count([
|
|
('notebook', '=', n.id),
|
|
('urgent', '=', True),
|
|
])
|
|
result[n.id] = True if lines > 0 else False
|
|
return result
|
|
|
|
@classmethod
|
|
def search_urgent(cls, name, clause):
|
|
field, op, operand = clause
|
|
if (op, operand) in (('=', True), ('!=', False)):
|
|
return [('lines.urgent', '=', True)]
|
|
elif (op, operand) in (('=', False), ('!=', True)):
|
|
urgents = cls.search([('lines.urgent', '=', True)])
|
|
return [('id', 'not in', [u.id for u in urgents])]
|
|
return []
|
|
|
|
|
|
class NotebookLine(ModelSQL, ModelView):
|
|
'Laboratory Notebook Line'
|
|
__name__ = 'lims.notebook.line'
|
|
|
|
_states = {'readonly': Bool(Eval('accepted'))}
|
|
_depends = ['accepted']
|
|
|
|
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
analysis_detail = fields.Many2One('lims.entry.detail.analysis',
|
|
'Analysis detail', select=True)
|
|
service = fields.Many2One('lims.service', 'Service', readonly=True,
|
|
ondelete='CASCADE', select=True)
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', required=True,
|
|
readonly=True, select=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
start_date = fields.Date('Start date', states={'readonly': True},
|
|
select=True)
|
|
end_date = fields.Date('End date', states={
|
|
'readonly': Or(~Bool(Eval('start_date')), Bool(Eval('accepted'))),
|
|
}, depends=['start_date', 'accepted'])
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
|
|
readonly=True, select=True)
|
|
method = fields.Many2One('lims.lab.method', 'Method',
|
|
required=True, domain=['OR', ('id', '=', Eval('method')),
|
|
('id', 'in', Eval('method_domain'))],
|
|
depends=['method_domain'])
|
|
method_view = fields.Function(fields.Many2One('lims.lab.method',
|
|
'Method'), 'get_views_field')
|
|
method_domain = fields.Function(fields.Many2Many('lims.lab.method',
|
|
None, None, 'Method domain'),
|
|
'on_change_with_method_domain')
|
|
device = fields.Many2One('lims.lab.device', 'Device',
|
|
domain=['OR', ('id', '=', Eval('device')),
|
|
('id', 'in', Eval('device_domain'))],
|
|
depends=['device_domain'])
|
|
device_view = fields.Function(fields.Many2One('lims.lab.device',
|
|
'Device'), 'get_views_field')
|
|
device_domain = fields.Function(fields.Many2Many('lims.lab.device',
|
|
None, None, 'Device domain'), 'on_change_with_device_domain')
|
|
analysis_origin = fields.Char('Analysis origin', readonly=True)
|
|
initial_concentration = fields.Char('Initial concentration')
|
|
final_concentration = fields.Char('Final concentration')
|
|
laboratory_professionals = fields.Many2Many(
|
|
'lims.notebook.line-laboratory.professional', 'notebook_line',
|
|
'professional', 'Preparation professionals')
|
|
initial_unit = fields.Many2One('product.uom', 'Initial unit',
|
|
domain=[('category.lims_only_available', '=', True)],
|
|
states=_states, depends=_depends)
|
|
final_unit = fields.Many2One('product.uom', 'Final unit',
|
|
domain=[('category.lims_only_available', '=', True)],
|
|
states=_states, depends=_depends)
|
|
result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('d', 'Detected'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
('abs', 'Absence'),
|
|
('pre', 'Presence'),
|
|
], 'Result modifier', sort=False,
|
|
states=_states, depends=_depends)
|
|
result_modifier_string = result_modifier.translated('result_modifier')
|
|
converted_result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('nd', 'nd'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
], 'Converted result modifier', sort=False,
|
|
states=_states, depends=_depends)
|
|
converted_result_modifier_string = converted_result_modifier.translated(
|
|
'converted_result_modifier')
|
|
result = fields.Char('Result',
|
|
states=_states, depends=_depends)
|
|
converted_result = fields.Char('Converted result',
|
|
states=_states, depends=_depends)
|
|
detection_limit = fields.Char('Detection limit',
|
|
states=_states, depends=_depends)
|
|
quantification_limit = fields.Char('Quantification limit',
|
|
states=_states, depends=_depends)
|
|
lower_limit = fields.Char('Lower limit allowed',
|
|
states=_states, depends=_depends)
|
|
upper_limit = fields.Char('Upper limit allowed',
|
|
states=_states, depends=_depends)
|
|
check_result_limits = fields.Function(fields.Boolean(
|
|
'Validate limits directly on the result'), 'get_typification_field')
|
|
chromatogram = fields.Char('Chromatogram')
|
|
professionals = fields.One2Many('lims.notebook.line.professional',
|
|
'notebook_line', 'Analytic professionals')
|
|
comments = fields.Text('Entry comments')
|
|
theoretical_concentration = fields.Char('Theoretical concentration')
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level')
|
|
decimals = fields.Integer('Decimals')
|
|
significant_digits = fields.Integer('Significant digits')
|
|
backup = fields.Char('Backup')
|
|
reference = fields.Char('Reference')
|
|
literal_result = fields.Char('Literal result', translate=True,
|
|
states=_states, depends=_depends)
|
|
rm_correction_formula = fields.Char('RM Correction Formula')
|
|
report = fields.Boolean('Report')
|
|
uncertainty = fields.Char('Uncertainty')
|
|
verification = fields.Char('Verification')
|
|
analysis_order = fields.Function(fields.Integer('Order'),
|
|
'get_analysis_order')
|
|
dilution_factor = fields.Float('Dilution factor')
|
|
accepted = fields.Boolean('Accepted', select=True)
|
|
acceptance_date = fields.DateTime('Acceptance date',
|
|
states={'readonly': True})
|
|
not_accepted_message = fields.Text('Message', readonly=True,
|
|
states={'invisible': Not(Bool(Eval('not_accepted_message')))})
|
|
annulled = fields.Boolean('Annulled', states={'readonly': True},
|
|
select=True)
|
|
annulment_date = fields.DateTime('Annulment date',
|
|
states={'readonly': True})
|
|
results_report = fields.Many2One('lims.results_report', 'Results Report',
|
|
readonly=True, select=True)
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
readonly=True)
|
|
urgent = fields.Boolean('Urgent')
|
|
priority = fields.Function(fields.Integer('Priority'),
|
|
'get_service_field', searcher='search_service_field')
|
|
fraction = fields.Function(fields.Many2One('lims.fraction', 'Fraction'),
|
|
'get_service_field', searcher='search_service_field')
|
|
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
|
|
'Fraction type'), 'get_fraction_field',
|
|
searcher='search_fraction_field')
|
|
sample = fields.Function(fields.Many2One('lims.sample', 'Sample'),
|
|
'get_fraction_field', searcher='search_fraction_field')
|
|
party = fields.Function(fields.Many2One('party.party', 'Party'),
|
|
'get_fraction_field', searcher='search_fraction_field')
|
|
product_type = fields.Function(fields.Many2One('lims.product.type',
|
|
'Product type'), 'get_sample_field', searcher='search_sample_field')
|
|
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
|
|
'get_sample_field', searcher='search_sample_field')
|
|
label = fields.Function(fields.Char('Label'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
date = fields.Function(fields.DateTime('Date'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
date2 = fields.Function(fields.Date('Date'), 'get_sample_field',
|
|
searcher='search_sample_field')
|
|
report_type = fields.Function(fields.Char('Report type'),
|
|
'get_typification_field', searcher='search_typification_field')
|
|
report_result_type = fields.Function(fields.Char('Result type'),
|
|
'get_typification_field', searcher='search_typification_field')
|
|
results_estimated_waiting = fields.Integer(
|
|
'Estimated number of days for results', states={'readonly': True})
|
|
results_estimated_date = fields.Function(fields.Date(
|
|
'Estimated date of result'), 'get_results_estimated_date')
|
|
laboratory_date = fields.Function(fields.Date('Laboratory deadline'),
|
|
'get_service_field', searcher='search_service_field')
|
|
report_date = fields.Function(fields.Date('Date agreed for result'),
|
|
'get_service_field', searcher='search_service_field')
|
|
department = fields.Many2One('company.department', 'Department',
|
|
readonly=True)
|
|
icon = fields.Function(fields.Char("Icon"), 'get_icon')
|
|
planning_comments = fields.Function(fields.Text('Planification comments'),
|
|
'get_planning_comments')
|
|
controls = fields.Many2Many('lims.notebook.line-fraction',
|
|
'notebook_line', 'fraction', 'Controls')
|
|
referral = fields.Function(fields.Many2One('lims.referral', 'Referral'),
|
|
'get_detail_field', searcher='search_detail_field')
|
|
|
|
del _states, _depends
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
table_h = cls.__table_handler__(module_name)
|
|
urgent_exist = table_h.column_exist('urgent')
|
|
super().__register__(module_name)
|
|
if not urgent_exist:
|
|
cursor = Transaction().connection.cursor()
|
|
Service = Pool().get('lims.service')
|
|
cursor.execute('UPDATE "' + cls._table + '" nl '
|
|
'SET urgent = srv.urgent FROM '
|
|
'"' + Service._table + '" srv '
|
|
'WHERE srv.id = nl.service')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._order.insert(0, ('analysis_order', 'ASC'))
|
|
cls._order.insert(1, ('repetition', 'ASC'))
|
|
|
|
@staticmethod
|
|
def default_repetition():
|
|
return 0
|
|
|
|
@staticmethod
|
|
def default_result_modifier():
|
|
return 'eq'
|
|
|
|
@staticmethod
|
|
def default_converted_result_modifier():
|
|
return 'eq'
|
|
|
|
@staticmethod
|
|
def default_decimals():
|
|
return 2
|
|
|
|
@staticmethod
|
|
def default_report():
|
|
return True
|
|
|
|
@staticmethod
|
|
def default_dilution_factor():
|
|
return 1.0
|
|
|
|
@staticmethod
|
|
def default_accepted():
|
|
return False
|
|
|
|
@staticmethod
|
|
def default_annulled():
|
|
return False
|
|
|
|
@classmethod
|
|
def create(cls, vlist):
|
|
lines = super().create(vlist)
|
|
cls.update_detail_report(lines)
|
|
return lines
|
|
|
|
@classmethod
|
|
def write(cls, *args):
|
|
Sample = Pool().get('lims.sample')
|
|
super().write(*args)
|
|
actions = iter(args)
|
|
for lines, vals in zip(actions, actions):
|
|
if vals.get('not_accepted_message'):
|
|
cls.write(lines, {'not_accepted_message': None})
|
|
if 'accepted' in vals:
|
|
cls.update_detail_analysis(lines, vals['accepted'])
|
|
if 'report' in vals:
|
|
cls.update_detail_report(lines)
|
|
change_dates = False
|
|
for field in ('start_date', 'end_date', 'acceptance_date'):
|
|
if field in vals:
|
|
change_dates = True
|
|
break
|
|
if change_dates:
|
|
sample_ids = list(set(nl.sample.id for nl in lines))
|
|
Sample.update_samples_state(sample_ids)
|
|
|
|
@staticmethod
|
|
def update_detail_analysis(lines, accepted):
|
|
EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
|
|
|
|
details = [nl.analysis_detail.id for nl in lines]
|
|
if accepted:
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', details),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'done',
|
|
})
|
|
else:
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', details),
|
|
('analysis.behavior', '!=', 'internal_relation'),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'planned',
|
|
})
|
|
analysis_details = EntryDetailAnalysis.search([
|
|
('id', 'in', details),
|
|
('analysis.behavior', '=', 'internal_relation'),
|
|
])
|
|
if analysis_details:
|
|
EntryDetailAnalysis.write(analysis_details, {
|
|
'state': 'unplanned',
|
|
})
|
|
|
|
@staticmethod
|
|
def update_detail_report(lines):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
to_save = []
|
|
details_ids = list(set(nl.analysis_detail.id for nl in lines))
|
|
with Transaction().set_context(_check_access=False):
|
|
analysis_details = EntryDetailAnalysis.browse(details_ids)
|
|
for d in analysis_details:
|
|
cursor.execute('SELECT report '
|
|
'FROM "' + NotebookLine._table + '" '
|
|
'WHERE analysis_detail = %s '
|
|
'ORDER BY id DESC LIMIT 1',
|
|
(d.id,))
|
|
value = cursor.fetchone()
|
|
d.report = value[0] if value else False
|
|
to_save.append(d)
|
|
EntryDetailAnalysis.save(to_save)
|
|
|
|
@classmethod
|
|
def validate(cls, notebook_lines):
|
|
super().validate(notebook_lines)
|
|
for line in notebook_lines:
|
|
line.check_end_date()
|
|
line.check_accepted()
|
|
|
|
def check_end_date(self):
|
|
if self.end_date:
|
|
if not self.start_date or self.end_date < self.start_date:
|
|
raise UserError(gettext('lims.msg_line_end_date',
|
|
analysis=self.analysis.rec_name))
|
|
if not self.start_date or self.end_date > datetime.now().date():
|
|
raise UserError(gettext('lims.msg_end_date_wrong',
|
|
analysis=self.analysis.rec_name))
|
|
|
|
def check_accepted(self):
|
|
if self.accepted:
|
|
accepted_lines = self.search([
|
|
('notebook', '=', self.notebook.id),
|
|
('analysis', '=', self.analysis.id),
|
|
('accepted', '=', True),
|
|
('id', '!=', self.id),
|
|
])
|
|
if accepted_lines:
|
|
raise UserError(gettext('lims.msg_accepted',
|
|
analysis=self.analysis.rec_name))
|
|
|
|
@classmethod
|
|
def get_analysis_order(cls, notebook_lines, name):
|
|
result = {}
|
|
for nl in notebook_lines:
|
|
analysis = getattr(nl, 'analysis', None)
|
|
result[nl.id] = analysis.order if analysis else None
|
|
return result
|
|
|
|
@staticmethod
|
|
def order_analysis_order(tables):
|
|
Analysis = Pool().get('lims.analysis')
|
|
field = Analysis._fields['order']
|
|
table, _ = tables[None]
|
|
analysis_tables = tables.get('analysis')
|
|
if analysis_tables is None:
|
|
analysis = Analysis.__table__()
|
|
analysis_tables = {
|
|
None: (analysis, analysis.id == table.analysis),
|
|
}
|
|
tables['analysis'] = analysis_tables
|
|
return field.convert_order('order', analysis_tables, Analysis)
|
|
|
|
@classmethod
|
|
def get_views_field(cls, notebook_lines, names):
|
|
result = {}
|
|
for name in names:
|
|
field_name = name[:-5]
|
|
result[name] = {}
|
|
for nl in notebook_lines:
|
|
field = getattr(nl, field_name, None)
|
|
result[name][nl.id] = field.id if field else None
|
|
return result
|
|
|
|
@classmethod
|
|
def get_detail_field(cls, notebook_lines, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if cls._fields[name]._type == 'many2one':
|
|
for nl in notebook_lines:
|
|
field = getattr(nl.analysis_detail, name, None)
|
|
result[name][nl.id] = field.id if field else None
|
|
else:
|
|
for nl in notebook_lines:
|
|
result[name][nl.id] = getattr(nl.analysis_detail,
|
|
name, None)
|
|
return result
|
|
|
|
@classmethod
|
|
def search_detail_field(cls, name, clause):
|
|
return [('analysis_detail.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_service_field(cls, notebook_lines, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'fraction':
|
|
for nl in notebook_lines:
|
|
field = getattr(nl.service, name, None)
|
|
result[name][nl.id] = field.id if field else None
|
|
else:
|
|
for nl in notebook_lines:
|
|
result[name][nl.id] = getattr(nl.service, name, None)
|
|
return result
|
|
|
|
@classmethod
|
|
def search_service_field(cls, name, clause):
|
|
return [('service.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_fraction_field(cls, notebook_lines, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name == 'fraction_type':
|
|
for nl in notebook_lines:
|
|
fraction = getattr(nl.service, 'fraction', None)
|
|
if fraction:
|
|
field = getattr(fraction, 'type', None)
|
|
result[name][nl.id] = field.id if field else None
|
|
else:
|
|
result[name][nl.id] = None
|
|
else:
|
|
for nl in notebook_lines:
|
|
fraction = getattr(nl.service, 'fraction', None)
|
|
if fraction:
|
|
field = getattr(fraction, name, None)
|
|
result[name][nl.id] = field.id if field else None
|
|
else:
|
|
result[name][nl.id] = None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_fraction_field(cls, name, clause):
|
|
if name == 'fraction_type':
|
|
name = 'type'
|
|
return [('service.fraction.' + name,) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def get_sample_field(cls, notebook_lines, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
for nl in notebook_lines:
|
|
result[name][nl.id] = None
|
|
fraction = getattr(nl.service, 'fraction', None)
|
|
if fraction:
|
|
sample = getattr(fraction, 'sample', None)
|
|
if sample:
|
|
field = getattr(fraction, name, None)
|
|
if name in ('label', 'date', 'date2'):
|
|
result[name][nl.id] = field
|
|
else:
|
|
result[name][nl.id] = field.id if field else None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_sample_field(cls, name, clause):
|
|
return [('service.fraction.sample.' + name,) + tuple(clause[1:])]
|
|
|
|
def get_rec_name(self, name):
|
|
rec_name = '%s / %s' % (self.notebook.rec_name, self.analysis.rec_name)
|
|
if self.repetition != 0:
|
|
rec_name += ' (%s)' % self.repetition
|
|
return rec_name
|
|
|
|
@classmethod
|
|
def search_rec_name(cls, name, clause):
|
|
return [('analysis',) + tuple(clause[1:])]
|
|
|
|
@classmethod
|
|
def fields_view_get(cls, view_id=None, view_type='form'):
|
|
pool = Pool()
|
|
User = pool.get('res.user')
|
|
Config = pool.get('lims.configuration')
|
|
UiView = pool.get('ir.ui.view')
|
|
|
|
result = super().fields_view_get(view_id=view_id,
|
|
view_type=view_type)
|
|
|
|
# All Notebook Lines view
|
|
if view_id and UiView(view_id).name == 'notebook_line_all_list':
|
|
return result
|
|
|
|
notebook_view = User(Transaction().user).notebook_view
|
|
if not notebook_view:
|
|
notebook_view = Config(1).default_notebook_view
|
|
if not notebook_view:
|
|
return result
|
|
|
|
if view_type == 'tree':
|
|
xml = '<?xml version="1.0"?>\n' \
|
|
'<tree editable="1">\n'
|
|
fields = set()
|
|
for column in notebook_view.columns:
|
|
fields.add(column.field.name)
|
|
attrs = []
|
|
if column.field.name == 'analysis':
|
|
attrs.append('icon="icon"')
|
|
if column.field.name in ('acceptance_date', 'annulment_date'):
|
|
attrs.append('widget="date"')
|
|
xml += ('<field name="%s" %s/>\n'
|
|
% (column.field.name, ' '.join(attrs)))
|
|
for depend in getattr(cls, column.field.name).depends:
|
|
fields.add(depend)
|
|
for field in ('report_date', 'result', 'converted_result',
|
|
'result_modifier', 'converted_result_modifier',
|
|
'literal_result', 'backup', 'verification', 'uncertainty',
|
|
'accepted', 'acceptance_date', 'end_date', 'report',
|
|
'annulled', 'annulment_date', 'icon'):
|
|
fields.add(field)
|
|
xml += '</tree>'
|
|
result['arch'] = xml
|
|
result['fields'] = cls.fields_get(fields_names=list(fields))
|
|
return result
|
|
|
|
@fields.depends('result', 'converted_result', 'converted_result_modifier',
|
|
'backup', 'verification', 'uncertainty', 'end_date', 'start_date',
|
|
'analysis')
|
|
def on_change_result(self):
|
|
self.converted_result = None
|
|
self.converted_result_modifier = 'eq'
|
|
self.backup = None
|
|
self.verification = None
|
|
self.uncertainty = None
|
|
self.end_date = None
|
|
if self.analysis and self.analysis.behavior == 'internal_relation':
|
|
self.start_date = None
|
|
|
|
@fields.depends('accepted', 'report', 'annulled', 'result',
|
|
'converted_result', 'literal_result', 'result_modifier',
|
|
'converted_result_modifier', 'end_date', 'acceptance_date')
|
|
def on_change_accepted(self):
|
|
self.not_accepted_message = ''
|
|
if self.accepted:
|
|
if not self.report:
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext('lims.msg_not_accepted_1')
|
|
elif self.annulled:
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext('lims.msg_not_accepted_2')
|
|
elif not self.end_date:
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext('lims.msg_not_accepted_3')
|
|
elif not (self.result or self.converted_result or
|
|
self.literal_result or
|
|
self.result_modifier in
|
|
('d', 'nd', 'pos', 'neg', 'ni', 'abs', 'pre') or
|
|
self.converted_result_modifier in
|
|
('d', 'nd', 'pos', 'neg', 'ni', 'abs', 'pre')):
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext('lims.msg_not_accepted_4')
|
|
else:
|
|
if (self.converted_result and self.converted_result_modifier
|
|
not in ('ni', 'eq', 'low')):
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext(
|
|
'lims.msg_not_accepted_5')
|
|
elif (self.result and self.result_modifier
|
|
not in ('ni', 'eq', 'low')):
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext(
|
|
'lims.msg_not_accepted_6')
|
|
elif (self.result_modifier == 'ni' and
|
|
not self.literal_result and
|
|
(not self.converted_result_modifier or
|
|
not self.converted_result) and
|
|
self.converted_result_modifier != 'nd'):
|
|
self.accepted = False
|
|
self.not_accepted_message = gettext(
|
|
'lims.msg_not_accepted_7')
|
|
else:
|
|
self.acceptance_date = datetime.now()
|
|
else:
|
|
ResultsLine = Pool().get('lims.results_report.version.detail.line')
|
|
report_lines = ResultsLine.search([
|
|
('notebook_line', '=', self.id),
|
|
('detail_sample.version_detail.state', '!=', 'annulled'),
|
|
])
|
|
if report_lines:
|
|
self.accepted = True
|
|
report_detail = report_lines[0].detail_sample.version_detail
|
|
self.not_accepted_message = gettext('lims.msg_accepted_1',
|
|
report=report_detail.report_version.results_report.number)
|
|
else:
|
|
self.acceptance_date = None
|
|
|
|
@fields.depends('result_modifier', 'annulled', 'annulment_date', 'report')
|
|
def on_change_result_modifier(self):
|
|
if self.result_modifier == 'na' and not self.annulled:
|
|
self.annulled = True
|
|
self.annulment_date = datetime.now()
|
|
self.report = False
|
|
elif self.result_modifier != 'na' and self.annulled:
|
|
self.annulled = False
|
|
self.annulment_date = None
|
|
self.report = True
|
|
|
|
@classmethod
|
|
def get_typification_field(cls, notebook_lines, names):
|
|
Typification = Pool().get('lims.typification')
|
|
result = dict((name, {}) for name in names)
|
|
for nl in notebook_lines:
|
|
typifications = Typification.search([
|
|
('product_type', '=', nl.notebook.product_type.id),
|
|
('matrix', '=', nl.notebook.matrix.id),
|
|
('analysis', '=', nl.analysis.id),
|
|
('method', '=', nl.method.id),
|
|
('valid', '=', True),
|
|
])
|
|
typification = (typifications[0] if len(typifications) == 1
|
|
else None)
|
|
for name in names:
|
|
if typification:
|
|
result[name][nl.id] = getattr(typification, name, None)
|
|
else:
|
|
if name == 'report_type':
|
|
result[name][nl.id] = 'normal'
|
|
elif name == 'report_result_type':
|
|
result[name][nl.id] = 'result'
|
|
elif name == 'check_result_limits':
|
|
result[name][nl.id] = False
|
|
else:
|
|
result[name][nl.id] = None
|
|
return result
|
|
|
|
@classmethod
|
|
def search_typification_field(cls, name, clause):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Notebook = pool.get('lims.notebook')
|
|
Fraction = pool.get('lims.fraction')
|
|
Sample = pool.get('lims.sample')
|
|
Typification = pool.get('lims.typification')
|
|
|
|
operator_ = clause[1:2][0]
|
|
cursor.execute('SELECT nl.id '
|
|
'FROM "' + cls._table + '" nl '
|
|
'INNER JOIN "' + Notebook._table + '" n '
|
|
'ON nl.notebook = n.id '
|
|
'INNER JOIN "' + Fraction._table + '" f '
|
|
'ON n.fraction = f.id '
|
|
'INNER JOIN "' + Sample._table + '" s '
|
|
'ON f.sample = s.id '
|
|
'INNER JOIN "' + Typification._table + '" t '
|
|
'ON (nl.analysis = t.analysis AND nl.method = t.method '
|
|
'AND s.product_type = t.product_type AND t.matrix = t.matrix) '
|
|
'WHERE t.valid = TRUE '
|
|
'AND t.' + name + ' ' + operator_ + ' %s',
|
|
clause[2:3])
|
|
return [('id', 'in', [x[0] for x in cursor.fetchall()])]
|
|
|
|
@fields.depends('method', 'party',
|
|
'_parent_method.results_estimated_waiting')
|
|
def on_change_with_results_estimated_waiting(self, name=None):
|
|
LabMethodWaitingTime = Pool().get('lims.lab.method.results_waiting')
|
|
if self.method:
|
|
waiting_times = LabMethodWaitingTime.search([
|
|
('method', '=', self.method.id),
|
|
('party', '=', self.party.id),
|
|
])
|
|
if waiting_times:
|
|
return waiting_times[0].results_estimated_waiting
|
|
return self.method.results_estimated_waiting
|
|
return None
|
|
|
|
@classmethod
|
|
def get_results_estimated_date(cls, notebook_lines, name):
|
|
result = {}
|
|
for nl in notebook_lines:
|
|
result[nl.id] = None
|
|
detail = getattr(nl, 'analysis_detail', None)
|
|
if not detail:
|
|
continue
|
|
confirmation_date = getattr(detail, 'confirmation_date', None)
|
|
if not confirmation_date:
|
|
continue
|
|
estimated_waiting = getattr(nl, 'results_estimated_waiting', None)
|
|
if not estimated_waiting:
|
|
continue
|
|
result[nl.id] = cls._get_results_estimated_date(confirmation_date,
|
|
estimated_waiting)
|
|
return result
|
|
|
|
@staticmethod
|
|
def _get_results_estimated_date(confirmation_date, estimated_waiting):
|
|
date = (confirmation_date +
|
|
relativedelta(days=estimated_waiting))
|
|
return date
|
|
|
|
@fields.depends('analysis', '_parent_analysis.methods')
|
|
def on_change_with_method_domain(self, name=None):
|
|
methods = []
|
|
if self.analysis and self.analysis.methods:
|
|
methods = [m.id for m in self.analysis.methods]
|
|
return methods
|
|
|
|
@fields.depends('analysis', 'laboratory')
|
|
def on_change_with_device_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
AnalysisDevice = Pool().get('lims.analysis.device')
|
|
|
|
if not self.analysis or not self.laboratory:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(device) '
|
|
'FROM "' + AnalysisDevice._table + '" '
|
|
'WHERE active IS TRUE '
|
|
'AND analysis = %s '
|
|
'AND laboratory = %s',
|
|
(self.analysis.id, self.laboratory.id))
|
|
res = cursor.fetchall()
|
|
if not res:
|
|
return []
|
|
return [x[0] for x in res]
|
|
|
|
def get_icon(self, name):
|
|
if self.report_date:
|
|
return 'lims-red'
|
|
return 'lims-white'
|
|
|
|
def get_planning_comments(self, name=None):
|
|
if self.planification:
|
|
return self.planification.comments
|
|
return ''
|
|
|
|
def get_formated_result(self):
|
|
res = ''
|
|
result_modifier = self.result_modifier
|
|
if self.literal_result:
|
|
res = self.literal_result
|
|
else:
|
|
res = self._format_result(self.result,
|
|
self.decimals, self.significant_digits)
|
|
if result_modifier == 'eq':
|
|
res = res
|
|
elif result_modifier == 'low':
|
|
res = gettext('lims.msg_quantification_limit', loq=res)
|
|
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
|
|
return res
|
|
|
|
def get_formated_converted_result(self):
|
|
res = ''
|
|
result_modifier = self.converted_result_modifier
|
|
if not self.literal_result:
|
|
res = self._format_result(self.converted_result,
|
|
self.decimals, self.significant_digits)
|
|
if result_modifier == 'eq':
|
|
res = res
|
|
elif result_modifier == 'low':
|
|
res = gettext('lims.msg_quantification_limit', loq=res)
|
|
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
|
|
return res
|
|
|
|
def _format_result(self, result, decimals, significant_digits=None):
|
|
res = ''
|
|
if not result:
|
|
return res
|
|
try:
|
|
if significant_digits:
|
|
res = ("{0:.%ie}" % (significant_digits - 1)).format(
|
|
float(result))
|
|
else:
|
|
res = round(float(result), decimals)
|
|
if decimals == 0:
|
|
res = int(res)
|
|
res = str(res)
|
|
except (TypeError, ValueError):
|
|
pass
|
|
return res
|
|
|
|
|
|
class NotebookLineAllFields(ModelSQL, ModelView):
|
|
'Laboratory Notebook Line'
|
|
__name__ = 'lims.notebook.line.all_fields'
|
|
|
|
line = fields.Many2One('lims.notebook.line', 'Notebook Line')
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
|
|
fraction_type = fields.Many2One('lims.fraction.type', 'Fraction type',
|
|
readonly=True)
|
|
party = fields.Many2One('party.party', 'Party', readonly=True)
|
|
party_code = fields.Char('Party', readonly=True)
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
readonly=True)
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', readonly=True)
|
|
label = fields.Char('Label', readonly=True)
|
|
date = fields.DateTime('Date', readonly=True)
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
start_date = fields.Date('Start date', readonly=True)
|
|
end_date = fields.Date('End date', readonly=True)
|
|
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
|
|
readonly=True)
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
device = fields.Many2One('lims.lab.device', 'Device', readonly=True)
|
|
service = fields.Many2One('lims.service', 'Service', readonly=True)
|
|
analysis_origin = fields.Char('Analysis origin', readonly=True)
|
|
urgent = fields.Boolean('Urgent', readonly=True)
|
|
priority = fields.Integer('Priority', readonly=True)
|
|
initial_concentration = fields.Char('Initial concentration', readonly=True)
|
|
final_concentration = fields.Char('Final concentration', readonly=True)
|
|
laboratory_professionals = fields.Function(fields.Many2Many(
|
|
'lims.laboratory.professional', None, None,
|
|
'Preparation professionals'), 'get_line_field',
|
|
searcher='search_line_field')
|
|
initial_unit = fields.Many2One('product.uom', 'Initial unit',
|
|
readonly=True)
|
|
final_unit = fields.Many2One('product.uom', 'Final unit', readonly=True)
|
|
result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('d', 'Detected'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
('abs', 'Absence'),
|
|
('pre', 'Presence'),
|
|
], 'Result modifier', readonly=True)
|
|
converted_result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('nd', 'nd'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
], 'Converted result modifier', readonly=True)
|
|
result_modifier_string = result_modifier.translated('result_modifier')
|
|
converted_result_modifier_string = converted_result_modifier.translated(
|
|
'converted_result_modifier')
|
|
result = fields.Char('Result', readonly=True)
|
|
converted_result = fields.Char('Converted result', readonly=True)
|
|
detection_limit = fields.Char('Detection limit', readonly=True)
|
|
quantification_limit = fields.Char('Quantification limit', readonly=True)
|
|
lower_limit = fields.Char('Lower limit allowed', readonly=True)
|
|
upper_limit = fields.Char('Upper limit allowed', readonly=True)
|
|
chromatogram = fields.Char('Chromatogram', readonly=True)
|
|
professionals = fields.Function(fields.One2Many(
|
|
'lims.notebook.line.professional', None,
|
|
'Analytic professionals'), 'get_line_field',
|
|
searcher='search_line_field')
|
|
comments = fields.Text('Entry comments', readonly=True)
|
|
theoretical_concentration = fields.Char('Theoretical concentration',
|
|
readonly=True)
|
|
concentration_level = fields.Many2One('lims.concentration.level',
|
|
'Concentration level', readonly=True)
|
|
decimals = fields.Integer('Decimals', readonly=True)
|
|
significant_digits = fields.Integer('Significant digits', readonly=True)
|
|
backup = fields.Char('Backup', readonly=True)
|
|
reference = fields.Char('Reference', readonly=True)
|
|
literal_result = fields.Char('Literal result', readonly=True)
|
|
rm_correction_formula = fields.Char('RM Correction Formula', readonly=True)
|
|
report = fields.Boolean('Report', readonly=True)
|
|
uncertainty = fields.Char('Uncertainty', readonly=True)
|
|
verification = fields.Char('Verification', readonly=True)
|
|
dilution_factor = fields.Float('Dilution factor', readonly=True)
|
|
accepted = fields.Boolean('Accepted', readonly=True)
|
|
acceptance_date = fields.DateTime('Acceptance date', readonly=True)
|
|
annulled = fields.Boolean('Annulled', readonly=True)
|
|
annulment_date = fields.DateTime('Annulment date', readonly=True)
|
|
results_report = fields.Many2One('lims.results_report', 'Results Report',
|
|
readonly=True)
|
|
planification = fields.Many2One('lims.planification', 'Planification',
|
|
readonly=True)
|
|
confirmation_date = fields.Date('Confirmation date', readonly=True)
|
|
results_estimated_waiting = fields.Integer(
|
|
'Estimated number of days for results', readonly=True)
|
|
results_estimated_date = fields.Function(fields.Date(
|
|
'Estimated date of result'), 'get_line_field')
|
|
laboratory_date = fields.Date('Laboratory deadline', readonly=True)
|
|
report_date = fields.Date('Date agreed for result', readonly=True)
|
|
department = fields.Many2One('company.department', 'Department',
|
|
readonly=True)
|
|
referral = fields.Function(fields.Many2One('lims.referral', 'Referral'),
|
|
'get_line_field', searcher='search_line_field')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._order.insert(0, ('fraction', 'DESC'))
|
|
cls._order.insert(1, ('analysis', 'ASC'))
|
|
cls._order.insert(2, ('repetition', 'ASC'))
|
|
|
|
@staticmethod
|
|
def table_query():
|
|
pool = Pool()
|
|
line = pool.get('lims.notebook.line').__table__()
|
|
detail = pool.get('lims.entry.detail.analysis').__table__()
|
|
service = pool.get('lims.service').__table__()
|
|
fraction = pool.get('lims.fraction').__table__()
|
|
sample = pool.get('lims.sample').__table__()
|
|
entry = pool.get('lims.entry').__table__()
|
|
party = pool.get('party.party').__table__()
|
|
|
|
join1 = Join(line, service)
|
|
join1.condition = join1.right.id == line.service
|
|
join2 = Join(join1, fraction)
|
|
join2.condition = join2.right.id == join1.right.fraction
|
|
join3 = Join(join2, sample)
|
|
join3.condition = join3.right.id == join2.right.sample
|
|
join4 = Join(join3, entry)
|
|
join4.condition = join4.right.id == join3.right.entry
|
|
join5 = Join(join4, party)
|
|
join5.condition = join5.right.id == join4.right.party
|
|
join6 = Join(join5, detail)
|
|
join6.condition = join6.right.id == join1.left.analysis_detail
|
|
|
|
columns = [
|
|
line.id,
|
|
line.create_uid,
|
|
line.create_date,
|
|
line.write_uid,
|
|
line.write_date,
|
|
line.id.as_('line'),
|
|
service.fraction,
|
|
entry.party,
|
|
party.code.as_('party_code'),
|
|
sample.product_type,
|
|
sample.matrix,
|
|
sample.label,
|
|
fraction.type.as_('fraction_type'),
|
|
sample.date,
|
|
line.analysis,
|
|
line.repetition,
|
|
line.start_date,
|
|
line.end_date,
|
|
line.laboratory,
|
|
line.method,
|
|
line.device,
|
|
line.service,
|
|
line.analysis_origin,
|
|
line.urgent,
|
|
service.priority,
|
|
line.initial_concentration,
|
|
line.final_concentration,
|
|
line.initial_unit,
|
|
line.final_unit,
|
|
line.result_modifier,
|
|
line.converted_result_modifier,
|
|
line.result,
|
|
line.converted_result,
|
|
line.detection_limit,
|
|
line.quantification_limit,
|
|
line.lower_limit,
|
|
line.upper_limit,
|
|
line.dilution_factor,
|
|
line.chromatogram,
|
|
line.comments,
|
|
line.theoretical_concentration,
|
|
line.concentration_level,
|
|
line.decimals,
|
|
line.significant_digits,
|
|
line.backup,
|
|
line.reference,
|
|
line.literal_result,
|
|
line.rm_correction_formula,
|
|
line.report,
|
|
line.uncertainty,
|
|
line.verification,
|
|
line.accepted,
|
|
line.acceptance_date,
|
|
line.annulled,
|
|
line.annulment_date,
|
|
line.results_report,
|
|
line.planification,
|
|
detail.confirmation_date,
|
|
line.results_estimated_waiting,
|
|
service.laboratory_date,
|
|
service.report_date,
|
|
line.department,
|
|
]
|
|
where = Literal(True)
|
|
return join6.select(*columns, where=where)
|
|
|
|
@classmethod
|
|
def get_line_field(cls, notebook_lines, names):
|
|
result = dict((name, {}) for name in names)
|
|
for nl in notebook_lines:
|
|
for name in names:
|
|
field = getattr(nl.line, name, None)
|
|
if isinstance(field, ModelSQL):
|
|
result[name][nl.id] = field.id if field else None
|
|
elif isinstance(field, tuple):
|
|
result[name][nl.id] = [f.id for f in field]
|
|
else:
|
|
result[name][nl.id] = field
|
|
return result
|
|
|
|
@classmethod
|
|
def search_line_field(cls, name, clause):
|
|
return [('line.' + name,) + tuple(clause[1:])]
|
|
|
|
|
|
class NotebookLineLaboratoryProfessional(ModelSQL):
|
|
'Laboratory Notebook Line - Laboratory Professional'
|
|
__name__ = 'lims.notebook.line-laboratory.professional'
|
|
|
|
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', ondelete='CASCADE', select=True,
|
|
required=True)
|
|
|
|
|
|
class NotebookLineProfessional(ModelSQL, ModelView):
|
|
'Laboratory Notebook Line Professional'
|
|
__name__ = 'lims.notebook.line.professional'
|
|
|
|
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', required=True)
|
|
|
|
|
|
class NotebookInitialConcentrationCalcStart(ModelView):
|
|
'Initial Concentration Calculation'
|
|
__name__ = 'lims.notebook.initial_concentration_calc.start'
|
|
|
|
|
|
class NotebookInitialConcentrationCalc(Wizard):
|
|
'Initial Concentration Calculation'
|
|
__name__ = 'lims.notebook.initial_concentration_calc'
|
|
|
|
start_state = 'ok'
|
|
start = StateView('lims.notebook.initial_concentration_calc.start',
|
|
'lims.lims_notebook_initial_concentration_calc_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
self.lines_initial_concentration_calc(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_initial_concentration_calc(self, notebook_lines):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
ic = notebook_line.initial_concentration
|
|
if not ic:
|
|
continue
|
|
if ic[0] == 'A':
|
|
analysis_code = ic[1:]
|
|
result = self._get_analysis_result(analysis_code,
|
|
notebook_line.notebook)
|
|
if result is not None:
|
|
notebook_line.initial_concentration = str(result)
|
|
lines_to_save.append(notebook_line)
|
|
elif ic[0] == 'R':
|
|
analysis_code = ic[1:]
|
|
result = self._get_relation_result(analysis_code,
|
|
notebook_line.notebook)
|
|
if result is not None:
|
|
notebook_line.initial_concentration = str(result)
|
|
lines_to_save.append(notebook_line)
|
|
else:
|
|
continue
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def _get_analysis_result(self, analysis_code, notebook, round_=False):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
return None
|
|
|
|
try:
|
|
res = float(notebook_lines[0].result)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
if not round_:
|
|
return res
|
|
return round(res, notebook_lines[0].decimals)
|
|
|
|
def _get_relation_result(self, analysis_code, notebook, round_=False):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
internal_relations = Analysis.search([
|
|
('code', '=', analysis_code),
|
|
])
|
|
if not internal_relations:
|
|
return None
|
|
formula = internal_relations[0].result_formula
|
|
if not formula:
|
|
return None
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formula = formula.replace(i, '')
|
|
variables = self._get_variables(formula, notebook)
|
|
if not variables:
|
|
return None
|
|
|
|
parser = FormulaParser(formula, variables)
|
|
value = parser.getValue()
|
|
|
|
if int(value) == value:
|
|
res = int(value)
|
|
else:
|
|
epsilon = 0.0000000001
|
|
if int(value + epsilon) != int(value):
|
|
res = int(value + epsilon)
|
|
elif int(value - epsilon) != int(value):
|
|
res = int(value)
|
|
else:
|
|
res = float(value)
|
|
if not round_:
|
|
return res
|
|
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('repetition', '=', 0),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
return None
|
|
return round(res, notebook_lines[0].decimals)
|
|
|
|
def _get_variables(self, formula, notebook):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
variables[var] = None
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var[0] == 'A':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
round_=True)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'D':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'T':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'R':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'Y':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
for var in variables.values():
|
|
if var is None:
|
|
return None
|
|
return variables
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineInitialConcentrationCalc(NotebookInitialConcentrationCalc):
|
|
'Initial Concentration Calculation'
|
|
__name__ = 'lims.notebook_line.initial_concentration_calc'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_initial_concentration_calc(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class NotebookResultsConversionStart(ModelView):
|
|
'Results Conversion'
|
|
__name__ = 'lims.notebook.results_conversion.start'
|
|
|
|
|
|
class NotebookResultsConversion(Wizard):
|
|
'Results Conversion'
|
|
__name__ = 'lims.notebook.results_conversion'
|
|
|
|
start_state = 'ok'
|
|
start = StateView('lims.notebook.results_conversion.start',
|
|
'lims.lims_notebook_results_conversion_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
self.lines_results_conversion(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_results_conversion(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
UomConversion = pool.get('lims.uom.conversion')
|
|
VolumeConversion = pool.get('lims.volume.conversion')
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
if (notebook_line.converted_result or not notebook_line.result or
|
|
notebook_line.result_modifier != 'eq'):
|
|
continue
|
|
iu = notebook_line.initial_unit
|
|
if not iu:
|
|
continue
|
|
fu = notebook_line.final_unit
|
|
if not fu:
|
|
continue
|
|
try:
|
|
ic = float(notebook_line.initial_concentration)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
try:
|
|
fc = float(notebook_line.final_concentration)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
try:
|
|
result = float(notebook_line.result)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
if (iu == fu and ic == fc):
|
|
converted_result = result
|
|
notebook_line.converted_result = str(converted_result)
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
lines_to_save.append(notebook_line)
|
|
elif (iu != fu and ic == fc):
|
|
formula = UomConversion.get_conversion_formula(iu, fu)
|
|
if not formula:
|
|
continue
|
|
variables = self._get_variables(formula, notebook_line)
|
|
parser = FormulaParser(formula, variables)
|
|
formula_result = parser.getValue()
|
|
|
|
converted_result = result * formula_result
|
|
notebook_line.converted_result = str(converted_result)
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
lines_to_save.append(notebook_line)
|
|
elif (iu == fu and ic != fc):
|
|
converted_result = result * (fc / ic)
|
|
notebook_line.converted_result = str(converted_result)
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
lines_to_save.append(notebook_line)
|
|
else:
|
|
formula = None
|
|
conversions = UomConversion.search([
|
|
('initial_uom', '=', iu),
|
|
('final_uom', '=', fu),
|
|
])
|
|
if conversions:
|
|
formula = conversions[0].conversion_formula
|
|
if not formula:
|
|
continue
|
|
|
|
initial_uom_volume = conversions[0].initial_uom_volume
|
|
final_uom_volume = conversions[0].final_uom_volume
|
|
variables = self._get_variables(formula, notebook_line,
|
|
initial_uom_volume, final_uom_volume)
|
|
parser = FormulaParser(formula, variables)
|
|
formula_result = parser.getValue()
|
|
|
|
if initial_uom_volume and final_uom_volume:
|
|
d_ic = VolumeConversion.brixToDensity(ic)
|
|
d_fc = VolumeConversion.brixToDensity(fc)
|
|
converted_result = (result * (fc / ic) * (d_fc / d_ic) *
|
|
formula_result)
|
|
notebook_line.converted_result = str(converted_result)
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
lines_to_save.append(notebook_line)
|
|
else:
|
|
converted_result = result * (fc / ic) * formula_result
|
|
notebook_line.converted_result = str(converted_result)
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
lines_to_save.append(notebook_line)
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def _get_variables(self, formula, notebook_line,
|
|
initial_uom_volume=False, final_uom_volume=False):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for var in ('DI',):
|
|
while True:
|
|
idx = formula.find(var)
|
|
if idx >= 0:
|
|
variables[var] = 0
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var == 'DI':
|
|
if initial_uom_volume:
|
|
c = float(notebook_line.initial_concentration)
|
|
result = VolumeConversion.brixToDensity(c)
|
|
if result:
|
|
variables[var] = result
|
|
elif final_uom_volume:
|
|
c = float(notebook_line.final_concentration)
|
|
result = VolumeConversion.brixToDensity(c)
|
|
if result:
|
|
variables[var] = result
|
|
return variables
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineResultsConversion(NotebookResultsConversion):
|
|
'Results Conversion'
|
|
__name__ = 'lims.notebook_line.results_conversion'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_results_conversion(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class NotebookLimitsValidationStart(ModelView):
|
|
'Limits Validation'
|
|
__name__ = 'lims.notebook.limits_validation.start'
|
|
|
|
|
|
class NotebookLimitsValidation(Wizard):
|
|
'Limits Validation'
|
|
__name__ = 'lims.notebook.limits_validation'
|
|
|
|
start_state = 'ok'
|
|
start = StateView('lims.notebook.limits_validation.start',
|
|
'lims.lims_notebook_limits_validation_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
self.lines_limits_validation(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_limits_validation(self, notebook_lines):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
try:
|
|
dl = float(notebook_line.detection_limit)
|
|
ql = float(notebook_line.quantification_limit)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
ll = (notebook_line.lower_limit and
|
|
float(notebook_line.lower_limit) or None)
|
|
ul = (notebook_line.upper_limit and
|
|
float(notebook_line.upper_limit) or None)
|
|
|
|
if (notebook_line.result and (
|
|
notebook_line.check_result_limits or
|
|
not notebook_line.converted_result)):
|
|
if notebook_line.result_modifier != 'eq':
|
|
continue
|
|
try:
|
|
value = float(notebook_line.result)
|
|
except ValueError:
|
|
continue
|
|
if (ll and value < ll) or (ul and value > ul):
|
|
raise UserError(gettext('lims.msg_error_limits_allowed',
|
|
line=notebook_line.rec_name))
|
|
if dl < value and value < ql:
|
|
notebook_line.result = str(ql)
|
|
notebook_line.result_modifier = 'low'
|
|
notebook_line.converted_result = None
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
notebook_line.rm_correction_formula = None
|
|
elif value < dl:
|
|
notebook_line.result = None
|
|
notebook_line.result_modifier = 'nd'
|
|
notebook_line.converted_result = None
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
notebook_line.rm_correction_formula = None
|
|
elif value == dl:
|
|
notebook_line.result = str(ql)
|
|
notebook_line.result_modifier = 'low'
|
|
notebook_line.converted_result = None
|
|
notebook_line.converted_result_modifier = 'eq'
|
|
notebook_line.rm_correction_formula = None
|
|
notebook_line.backup = str(value)
|
|
lines_to_save.append(notebook_line)
|
|
|
|
elif notebook_line.converted_result:
|
|
if notebook_line.converted_result_modifier != 'eq':
|
|
continue
|
|
try:
|
|
value = float(notebook_line.converted_result)
|
|
except ValueError:
|
|
continue
|
|
if (ll and value < ll) or (ul and value > ul):
|
|
raise UserError(gettext('lims.msg_error_limits_allowed',
|
|
line=notebook_line.rec_name))
|
|
if dl < value and value < ql:
|
|
notebook_line.converted_result = str(ql)
|
|
notebook_line.converted_result_modifier = 'low'
|
|
notebook_line.result_modifier = 'ni'
|
|
notebook_line.rm_correction_formula = None
|
|
elif value < dl:
|
|
notebook_line.converted_result = None
|
|
notebook_line.converted_result_modifier = 'nd'
|
|
notebook_line.result_modifier = 'ni'
|
|
notebook_line.rm_correction_formula = None
|
|
elif value == dl:
|
|
notebook_line.converted_result = str(ql)
|
|
notebook_line.converted_result_modifier = 'low'
|
|
notebook_line.result_modifier = 'ni'
|
|
notebook_line.rm_correction_formula = None
|
|
notebook_line.backup = str(value)
|
|
lines_to_save.append(notebook_line)
|
|
|
|
else:
|
|
continue
|
|
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineLimitsValidation(NotebookLimitsValidation):
|
|
'Limits Validation'
|
|
__name__ = 'lims.notebook_line.limits_validation'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_limits_validation(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class NotebookInternalRelationsCalc1Start(ModelView):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_1.start'
|
|
|
|
|
|
class NotebookInternalRelationsCalc1Relation(ModelSQL):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_1.relation'
|
|
_table = 'lims_notebook_internal_relations_c_1_rel'
|
|
|
|
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook')
|
|
internal_relation = fields.Many2One('lims.analysis', 'Internal relation')
|
|
variables = fields.One2Many(
|
|
'lims.notebook.internal_relations_calc_1.variable', 'relation',
|
|
'Variables')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class NotebookInternalRelationsCalc1Variable(ModelSQL):
|
|
'Formula Variable'
|
|
__name__ = 'lims.notebook.internal_relations_calc_1.variable'
|
|
|
|
relation = fields.Many2One(
|
|
'lims.notebook.internal_relations_calc_1.relation', 'Relation',
|
|
ondelete='CASCADE', readonly=True)
|
|
line = fields.Many2One('lims.notebook.line', 'Line')
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
use = fields.Boolean('Use')
|
|
|
|
|
|
class NotebookInternalRelationsCalc1(Wizard):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_1'
|
|
|
|
start_state = 'search'
|
|
start = StateView('lims.notebook.internal_relations_calc_1.start',
|
|
'lims.lims_notebook_internal_relations_calc_1_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
confirm = StateTransition()
|
|
|
|
def transition_search(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
if not notebook.lines:
|
|
return 'end'
|
|
|
|
if self.get_relations(notebook.lines):
|
|
return 'confirm'
|
|
return 'end'
|
|
|
|
def get_relations(self, notebook_lines):
|
|
NotebookInternalRelationsCalc1Relation = Pool().get(
|
|
'lims.notebook.internal_relations_calc_1.relation')
|
|
|
|
relations = {}
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
analysis_code = notebook_line.analysis.code
|
|
if (not analysis_code or notebook_line.analysis.behavior !=
|
|
'internal_relation'):
|
|
continue
|
|
if notebook_line.result or notebook_line.converted_result:
|
|
continue
|
|
|
|
formulas = notebook_line.analysis.result_formula
|
|
formulas += ("+" +
|
|
notebook_line.analysis.converted_result_formula)
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formulas = formulas.replace(i, '')
|
|
variables = self._get_variables_list(formulas,
|
|
notebook_line.notebook, {})
|
|
if not variables:
|
|
continue
|
|
has_repetition_zero = False
|
|
for var in variables:
|
|
if var['use']:
|
|
has_repetition_zero = True
|
|
if not has_repetition_zero:
|
|
continue
|
|
|
|
relations[notebook_line.analysis.id] = {
|
|
'notebook': notebook_line.notebook.id,
|
|
'internal_relation': notebook_line.analysis.id,
|
|
'variables': [('create', variables)],
|
|
'session_id': self._session_id,
|
|
}
|
|
if relations:
|
|
NotebookInternalRelationsCalc1Relation.create(
|
|
[ir for ir in relations.values()])
|
|
return True
|
|
return False
|
|
|
|
def _get_variables_list(self, formula, notebook, analysis={}):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
variables = {}
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
variables[var] = None
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var[0] in ('A', 'D', 'T'):
|
|
analysis_code = var[1:]
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
continue
|
|
for nl in notebook_lines:
|
|
analysis[nl.id] = {
|
|
'line': nl.id,
|
|
'analysis': nl.analysis.id,
|
|
'repetition': nl.repetition,
|
|
'use': True if nl.repetition == 0 else False,
|
|
}
|
|
elif var[0] in ('Y', 'R'):
|
|
analysis_code = var[1:]
|
|
internal_relations = Analysis.search([
|
|
('code', '=', analysis_code),
|
|
])
|
|
if not internal_relations:
|
|
continue
|
|
more_formulas = internal_relations[0].converted_result_formula
|
|
more_formulas += "+" + internal_relations[0].result_formula
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
more_formulas = more_formulas.replace(i, '')
|
|
self._get_variables_list(more_formulas, notebook, analysis)
|
|
|
|
return [v for v in analysis.values()]
|
|
|
|
def transition_confirm(self):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
NotebookInternalRelationsCalc1Relation = pool.get(
|
|
'lims.notebook.internal_relations_calc_1.relation')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
date = Date.today()
|
|
|
|
relations = NotebookInternalRelationsCalc1Relation.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
notebook_lines_to_save = []
|
|
for relation in relations:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', relation.notebook.id),
|
|
('analysis', '=', relation.internal_relation.id)
|
|
])
|
|
if len(notebook_lines) != 1:
|
|
continue
|
|
|
|
analysis_code = relation.internal_relation.code
|
|
result = self._get_relation_result(analysis_code,
|
|
relation.notebook, analysis_code)
|
|
converted_result = self._get_relation_result(analysis_code,
|
|
relation.notebook, analysis_code, converted=True)
|
|
|
|
notebook_line = notebook_lines[0]
|
|
if result is not None:
|
|
notebook_line.result = str(result)
|
|
if converted_result is not None:
|
|
notebook_line.converted_result = str(converted_result)
|
|
if result is not None or converted_result is not None:
|
|
notebook_line.start_date = date
|
|
notebook_line.end_date = date
|
|
notebook_lines_to_save.append(notebook_line)
|
|
NotebookLine.save(notebook_lines_to_save)
|
|
return 'end'
|
|
|
|
def _get_analysis_result(self, analysis_code, notebook, relation_code,
|
|
converted=False):
|
|
NotebookInternalRelationsCalc1Variable = Pool().get(
|
|
'lims.notebook.internal_relations_calc_1.variable')
|
|
|
|
variables = NotebookInternalRelationsCalc1Variable.search([
|
|
('relation.session_id', '=', self._session_id),
|
|
('relation.notebook', '=', notebook.id),
|
|
('relation.internal_relation.code', '=', relation_code),
|
|
('analysis.code', '=', analysis_code),
|
|
('use', '=', True),
|
|
])
|
|
if not variables:
|
|
return None
|
|
|
|
notebook_line = variables[0].line
|
|
if not notebook_line:
|
|
return None
|
|
|
|
try:
|
|
if converted:
|
|
res = float(notebook_line.converted_result)
|
|
else:
|
|
res = float(notebook_line.result)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
return round(res, notebook_line.decimals)
|
|
|
|
def _get_relation_result(self, analysis_code, notebook, relation_code,
|
|
converted=False, round_=False):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
internal_relations = Analysis.search([
|
|
('code', '=', analysis_code),
|
|
])
|
|
if not internal_relations:
|
|
return None
|
|
if converted:
|
|
formula = internal_relations[0].converted_result_formula
|
|
else:
|
|
formula = internal_relations[0].result_formula
|
|
if not formula:
|
|
return None
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formula = formula.replace(i, '')
|
|
variables = self._get_variables(formula, notebook, relation_code,
|
|
converted)
|
|
if not variables:
|
|
return None
|
|
|
|
parser = FormulaParser(formula, variables)
|
|
value = parser.getValue()
|
|
|
|
if int(value) == value:
|
|
res = int(value)
|
|
else:
|
|
epsilon = 0.0000000001
|
|
if int(value + epsilon) != int(value):
|
|
res = int(value + epsilon)
|
|
elif int(value - epsilon) != int(value):
|
|
res = int(value)
|
|
else:
|
|
res = float(value)
|
|
if not round_:
|
|
return res
|
|
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('repetition', '=', 0),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
return None
|
|
return round(res, notebook_lines[0].decimals)
|
|
|
|
def _get_variables(self, formula, notebook, relation_code,
|
|
converted=False):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
variables[var] = None
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var[0] == 'A':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'D':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'T':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'R':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
relation_code, converted, round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'Y':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
relation_code, converted, round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
for var in variables.values():
|
|
if var is None:
|
|
return None
|
|
return variables
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineInternalRelationsCalc1(NotebookInternalRelationsCalc1):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook_line.internal_relations_calc_1'
|
|
|
|
def transition_search(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
if self.get_relations(notebook_lines):
|
|
return 'confirm'
|
|
return 'end'
|
|
|
|
|
|
class NotebookInternalRelationsCalc2Start(ModelView):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2.start'
|
|
|
|
|
|
class NotebookInternalRelationsCalc2Result(ModelView):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2.result'
|
|
|
|
relations = fields.Many2Many(
|
|
'lims.notebook.internal_relations_calc_2.relation', None, None,
|
|
'Relation')
|
|
total = fields.Integer('Total')
|
|
index = fields.Integer('Index')
|
|
|
|
|
|
class NotebookInternalRelationsCalc2Relation(ModelSQL, ModelView):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2.relation'
|
|
_table = 'lims_notebook_internal_relations_c_2_rel'
|
|
|
|
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook')
|
|
internal_relation = fields.Many2One('lims.analysis', 'Internal relation')
|
|
variables = fields.One2Many(
|
|
'lims.notebook.internal_relations_calc_2.variable', 'relation',
|
|
'Variables')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class NotebookInternalRelationsCalc2Variable(ModelSQL, ModelView):
|
|
'Formula Variable'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2.variable'
|
|
|
|
relation = fields.Many2One(
|
|
'lims.notebook.internal_relations_calc_2.relation', 'Relation',
|
|
ondelete='CASCADE', readonly=True)
|
|
line = fields.Many2One('lims.notebook.line', 'Line')
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
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_line_field')
|
|
result = fields.Function(fields.Char('Result'), 'get_line_field')
|
|
initial_unit = fields.Function(fields.Many2One('product.uom',
|
|
'Initial unit'), 'get_line_field')
|
|
initial_concentration = fields.Function(fields.Char(
|
|
'Initial concentration'), 'get_line_field')
|
|
converted_result_modifier = fields.Function(fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
], 'Converted result modifier'), 'get_line_field')
|
|
converted_result = fields.Function(fields.Char('Converted result'),
|
|
'get_line_field')
|
|
final_unit = fields.Function(fields.Many2One('product.uom', 'Final unit'),
|
|
'get_line_field')
|
|
final_concentration = fields.Function(fields.Char('Final concentration'),
|
|
'get_line_field')
|
|
use = fields.Boolean('Use')
|
|
|
|
@classmethod
|
|
def __setup__(cls):
|
|
super().__setup__()
|
|
cls._order.insert(0, ('relation', 'ASC'))
|
|
cls._order.insert(1, ('analysis', 'ASC'))
|
|
cls._order.insert(2, ('repetition', 'ASC'))
|
|
|
|
@classmethod
|
|
def get_line_field(cls, variables, names):
|
|
result = {}
|
|
for name in names:
|
|
result[name] = {}
|
|
if name in ('initial_unit', 'final_unit'):
|
|
for v in variables:
|
|
field = getattr(v.line, name, None)
|
|
result[name][v.id] = field.id if field else None
|
|
else:
|
|
for v in variables:
|
|
result[name][v.id] = getattr(v.line, name, None)
|
|
return result
|
|
|
|
|
|
class NotebookInternalRelationsCalc2Process(ModelView):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2.process'
|
|
|
|
notebook = fields.Many2One('lims.notebook', 'Laboratory notebook',
|
|
readonly=True)
|
|
internal_relation = fields.Many2One('lims.analysis', 'Internal relation',
|
|
readonly=True)
|
|
variables = fields.One2Many(
|
|
'lims.notebook.internal_relations_calc_2.variable', None,
|
|
'Variables')
|
|
|
|
|
|
class NotebookInternalRelationsCalc2(Wizard):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook.internal_relations_calc_2'
|
|
|
|
start_state = 'search'
|
|
start = StateView('lims.notebook.internal_relations_calc_2.start',
|
|
'lims.lims_notebook_internal_relations_calc_2_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
result = StateView('lims.notebook.internal_relations_calc_2.result',
|
|
'lims.lims_notebook_internal_relations_calc_2_result_view_form', [])
|
|
next_ = StateTransition()
|
|
process = StateView('lims.notebook.internal_relations_calc_2.process',
|
|
'lims.lims_notebook_internal_relations_calc_2_process_view_form', [
|
|
Button('Next', 'check_variables', 'tryton-forward', default=True),
|
|
])
|
|
check_variables = StateTransition()
|
|
confirm = StateTransition()
|
|
|
|
def transition_search(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
|
|
if self.get_relations(notebook.lines):
|
|
return 'next_'
|
|
return 'end'
|
|
|
|
def get_relations(self, notebook_lines):
|
|
NotebookInternalRelationsCalc2Relation = Pool().get(
|
|
'lims.notebook.internal_relations_calc_2.relation')
|
|
|
|
relations = {}
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
analysis_code = notebook_line.analysis.code
|
|
if (not analysis_code or notebook_line.analysis.behavior !=
|
|
'internal_relation'):
|
|
continue
|
|
if notebook_line.result or notebook_line.converted_result:
|
|
continue
|
|
|
|
formulas = notebook_line.analysis.result_formula
|
|
formulas += ("+" +
|
|
notebook_line.analysis.converted_result_formula)
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formulas = formulas.replace(i, '')
|
|
variables = self._get_variables_list(formulas,
|
|
notebook_line.notebook, {})
|
|
if not variables:
|
|
continue
|
|
has_repetitions = False
|
|
for var in variables:
|
|
if var['repetition'] > 0:
|
|
has_repetitions = True
|
|
if not has_repetitions:
|
|
continue
|
|
|
|
relations[notebook_line.analysis.id] = {
|
|
'notebook': notebook_line.notebook.id,
|
|
'internal_relation': notebook_line.analysis.id,
|
|
'variables': [('create', variables)],
|
|
'session_id': self._session_id,
|
|
}
|
|
|
|
if relations:
|
|
res_lines = NotebookInternalRelationsCalc2Relation.create(
|
|
[ir for ir in relations.values()])
|
|
self.result.relations = res_lines
|
|
self.result.total = len(self.result.relations)
|
|
self.result.index = 0
|
|
return True
|
|
return False
|
|
|
|
def transition_next_(self):
|
|
if self.result.index < self.result.total:
|
|
relation = self.result.relations[self.result.index]
|
|
self.process.notebook = relation.notebook.id
|
|
self.process.internal_relation = relation.internal_relation.id
|
|
self.process.variables = None
|
|
self.result.index += 1
|
|
return 'process'
|
|
return 'confirm'
|
|
|
|
def default_process(self, fields):
|
|
NotebookInternalRelationsCalc2Variable = Pool().get(
|
|
'lims.notebook.internal_relations_calc_2.variable')
|
|
|
|
if not self.process.internal_relation:
|
|
return {}
|
|
|
|
default = {}
|
|
default['notebook'] = self.process.notebook.id
|
|
default['internal_relation'] = self.process.internal_relation.id
|
|
if self.process.variables:
|
|
default['variables'] = [v.id for v in self.process.variables]
|
|
else:
|
|
variables = NotebookInternalRelationsCalc2Variable.search([
|
|
('relation.session_id', '=', self._session_id),
|
|
('relation.notebook', '=', self.process.notebook.id),
|
|
('relation.internal_relation', '=',
|
|
self.process.internal_relation.id),
|
|
])
|
|
if variables:
|
|
default['variables'] = [v.id for v in variables]
|
|
return default
|
|
|
|
def _get_variables_list(self, formula, notebook, analysis={}):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
variables = {}
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
variables[var] = None
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var[0] in ('A', 'D', 'T'):
|
|
analysis_code = var[1:]
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
continue
|
|
for nl in notebook_lines:
|
|
analysis[nl.id] = {
|
|
'line': nl.id,
|
|
'analysis': nl.analysis.id,
|
|
'repetition': nl.repetition,
|
|
'use': True if nl.repetition == 0 else False,
|
|
}
|
|
elif var[0] in ('R', 'Y'):
|
|
analysis_code = var[1:]
|
|
internal_relations = Analysis.search([
|
|
('code', '=', analysis_code),
|
|
])
|
|
if not internal_relations:
|
|
continue
|
|
more_formulas = internal_relations[0].converted_result_formula
|
|
more_formulas += "+" + internal_relations[0].result_formula
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
more_formulas = more_formulas.replace(i, '')
|
|
self._get_variables_list(more_formulas, notebook, analysis)
|
|
|
|
return [v for v in analysis.values()]
|
|
|
|
def transition_check_variables(self):
|
|
variables = {}
|
|
for var in self.process.variables:
|
|
analysis_code = var.analysis.code
|
|
if analysis_code not in variables:
|
|
variables[analysis_code] = False
|
|
if var.use:
|
|
if variables[analysis_code]:
|
|
variables[analysis_code] = False
|
|
else:
|
|
variables[analysis_code] = True
|
|
var.save()
|
|
|
|
for var in variables.values():
|
|
if not var:
|
|
return 'process'
|
|
return 'next_'
|
|
|
|
def transition_confirm(self):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
NotebookInternalRelationsCalc2Relation = pool.get(
|
|
'lims.notebook.internal_relations_calc_2.relation')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
date = Date.today()
|
|
|
|
relations = NotebookInternalRelationsCalc2Relation.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
notebook_lines_to_save = []
|
|
for relation in relations:
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', relation.notebook.id),
|
|
('analysis', '=', relation.internal_relation.id)
|
|
])
|
|
if len(notebook_lines) != 1:
|
|
continue
|
|
|
|
analysis_code = relation.internal_relation.code
|
|
result = self._get_relation_result(analysis_code,
|
|
relation.notebook, analysis_code)
|
|
converted_result = self._get_relation_result(analysis_code,
|
|
relation.notebook, analysis_code, converted=True)
|
|
|
|
notebook_line = notebook_lines[0]
|
|
if result is not None:
|
|
notebook_line.result = str(result)
|
|
if converted_result is not None:
|
|
notebook_line.converted_result = str(converted_result)
|
|
if result is not None or converted_result is not None:
|
|
notebook_line.start_date = date
|
|
notebook_line.end_date = date
|
|
notebook_lines_to_save.append(notebook_line)
|
|
NotebookLine.save(notebook_lines_to_save)
|
|
return 'end'
|
|
|
|
def _get_analysis_result(self, analysis_code, notebook, relation_code,
|
|
converted=False):
|
|
NotebookInternalRelationsCalc2Variable = Pool().get(
|
|
'lims.notebook.internal_relations_calc_2.variable')
|
|
|
|
variables = NotebookInternalRelationsCalc2Variable.search([
|
|
('relation.session_id', '=', self._session_id),
|
|
('relation.notebook', '=', notebook.id),
|
|
('relation.internal_relation.code', '=', relation_code),
|
|
('analysis.code', '=', analysis_code),
|
|
('use', '=', True),
|
|
])
|
|
if not variables:
|
|
return None
|
|
|
|
notebook_line = variables[0].line
|
|
if not notebook_line:
|
|
return None
|
|
|
|
try:
|
|
if converted:
|
|
res = float(notebook_line.converted_result)
|
|
else:
|
|
res = float(notebook_line.result)
|
|
except (TypeError, ValueError):
|
|
return None
|
|
return round(res, notebook_line.decimals)
|
|
|
|
def _get_relation_result(self, analysis_code, notebook, relation_code,
|
|
converted=False, round_=False):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
internal_relations = Analysis.search([
|
|
('code', '=', analysis_code),
|
|
])
|
|
if not internal_relations:
|
|
return None
|
|
if converted:
|
|
formula = internal_relations[0].converted_result_formula
|
|
else:
|
|
formula = internal_relations[0].result_formula
|
|
if not formula:
|
|
return None
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formula = formula.replace(i, '')
|
|
variables = self._get_variables(formula, notebook, relation_code,
|
|
converted)
|
|
if not variables:
|
|
return None
|
|
|
|
parser = FormulaParser(formula, variables)
|
|
value = parser.getValue()
|
|
|
|
if int(value) == value:
|
|
res = int(value)
|
|
else:
|
|
epsilon = 0.0000000001
|
|
if int(value + epsilon) != int(value):
|
|
res = int(value + epsilon)
|
|
elif int(value - epsilon) != int(value):
|
|
res = int(value)
|
|
else:
|
|
res = float(value)
|
|
if not round_:
|
|
return res
|
|
|
|
with Transaction().set_user(0):
|
|
notebook_lines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis.code', '=', analysis_code),
|
|
('repetition', '=', 0),
|
|
('annulment_date', '=', None),
|
|
])
|
|
if not notebook_lines:
|
|
return None
|
|
return round(res, notebook_lines[0].decimals)
|
|
|
|
def _get_variables(self, formula, notebook, relation_code,
|
|
converted=False):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
variables[var] = None
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var[0] == 'A':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'D':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'T':
|
|
analysis_code = var[1:]
|
|
result = self._get_analysis_result(analysis_code, notebook,
|
|
relation_code, converted)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'R':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
relation_code, converted, round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToSolubleSolids(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
elif var[0] == 'Y':
|
|
analysis_code = var[1:]
|
|
result = self._get_relation_result(analysis_code, notebook,
|
|
relation_code, converted, round_=True)
|
|
if result is not None:
|
|
result = VolumeConversion.brixToDensity(result)
|
|
if result is not None:
|
|
variables[var] = result
|
|
for var in variables.values():
|
|
if var is None:
|
|
return None
|
|
return variables
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineInternalRelationsCalc2(NotebookInternalRelationsCalc2):
|
|
'Internal Relations Calculation'
|
|
__name__ = 'lims.notebook_line.internal_relations_calc_2'
|
|
|
|
def transition_search(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
if self.get_relations(notebook_lines):
|
|
return 'next_'
|
|
return 'end'
|
|
|
|
|
|
class NotebookLoadResultsFormulaStart(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.start'
|
|
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis',
|
|
domain=[('state', '=', 'active'), ('formula', '!=', None)])
|
|
method = fields.Many2One('lims.lab.method', 'Method')
|
|
start_date = fields.Date('Start date', required=True)
|
|
|
|
|
|
class NotebookLoadResultsFormulaEmpty(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.empty'
|
|
|
|
|
|
class NotebookLoadResultsFormulaResult(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.result'
|
|
|
|
lines = fields.Many2Many('lims.notebook.load_results_formula.line',
|
|
None, None, 'Lines')
|
|
total = fields.Integer('Total')
|
|
index = fields.Integer('Index')
|
|
|
|
|
|
class NotebookLoadResultsFormulaLine(ModelSQL, ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.line'
|
|
|
|
index = fields.Integer('Index')
|
|
line = fields.Many2One('lims.notebook.line', 'Line')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class NotebookLoadResultsFormulaAction(ModelSQL):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.action'
|
|
|
|
line = fields.Many2One('lims.notebook.line', 'Line')
|
|
result = fields.Char('Result')
|
|
result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('d', 'Detected'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
('abs', 'Absence'),
|
|
('pre', 'Presence'),
|
|
], 'Result modifier', sort=False)
|
|
end_date = fields.Date('End date')
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional')
|
|
chromatogram = fields.Char('Chromatogram')
|
|
initial_concentration = fields.Char('Initial concentration')
|
|
comments = fields.Text('Comments')
|
|
formula = fields.Many2One('lims.formula', 'Formula')
|
|
variables = fields.One2Many('lims.notebook.load_results_formula.variable',
|
|
'action', 'Variables')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
|
|
class NotebookLoadResultsFormulaProcess(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.process'
|
|
|
|
line = fields.Many2One('lims.notebook.line', 'Line', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
readonly=True)
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', readonly=True)
|
|
formula = fields.Many2One('lims.formula', 'Formula', readonly=True)
|
|
formula_formula = fields.Function(fields.Char('Formula'),
|
|
'on_change_with_formula_formula')
|
|
variables = fields.One2Many('lims.notebook.load_results_formula.variable',
|
|
None, 'Variables')
|
|
result = fields.Char('Result', required=True)
|
|
result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('d', 'Detected'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
('abs', 'Absence'),
|
|
('pre', 'Presence'),
|
|
], 'Result modifier', sort=False, required=True)
|
|
end_date = fields.Date('End date')
|
|
end_date_copy = fields.Boolean('Field copy')
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', required=True, readonly=True)
|
|
chromatogram = fields.Char('Chromatogram')
|
|
chromatogram_copy = fields.Boolean('Field copy')
|
|
initial_concentration = fields.Char('Initial concentration')
|
|
initial_concentration_copy = fields.Boolean('Field copy')
|
|
comments = fields.Text('Comments')
|
|
comments_copy = fields.Boolean('Field copy')
|
|
|
|
@fields.depends('formula', 'variables', '_parent_formula.formula')
|
|
def on_change_with_result(self, name=None):
|
|
if not self.formula or not self.variables:
|
|
return None
|
|
|
|
formula = self.formula.formula
|
|
variables = {}
|
|
for variable in self.variables:
|
|
if not variable.value:
|
|
return ''
|
|
variables[variable.number] = variable.value
|
|
|
|
parser = FormulaParser(formula, variables)
|
|
value = parser.getValue()
|
|
|
|
return str(value)
|
|
|
|
@fields.depends('formula', '_parent_formula.formula')
|
|
def on_change_with_formula_formula(self, name=None):
|
|
if self.formula:
|
|
formula = self.formula.formula
|
|
variables = {}
|
|
for variable in self.variables:
|
|
variables[variable.number] = variable.description
|
|
for k, v in variables.items():
|
|
formula = formula.replace(k, v)
|
|
return formula
|
|
return ''
|
|
|
|
|
|
class NotebookLoadResultsFormulaVariable(ModelSQL, ModelView):
|
|
'Formula Variable'
|
|
__name__ = 'lims.notebook.load_results_formula.variable'
|
|
|
|
action = fields.Many2One('lims.notebook.load_results_formula.action',
|
|
'Action', ondelete='CASCADE')
|
|
number = fields.Char('Number', readonly=True)
|
|
description = fields.Char('Description', readonly=True)
|
|
value = fields.Char('Value')
|
|
|
|
|
|
class NotebookLoadResultsFormulaBeginning(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.beginning'
|
|
|
|
|
|
class NotebookLoadResultsFormulaConfirm(ModelView):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula.confirm'
|
|
|
|
|
|
class NotebookLoadResultsFormulaSit1(ModelView):
|
|
'Professionals Control'
|
|
__name__ = 'lims.notebook.load_results_formula.sit1'
|
|
|
|
msg = fields.Text('Message')
|
|
|
|
|
|
class NotebookLoadResultsFormulaSit2(ModelView):
|
|
'Professionals Control'
|
|
__name__ = 'lims.notebook.load_results_formula.sit2'
|
|
|
|
details = fields.One2Many('lims.notebook.load_results_formula.sit2.detail',
|
|
None, 'Supervisors')
|
|
|
|
|
|
class NotebookLoadResultsFormulaSit2Detail(ModelSQL, ModelView):
|
|
'Supervisor'
|
|
__name__ = 'lims.notebook.load_results_formula.sit2.detail'
|
|
_table = 'lims_notebook_load_results_formula_s2_detail'
|
|
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Professional', readonly=True)
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
supervisor = fields.Many2One('lims.laboratory.professional',
|
|
'Supervisor', depends=['supervisor_domain'],
|
|
domain=[('id', 'in', Eval('supervisor_domain'))])
|
|
supervisor_domain = fields.Function(fields.Many2Many(
|
|
'lims.laboratory.professional', None, None, 'Supervisor domain'),
|
|
'get_supervisor_domain')
|
|
lines = fields.Many2Many(
|
|
'lims.notebook.load_results_formula.sit2.detail.line',
|
|
'load_results', 'notebook_line', 'Lines')
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
def get_supervisor_domain(self, name=None):
|
|
LabProfessionalMethod = Pool().get('lims.lab.professional.method')
|
|
|
|
res = []
|
|
qualifications = LabProfessionalMethod.search([
|
|
('method', '=', self.method.id),
|
|
('type', '=', 'analytical'),
|
|
('state', 'in', ('qualified', 'requalified')),
|
|
])
|
|
if qualifications:
|
|
res = [q.professional.id for q in qualifications]
|
|
return res
|
|
|
|
|
|
class NotebookLoadResultsFormulaSit2DetailLine(ModelSQL):
|
|
'Notebook Line'
|
|
__name__ = 'lims.notebook.load_results_formula.sit2.detail.line'
|
|
_table = 'lims_notebook_load_results_formula_sit2_d_l'
|
|
|
|
load_results = fields.Many2One(
|
|
'lims.notebook.load_results_formula.sit2.detail', 'Load Results',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
|
|
ondelete='CASCADE', select=True, required=True)
|
|
|
|
|
|
class NotebookLoadResultsFormula(Wizard):
|
|
'Load Results by Formula'
|
|
__name__ = 'lims.notebook.load_results_formula'
|
|
|
|
start = StateView('lims.notebook.load_results_formula.start',
|
|
'lims.lims_notebook_load_results_formula_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
empty = StateView('lims.notebook.load_results_formula.empty',
|
|
'lims.lims_notebook_load_results_formula_empty_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search again', 'start', 'tryton-forward', default=True),
|
|
])
|
|
result = StateView('lims.notebook.load_results_formula.result',
|
|
'lims.lims_notebook_load_results_formula_result_view_form', [])
|
|
next_ = StateTransition()
|
|
prev_ = StateTransition()
|
|
process = StateView('lims.notebook.load_results_formula.process',
|
|
'lims.lims_notebook_load_results_formula_process_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Previous', 'prev_', 'tryton-back'),
|
|
Button('Next', 'next_', 'ttryton-forward', default=True),
|
|
])
|
|
beginning = StateView('lims.notebook.load_results_formula.beginning',
|
|
'lims.lims_notebook_load_results_formula_beginning_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Next', 'next_', 'tryton-forward', default=True),
|
|
])
|
|
confirm = StateView('lims.notebook.load_results_formula.confirm',
|
|
'lims.lims_notebook_load_results_formula_confirm_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Previous', 'prev_', 'tryton-back'),
|
|
Button('Confirm', 'check_professional', 'tryton-ok', default=True),
|
|
])
|
|
check_professional = StateTransition()
|
|
sit1 = StateView('lims.notebook.load_results_formula.sit1',
|
|
'lims.lims_notebook_load_results_formula_sit1_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel', default=True),
|
|
])
|
|
sit2 = StateView('lims.notebook.load_results_formula.sit2',
|
|
'lims.lims_notebook_load_results_formula_sit2_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Confirm', 'sit2_ok', 'tryton-ok', default=True),
|
|
])
|
|
sit2_ok = StateTransition()
|
|
confirm_ = StateTransition()
|
|
|
|
def transition_search(self):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
NotebookLoadResultsFormulaLine = pool.get(
|
|
'lims.notebook.load_results_formula.line')
|
|
|
|
clause = [
|
|
('start_date', '=', self.start.start_date),
|
|
('end_date', '=', None),
|
|
('analysis.formula', '!=', None),
|
|
]
|
|
if self.start.analysis:
|
|
clause.append(('analysis', '=', self.start.analysis.id))
|
|
if self.start.method:
|
|
clause.append(('method', '=', self.start.method.id))
|
|
|
|
lines = NotebookLine.search(clause, order=[
|
|
('analysis_order', 'ASC'), ('id', 'ASC')])
|
|
if lines:
|
|
res_lines = []
|
|
count = 1
|
|
for line in lines:
|
|
res_line, = NotebookLoadResultsFormulaLine.create([{
|
|
'session_id': self._session_id,
|
|
'index': count,
|
|
'line': line.id,
|
|
}])
|
|
res_lines.append(res_line.id)
|
|
count += 1
|
|
|
|
self.result.lines = res_lines
|
|
self.result.total = len(res_lines)
|
|
self.result.index = 0
|
|
return 'next_'
|
|
return 'empty'
|
|
|
|
def transition_next_(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsFormulaAction = pool.get(
|
|
'lims.notebook.load_results_formula.action')
|
|
NotebookLoadResultsFormulaLine = pool.get(
|
|
'lims.notebook.load_results_formula.line')
|
|
LaboratoryProfessional = pool.get('lims.laboratory.professional')
|
|
|
|
has_prev = (hasattr(self.process, 'line') and
|
|
getattr(self.process, 'line'))
|
|
if has_prev:
|
|
defaults = {
|
|
'session_id': self._session_id,
|
|
'line': self.process.line.id,
|
|
'result': self.process.result,
|
|
'result_modifier': self.process.result_modifier,
|
|
'end_date': self.process.end_date,
|
|
'professional': self.process.professional.id,
|
|
'chromatogram': self.process.chromatogram,
|
|
'initial_concentration': self.process.initial_concentration,
|
|
'comments': self.process.comments,
|
|
'formula': (self.process.formula.id if
|
|
self.process.formula else None),
|
|
}
|
|
variables = []
|
|
for var in self.process.variables:
|
|
variables.append({
|
|
'number': var.number,
|
|
'description': var.description,
|
|
'value': var.value,
|
|
})
|
|
defaults['variables'] = [('create', variables)]
|
|
|
|
action = NotebookLoadResultsFormulaAction.search([
|
|
('session_id', '=', self._session_id),
|
|
('line', '=', self.process.line.id),
|
|
])
|
|
if action:
|
|
defaults['variables'] = [(
|
|
'delete', [v.id for a in action for v in a.variables],
|
|
)] + defaults['variables']
|
|
NotebookLoadResultsFormulaAction.write(action, defaults)
|
|
else:
|
|
NotebookLoadResultsFormulaAction.create([defaults])
|
|
|
|
self.result.index += 1
|
|
if self.result.index <= self.result.total:
|
|
|
|
line = NotebookLoadResultsFormulaLine.search([
|
|
('session_id', '=', self._session_id),
|
|
('index', '=', self.result.index),
|
|
])
|
|
self.process.line = line[0].line.id
|
|
|
|
action = NotebookLoadResultsFormulaAction.search([
|
|
('session_id', '=', self._session_id),
|
|
('line', '=', line[0].line.id),
|
|
])
|
|
if action:
|
|
self.process.result = action[0].result
|
|
self.process.result_modifier = action[0].result_modifier
|
|
self.process.end_date = action[0].end_date
|
|
self.process.professional = action[0].professional.id
|
|
self.process.chromatogram = action[0].chromatogram
|
|
self.process.initial_concentration = (
|
|
action[0].initial_concentration)
|
|
self.process.comments = action[0].comments
|
|
self.process.formula = action[0].formula
|
|
self.process.variables = [v.id for v in action[0].variables]
|
|
elif has_prev:
|
|
self.process.result = None
|
|
self.process.result_modifier = 'eq'
|
|
if not self.process.end_date_copy:
|
|
self.process.end_date = None
|
|
if not self.process.chromatogram_copy:
|
|
self.process.chromatogram = None
|
|
if not self.process.initial_concentration_copy:
|
|
self.process.initial_concentration = None
|
|
if not self.process.comments_copy:
|
|
self.process.comments = None
|
|
self.process.formula = None
|
|
self.process.variables = None
|
|
else:
|
|
professional_id = (
|
|
LaboratoryProfessional.get_lab_professional())
|
|
self.process.professional = professional_id
|
|
|
|
return 'process'
|
|
return 'confirm'
|
|
|
|
def transition_prev_(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsFormulaAction = pool.get(
|
|
'lims.notebook.load_results_formula.action')
|
|
NotebookLoadResultsFormulaLine = pool.get(
|
|
'lims.notebook.load_results_formula.line')
|
|
|
|
self.result.index -= 1
|
|
if self.result.index >= 1:
|
|
line = NotebookLoadResultsFormulaLine.search([
|
|
('session_id', '=', self._session_id),
|
|
('index', '=', self.result.index),
|
|
])
|
|
self.process.line = line[0].line.id
|
|
|
|
action = NotebookLoadResultsFormulaAction.search([
|
|
('session_id', '=', self._session_id),
|
|
('line', '=', line[0].line.id),
|
|
])
|
|
if action:
|
|
self.process.result = action[0].result
|
|
self.process.result_modifier = action[0].result_modifier
|
|
self.process.end_date = action[0].end_date
|
|
self.process.professional = action[0].professional.id
|
|
self.process.chromatogram = action[0].chromatogram
|
|
self.process.initial_concentration = (
|
|
action[0].initial_concentration)
|
|
self.process.comments = action[0].comments
|
|
self.process.formula = action[0].formula
|
|
self.process.variables = [v.id for v in action[0].variables]
|
|
else:
|
|
self.process.result = None
|
|
self.process.result_modifier = 'eq'
|
|
self.process.end_date = None
|
|
self.process.professional = None
|
|
self.process.chromatogram = None
|
|
self.process.initial_concentration = None
|
|
self.process.comments = None
|
|
self.process.formula = None
|
|
self.process.variables = None
|
|
|
|
return 'process'
|
|
|
|
self.process.line = None
|
|
return 'beginning'
|
|
|
|
def default_process(self, fields):
|
|
if not self.process.line:
|
|
return {}
|
|
|
|
default = {}
|
|
default['line'] = self.process.line.id
|
|
default['fraction'] = self.process.line.notebook.fraction.id
|
|
default['repetition'] = self.process.line.repetition
|
|
default['product_type'] = (
|
|
self.process.line.notebook.product_type.id)
|
|
default['matrix'] = self.process.line.notebook.matrix.id
|
|
|
|
if (hasattr(self.process, 'formula') and
|
|
getattr(self.process, 'formula')):
|
|
formula = self.process.formula
|
|
default['formula'] = formula.id
|
|
variables = []
|
|
variables_desc = {}
|
|
for var in self.process.variables:
|
|
variables.append({
|
|
'number': var.number,
|
|
'description': var.description,
|
|
'value': var.value,
|
|
})
|
|
variables_desc[var.number] = var.description
|
|
default['variables'] = variables
|
|
formula_formula = formula.formula
|
|
for k, v in variables_desc.items():
|
|
formula_formula = formula_formula.replace(k, v)
|
|
default['formula_formula'] = formula_formula
|
|
|
|
default['result'] = self.process.result
|
|
default['result_modifier'] = self.process.result_modifier
|
|
|
|
default['initial_concentration'] = (
|
|
self.process.initial_concentration)
|
|
default['comments'] = self.process.comments
|
|
default['professional'] = self.process.professional.id
|
|
default['end_date'] = self.process.end_date
|
|
default['chromatogram'] = self.process.chromatogram
|
|
|
|
else:
|
|
formula = self.process.line.analysis.formula
|
|
if formula:
|
|
default['formula'] = formula.id
|
|
variables = []
|
|
variables_desc = {}
|
|
for var in formula.variables:
|
|
variables.append({
|
|
'number': var.number,
|
|
'description': var.description,
|
|
'value': var.constant,
|
|
})
|
|
variables_desc[var.number] = var.description
|
|
default['variables'] = variables
|
|
formula_formula = formula.formula
|
|
for k, v in variables_desc.items():
|
|
formula_formula = formula_formula.replace(k, v)
|
|
default['formula_formula'] = formula_formula
|
|
default['result_modifier'] = 'eq'
|
|
|
|
for field in ('initial_concentration', 'comments'):
|
|
if (hasattr(self.process, field + '_copy') and
|
|
getattr(self.process, field + '_copy')):
|
|
default[field] = getattr(self.process, field)
|
|
default[field + '_copy'] = getattr(self.process,
|
|
field + '_copy')
|
|
else:
|
|
default[field] = getattr(self.process.line, field)
|
|
for field in ('professional',):
|
|
if (hasattr(self.process, field) and
|
|
getattr(self.process, field)):
|
|
default[field] = getattr(self.process, field).id
|
|
for field in ('end_date', 'chromatogram'):
|
|
if (hasattr(self.process, field) and
|
|
getattr(self.process, field)):
|
|
default[field] = getattr(self.process, field)
|
|
for field in ('end_date_copy', 'chromatogram_copy'):
|
|
if (hasattr(self.process, field) and
|
|
getattr(self.process, field)):
|
|
default[field] = getattr(self.process, field)
|
|
|
|
return default
|
|
|
|
def transition_check_professional(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsFormulaAction = pool.get(
|
|
'lims.notebook.load_results_formula.action')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
LaboratoryProfessional = pool.get('lims.laboratory.professional')
|
|
LabMethod = pool.get('lims.lab.method')
|
|
NotebookLoadResultsFormulaSit2Detail = pool.get(
|
|
'lims.notebook.load_results_formula.sit2.detail')
|
|
|
|
actions = NotebookLoadResultsFormulaAction.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
|
|
situations = {}
|
|
prof_lines = {}
|
|
for data in actions:
|
|
key = (data.professional.id, data.line.method.id)
|
|
if key not in situations:
|
|
situations[key] = 0
|
|
if key not in prof_lines:
|
|
prof_lines[key] = []
|
|
prof_lines[key].append(data.line.id)
|
|
|
|
situation_1 = []
|
|
for key in situations.keys():
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', key[0]),
|
|
('method', '=', key[1]),
|
|
('type', '=', 'analytical'),
|
|
])
|
|
if not qualifications:
|
|
situations[key] = 1
|
|
situation_1.append(key)
|
|
elif qualifications[0].state == 'training':
|
|
situations[key] = 2
|
|
elif (qualifications[0].state in ('qualified', 'requalified')):
|
|
situations[key] = 3
|
|
if situation_1:
|
|
msg = ''
|
|
for key in situation_1:
|
|
professional = LaboratoryProfessional(key[0])
|
|
method = LabMethod(key[1])
|
|
msg += '%s: %s\n' % (professional.rec_name, method.code)
|
|
self.sit1.msg = msg
|
|
return 'sit1'
|
|
|
|
situation_2 = []
|
|
for key, sit in situations.items():
|
|
if sit == 2:
|
|
situation_2.append({
|
|
'session_id': self._session_id,
|
|
'professional': key[0],
|
|
'method': key[1],
|
|
'lines': [('add', prof_lines[key])],
|
|
})
|
|
if situation_2:
|
|
details = NotebookLoadResultsFormulaSit2Detail.create(
|
|
situation_2)
|
|
self.sit2.details = details
|
|
return 'sit2'
|
|
|
|
return 'confirm_'
|
|
|
|
def default_sit1(self, fields):
|
|
defaults = {}
|
|
if self.sit1.msg:
|
|
defaults['msg'] = self.sit1.msg
|
|
return defaults
|
|
|
|
def default_sit2(self, fields):
|
|
defaults = {}
|
|
if self.sit2.details:
|
|
defaults['details'] = [d.id for d in self.sit2.details]
|
|
return defaults
|
|
|
|
def transition_sit2_ok(self):
|
|
for detail in self.sit2.details:
|
|
if not detail.supervisor:
|
|
return 'sit2'
|
|
return 'confirm_'
|
|
|
|
def transition_confirm_(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsFormulaAction = pool.get(
|
|
'lims.notebook.load_results_formula.action')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
LabProfessionalMethodRequalification = pool.get(
|
|
'lims.lab.professional.method.requalification')
|
|
Date = pool.get('ir.date')
|
|
|
|
# Write Results to Notebook lines
|
|
actions = NotebookLoadResultsFormulaAction.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
for data in actions:
|
|
notebook_line = NotebookLine(data.line.id)
|
|
if not notebook_line:
|
|
continue
|
|
notebook_line_write = {
|
|
'result': data.result,
|
|
'result_modifier': data.result_modifier,
|
|
'end_date': data.end_date,
|
|
'chromatogram': data.chromatogram,
|
|
'initial_concentration': data.initial_concentration,
|
|
'comments': data.comments,
|
|
'converted_result': None,
|
|
'converted_result_modifier': 'eq',
|
|
'backup': None,
|
|
'verification': None,
|
|
'uncertainty': None,
|
|
}
|
|
if data.result_modifier == 'na':
|
|
notebook_line_write['annulled'] = True
|
|
notebook_line_write['annulment_date'] = datetime.now()
|
|
notebook_line_write['report'] = False
|
|
professionals = [{'professional': data.professional.id}]
|
|
notebook_line_write['professionals'] = (
|
|
[('delete', [p.id for p in notebook_line.professionals])] +
|
|
[('create', professionals)])
|
|
NotebookLine.write([notebook_line], notebook_line_write)
|
|
|
|
# Write Supervisors to Notebook lines
|
|
supervisor_lines = {}
|
|
if hasattr(self.sit2, 'details'):
|
|
for detail in self.sit2.details:
|
|
if detail.supervisor.id not in supervisor_lines:
|
|
supervisor_lines[detail.supervisor.id] = []
|
|
supervisor_lines[detail.supervisor.id].extend([
|
|
l.id for l in detail.lines])
|
|
for prof_id, lines in supervisor_lines.items():
|
|
notebook_lines = NotebookLine.search([
|
|
('id', 'in', lines),
|
|
])
|
|
if notebook_lines:
|
|
professionals = [{'professional': prof_id}]
|
|
notebook_line_write = {
|
|
'professionals': [('create', professionals)],
|
|
}
|
|
NotebookLine.write(notebook_lines, notebook_line_write)
|
|
|
|
# Write the execution of method
|
|
all_prof = {}
|
|
for data in actions:
|
|
key = (data.professional.id, data.line.method.id)
|
|
if key not in all_prof:
|
|
all_prof[key] = []
|
|
if hasattr(self.sit2, 'details'):
|
|
for detail in self.sit2.details:
|
|
key = (detail.supervisor.id, detail.method.id)
|
|
if key not in all_prof:
|
|
all_prof[key] = []
|
|
key = (detail.professional.id, detail.method.id)
|
|
if detail.supervisor.id not in all_prof[key]:
|
|
all_prof[key].append(detail.supervisor.id)
|
|
|
|
today = Date.today()
|
|
for key, sup in all_prof.items():
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', key[0]),
|
|
('method', '=', key[1]),
|
|
('type', '=', 'analytical'),
|
|
])
|
|
if professional_method.state == 'training':
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'training'),
|
|
])
|
|
if history:
|
|
prev_supervisors = [s.supervisor.id for s in
|
|
history[0].supervisors]
|
|
supervisors = [{'supervisor': s} for s in sup
|
|
if s not in prev_supervisors]
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
'supervisors': [('create', supervisors)],
|
|
})
|
|
else:
|
|
supervisors = [{'supervisor': s} for s in sup]
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'training',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
'supervisors': [('create', supervisors)],
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
elif professional_method.state == 'qualified':
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'qualification'),
|
|
])
|
|
if history:
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
})
|
|
else:
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'qualification',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
else:
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'requalification'),
|
|
])
|
|
if history:
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
})
|
|
else:
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'requalification',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
return 'end'
|
|
|
|
|
|
class NotebookLoadResultsManualStart(ModelView):
|
|
'Load Results Manually'
|
|
__name__ = 'lims.notebook.load_results_manual.start'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method', required=True)
|
|
start_date = fields.Date('Start date', required=True)
|
|
|
|
|
|
class NotebookLoadResultsManualEmpty(ModelView):
|
|
'Load Results Manually'
|
|
__name__ = 'lims.notebook.load_results_manual.empty'
|
|
|
|
|
|
class NotebookLoadResultsManualResult(ModelView):
|
|
'Load Results Manually'
|
|
__name__ = 'lims.notebook.load_results_manual.result'
|
|
|
|
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
|
|
start_date = fields.Date('Start date', readonly=True)
|
|
professional = fields.Many2One('lims.laboratory.professional',
|
|
'Laboratory professional', required=True, readonly=True)
|
|
lines = fields.One2Many('lims.notebook.load_results_manual.line',
|
|
None, 'Lines')
|
|
|
|
|
|
class NotebookLoadResultsManualLine(ModelSQL, ModelView):
|
|
'Load Results Manually'
|
|
__name__ = 'lims.notebook.load_results_manual.line'
|
|
|
|
line = fields.Many2One('lims.notebook.line', 'Analysis', readonly=True)
|
|
repetition = fields.Integer('Repetition', readonly=True)
|
|
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
|
|
result = fields.Char('Result')
|
|
result_modifier = fields.Selection([
|
|
('eq', '='),
|
|
('low', '<'),
|
|
('d', 'Detected'),
|
|
('nd', 'nd'),
|
|
('na', 'na'),
|
|
('pos', 'Positive'),
|
|
('neg', 'Negative'),
|
|
('ni', 'ni'),
|
|
('abs', 'Absence'),
|
|
('pre', 'Presence'),
|
|
], 'Result modifier', sort=False)
|
|
end_date = fields.Date('End date')
|
|
chromatogram = fields.Char('Chromatogram')
|
|
initial_unit = fields.Many2One('product.uom', 'Initial unit',
|
|
domain=[('category.lims_only_available', '=', True)])
|
|
comments = fields.Text('Comments')
|
|
literal_result = fields.Char('Literal result')
|
|
fraction_type = fields.Many2One('lims.fraction.type', 'Fraction type',
|
|
readonly=True)
|
|
session_id = fields.Integer('Session ID')
|
|
|
|
@classmethod
|
|
def __register__(cls, module_name):
|
|
super().__register__(module_name)
|
|
cursor = Transaction().connection.cursor()
|
|
cursor.execute('DELETE FROM "' + cls._table + '"')
|
|
|
|
@fields.depends('result', 'literal_result', 'result_modifier', 'end_date')
|
|
def on_change_with_end_date(self):
|
|
pool = Pool()
|
|
Date = pool.get('ir.date')
|
|
if self.end_date:
|
|
return self.end_date
|
|
if (self.result or self.literal_result or
|
|
self.result_modifier not in ('eq', 'low')):
|
|
return Date.today()
|
|
return None
|
|
|
|
|
|
class NotebookLoadResultsManualSit1(ModelView):
|
|
'Professionals Control'
|
|
__name__ = 'lims.notebook.load_results_manual.sit1'
|
|
|
|
msg = fields.Text('Message')
|
|
|
|
|
|
class NotebookLoadResultsManualSit2(ModelView):
|
|
'Professionals Control'
|
|
__name__ = 'lims.notebook.load_results_manual.sit2'
|
|
|
|
supervisor = fields.Many2One('lims.laboratory.professional',
|
|
'Supervisor', depends=['supervisor_domain'],
|
|
domain=[('id', 'in', Eval('supervisor_domain'))], required=True)
|
|
supervisor_domain = fields.Many2Many('lims.laboratory.professional',
|
|
None, None, 'Supervisor domain')
|
|
lines = fields.Many2Many('lims.notebook.line', None, None, 'Lines')
|
|
|
|
|
|
class NotebookLoadResultsManual(Wizard):
|
|
'Load Results Manually'
|
|
__name__ = 'lims.notebook.load_results_manual'
|
|
|
|
start = StateView('lims.notebook.load_results_manual.start',
|
|
'lims.lims_notebook_load_results_manual_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search', 'search', 'tryton-forward', default=True),
|
|
])
|
|
search = StateTransition()
|
|
empty = StateView('lims.notebook.load_results_manual.empty',
|
|
'lims.lims_notebook_load_results_manual_empty_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Search again', 'start', 'tryton-forward', default=True),
|
|
])
|
|
result = StateView('lims.notebook.load_results_manual.result',
|
|
'lims.lims_notebook_load_results_manual_result_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Confirm', 'check_professional', 'tryton-ok', default=True),
|
|
])
|
|
check_professional = StateTransition()
|
|
sit1 = StateView('lims.notebook.load_results_manual.sit1',
|
|
'lims.lims_notebook_load_results_manual_sit1_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel', default=True),
|
|
])
|
|
sit2 = StateView('lims.notebook.load_results_manual.sit2',
|
|
'lims.lims_notebook_load_results_manual_sit2_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Confirm', 'confirm_', 'tryton-ok', default=True),
|
|
])
|
|
confirm_ = StateTransition()
|
|
|
|
def transition_search(self):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
LaboratoryProfessional = pool.get('lims.laboratory.professional')
|
|
NotebookLoadResultsManualLine = pool.get(
|
|
'lims.notebook.load_results_manual.line')
|
|
|
|
clause = [
|
|
('end_date', '=', None),
|
|
('method', '=', self.start.method.id),
|
|
('start_date', '=', self.start.start_date),
|
|
]
|
|
|
|
lines = NotebookLine.search(clause, order=[
|
|
('analysis_order', 'ASC'), ('id', 'ASC')])
|
|
if lines:
|
|
res_lines = []
|
|
for line in lines:
|
|
res_line, = NotebookLoadResultsManualLine.create([{
|
|
'session_id': self._session_id,
|
|
'line': line.id,
|
|
'repetition': line.repetition,
|
|
'result': line.result,
|
|
'result_modifier': line.result_modifier,
|
|
'end_date': line.end_date,
|
|
'chromatogram': line.chromatogram,
|
|
'initial_unit': (line.initial_unit.id if
|
|
line.initial_unit else None),
|
|
'comments': line.comments,
|
|
'fraction': line.fraction.id,
|
|
'fraction_type': line.fraction_type.id,
|
|
'literal_result': line.literal_result,
|
|
}])
|
|
res_lines.append(res_line.id)
|
|
|
|
professional_id = LaboratoryProfessional.get_lab_professional()
|
|
self.result.method = self.start.method.id
|
|
self.result.start_date = self.start.start_date
|
|
self.result.professional = professional_id
|
|
self.result.lines = res_lines
|
|
if line.result_modifier == 'na':
|
|
self.result.annulled = True
|
|
self.result.annulment_date = datetime.now()
|
|
self.result.report = False
|
|
return 'result'
|
|
return 'empty'
|
|
|
|
def default_result(self, fields):
|
|
default = {}
|
|
default['method'] = self.result.method.id
|
|
default['start_date'] = self.result.start_date
|
|
default['professional'] = (self.result.professional.id
|
|
if self.result.professional else None)
|
|
default['lines'] = [l.id for l in self.result.lines]
|
|
return default
|
|
|
|
def transition_check_professional(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsManualLine = pool.get(
|
|
'lims.notebook.load_results_manual.line')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
|
|
lines_to_save = []
|
|
for line in self.result.lines:
|
|
if line.line: # Avoid empty lines created with ENTER key
|
|
lines_to_save.append(line)
|
|
NotebookLoadResultsManualLine.save(lines_to_save)
|
|
|
|
professional = self.result.professional
|
|
method = self.result.method
|
|
|
|
qualifications = LabProfessionalMethod.search([
|
|
('professional', '=', professional.id),
|
|
('method', '=', method.id),
|
|
('type', '=', 'analytical'),
|
|
])
|
|
if not qualifications:
|
|
msg = '%s: %s' % (professional.rec_name, method.code)
|
|
self.sit1.msg = msg
|
|
return 'sit1'
|
|
elif qualifications[0].state == 'training':
|
|
situation_2_lines = [l.line.id for l in lines_to_save]
|
|
supervisor_domain = []
|
|
qualifications = LabProfessionalMethod.search([
|
|
('method', '=', method.id),
|
|
('type', '=', 'analytical'),
|
|
('state', 'in', ('qualified', 'requalified')),
|
|
])
|
|
if qualifications:
|
|
supervisor_domain = [q.professional.id for q in qualifications]
|
|
|
|
self.sit2.lines = situation_2_lines
|
|
self.sit2.supervisor_domain = supervisor_domain
|
|
return 'sit2'
|
|
|
|
return 'confirm_'
|
|
|
|
def default_sit1(self, fields):
|
|
defaults = {}
|
|
if self.sit1.msg:
|
|
defaults['msg'] = self.sit1.msg
|
|
return defaults
|
|
|
|
def default_sit2(self, fields):
|
|
defaults = {}
|
|
if self.sit2.supervisor_domain:
|
|
defaults['supervisor_domain'] = [p.id for p in
|
|
self.sit2.supervisor_domain]
|
|
if self.sit2.lines:
|
|
defaults['lines'] = [l.id for l in self.sit2.lines]
|
|
return defaults
|
|
|
|
def transition_confirm_(self):
|
|
pool = Pool()
|
|
NotebookLoadResultsManualLine = pool.get(
|
|
'lims.notebook.load_results_manual.line')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
LabProfessionalMethod = pool.get('lims.lab.professional.method')
|
|
LabProfessionalMethodRequalification = pool.get(
|
|
'lims.lab.professional.method.requalification')
|
|
Date = pool.get('ir.date')
|
|
|
|
# Write Results to Notebook lines
|
|
actions = NotebookLoadResultsManualLine.search([
|
|
('session_id', '=', self._session_id),
|
|
])
|
|
|
|
for data in actions:
|
|
notebook_line = NotebookLine(data.line.id)
|
|
if not notebook_line:
|
|
continue
|
|
notebook_line_write = {
|
|
'result': data.result,
|
|
'result_modifier': data.result_modifier,
|
|
'end_date': data.end_date,
|
|
'chromatogram': data.chromatogram,
|
|
'initial_unit': (data.initial_unit.id if
|
|
data.initial_unit else None),
|
|
'comments': data.comments,
|
|
'literal_result': data.literal_result,
|
|
'converted_result': None,
|
|
'converted_result_modifier': 'eq',
|
|
'backup': None,
|
|
'verification': None,
|
|
'uncertainty': None,
|
|
}
|
|
if data.result_modifier == 'na':
|
|
notebook_line_write['annulled'] = True
|
|
notebook_line_write['annulment_date'] = datetime.now()
|
|
notebook_line_write['report'] = False
|
|
professionals = [{'professional': self.result.professional.id}]
|
|
notebook_line_write['professionals'] = (
|
|
[('delete', [p.id for p in notebook_line.professionals])] +
|
|
[('create', professionals)])
|
|
NotebookLine.write([notebook_line], notebook_line_write)
|
|
|
|
# Write Supervisors to Notebook lines
|
|
supervisor_lines = {}
|
|
if hasattr(self.sit2, 'supervisor'):
|
|
supervisor_lines[self.sit2.supervisor.id] = [
|
|
l.id for l in self.sit2.lines]
|
|
for prof_id, lines in supervisor_lines.items():
|
|
notebook_lines = NotebookLine.search([
|
|
('id', 'in', lines),
|
|
])
|
|
if notebook_lines:
|
|
professionals = [{'professional': prof_id}]
|
|
notebook_line_write = {
|
|
'professionals': [('create', professionals)],
|
|
}
|
|
NotebookLine.write(notebook_lines, notebook_line_write)
|
|
|
|
# Write the execution of method
|
|
all_prof = {}
|
|
key = (self.result.professional.id, self.result.method.id)
|
|
all_prof[key] = []
|
|
if hasattr(self.sit2, 'supervisor'):
|
|
for detail in self.sit2.lines:
|
|
key = (self.sit2.supervisor.id, detail.method.id)
|
|
if key not in all_prof:
|
|
all_prof[key] = []
|
|
key = (self.result.professional.id, detail.method.id)
|
|
if self.sit2.supervisor.id not in all_prof[key]:
|
|
all_prof[key].append(self.sit2.supervisor.id)
|
|
|
|
today = Date.today()
|
|
for key, sup in all_prof.items():
|
|
professional_method, = LabProfessionalMethod.search([
|
|
('professional', '=', key[0]),
|
|
('method', '=', key[1]),
|
|
('type', '=', 'analytical'),
|
|
])
|
|
if professional_method.state == 'training':
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'training'),
|
|
])
|
|
if history:
|
|
prev_supervisors = [s.supervisor.id for s in
|
|
history[0].supervisors]
|
|
supervisors = [{'supervisor': s} for s in sup
|
|
if s not in prev_supervisors]
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
'supervisors': [('create', supervisors)],
|
|
})
|
|
else:
|
|
supervisors = [{'supervisor': s} for s in sup]
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'training',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
'supervisors': [('create', supervisors)],
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
elif professional_method.state == 'qualified':
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'qualification'),
|
|
])
|
|
if history:
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
})
|
|
else:
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'qualification',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
else:
|
|
history = LabProfessionalMethodRequalification.search([
|
|
('professional_method', '=', professional_method.id),
|
|
('type', '=', 'requalification'),
|
|
])
|
|
if history:
|
|
LabProfessionalMethodRequalification.write(history, {
|
|
'last_execution_date': today,
|
|
})
|
|
else:
|
|
to_create = [{
|
|
'professional_method': professional_method.id,
|
|
'type': 'requalification',
|
|
'date': today,
|
|
'last_execution_date': today,
|
|
}]
|
|
LabProfessionalMethodRequalification.create(to_create)
|
|
|
|
return 'end'
|
|
|
|
|
|
class NotebookAddInternalRelationsStart(ModelView):
|
|
'Add Internal Relations'
|
|
__name__ = 'lims.notebook.add_internal_relations.start'
|
|
|
|
analysis = fields.Many2Many('lims.analysis', None, None,
|
|
'Internal relations', required=True,
|
|
domain=[('id', 'in', Eval('analysis_domain'))],
|
|
depends=['analysis_domain'])
|
|
analysis_domain = fields.One2Many('lims.analysis', None,
|
|
'Internal relations domain')
|
|
|
|
|
|
class NotebookAddInternalRelations(Wizard):
|
|
'Add Internal Relations'
|
|
__name__ = 'lims.notebook.add_internal_relations'
|
|
|
|
start = StateView('lims.notebook.add_internal_relations.start',
|
|
'lims.lims_notebook_add_internal_relations_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Add', 'add', 'tryton-ok', default=True),
|
|
])
|
|
add = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
Notebook = pool.get('lims.notebook')
|
|
Analysis = pool.get('lims.analysis')
|
|
Typification = pool.get('lims.typification')
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
default = {
|
|
'analysis_domain': [],
|
|
}
|
|
if not notebook.lines:
|
|
return default
|
|
|
|
notebook_analysis = []
|
|
for notebook_line in notebook.lines:
|
|
notebook_analysis.append(notebook_line.analysis.code)
|
|
notebook_analysis_codes = '\', \''.join(str(a) for a
|
|
in notebook_analysis)
|
|
|
|
cursor.execute('SELECT DISTINCT(a.id, a.result_formula) '
|
|
'FROM "' + Analysis._table + '" a '
|
|
'INNER JOIN "' + Typification._table + '" t '
|
|
'ON a.id = t.analysis '
|
|
'WHERE t.product_type = %s '
|
|
'AND t.matrix = %s '
|
|
'AND t.valid '
|
|
'AND a.behavior = \'internal_relation\' '
|
|
'AND a.code NOT IN (\'' + notebook_analysis_codes + '\')',
|
|
(notebook.fraction.product_type.id,
|
|
notebook.fraction.matrix.id))
|
|
internal_relations = cursor.fetchall()
|
|
if not internal_relations:
|
|
return default
|
|
|
|
for internal_relation in internal_relations:
|
|
formula = internal_relation[0].split(',')[1][:-1]
|
|
if not formula:
|
|
continue
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formula = formula.replace(i, '')
|
|
variables = self._get_variables(formula)
|
|
|
|
available = True
|
|
if variables:
|
|
for v in variables:
|
|
if v not in notebook_analysis:
|
|
available = False
|
|
break
|
|
if available:
|
|
ir_id = int(internal_relation[0].split(',')[0][1:])
|
|
default['analysis_domain'].append(ir_id)
|
|
|
|
return default
|
|
|
|
def _get_variables(self, formula):
|
|
variables = []
|
|
for prefix in ('A', 'D', 'T', 'Y', 'R'):
|
|
while True:
|
|
idx = formula.find(prefix)
|
|
if idx >= 0:
|
|
var = formula[idx:idx + 5]
|
|
formula = formula.replace(var, '_')
|
|
variables.append(var[1:])
|
|
else:
|
|
break
|
|
return variables
|
|
|
|
def transition_add(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
for analysis in self.start.analysis:
|
|
self._create_service(analysis, notebook.fraction)
|
|
return 'end'
|
|
|
|
def _create_service(self, analysis, fraction):
|
|
pool = Pool()
|
|
Typification = pool.get('lims.typification')
|
|
Service = pool.get('lims.service')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
|
|
divide, report_grouper = self._get_report_grouper(analysis)
|
|
|
|
laboratory_id = (analysis.laboratories[0].laboratory.id if
|
|
analysis.laboratories else None)
|
|
|
|
typifications = Typification.search([
|
|
('product_type', '=', fraction.product_type.id),
|
|
('matrix', '=', fraction.matrix.id),
|
|
('analysis', '=', analysis.id),
|
|
('by_default', '=', True),
|
|
('valid', '=', True),
|
|
])
|
|
method_id = (typifications[0].method.id if typifications
|
|
else None)
|
|
|
|
device_id = None
|
|
if analysis.devices:
|
|
for d in analysis.devices:
|
|
if (d.laboratory.id == laboratory_id and
|
|
d.by_default is True):
|
|
device_id = d.device.id
|
|
|
|
service_create = [{
|
|
'fraction': fraction.id,
|
|
'analysis': analysis.id,
|
|
'laboratory': laboratory_id,
|
|
'method': method_id,
|
|
'device': device_id,
|
|
'divide': divide,
|
|
}]
|
|
new_service, = Service.create(service_create)
|
|
Service.set_confirmation_date([new_service])
|
|
analysis_detail = list(new_service.analysis_detail)
|
|
if report_grouper != 0:
|
|
EntryDetailAnalysis.write(analysis_detail, {
|
|
'report_grouper': report_grouper,
|
|
})
|
|
|
|
EntryDetailAnalysis.create_notebook_lines(analysis_detail,
|
|
fraction)
|
|
EntryDetailAnalysis.write(analysis_detail, {
|
|
'state': 'unplanned',
|
|
})
|
|
|
|
def _get_report_grouper(self, analysis):
|
|
pool = Pool()
|
|
Notebook = pool.get('lims.notebook')
|
|
|
|
divide = False
|
|
report_grouper = 0
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
notebook_analysis = {}
|
|
for notebook_line in notebook.lines:
|
|
notebook_analysis[notebook_line.analysis.code] = notebook_line
|
|
|
|
formula = analysis.result_formula
|
|
for i in (' ', '\t', '\n', '\r'):
|
|
formula = formula.replace(i, '')
|
|
variables = self._get_variables(formula)
|
|
for v in variables:
|
|
if v in notebook_analysis:
|
|
divide = notebook_analysis[v].service.divide
|
|
report_grouper = (
|
|
notebook_analysis[v].analysis_detail.report_grouper)
|
|
break
|
|
return divide, report_grouper
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineRepeatAnalysisStart(ModelView):
|
|
'Repeat Analysis'
|
|
__name__ = 'lims.notebook.line.repeat_analysis.start'
|
|
|
|
analysis = fields.Many2One('lims.analysis', 'Analysis', required=True,
|
|
domain=[('id', 'in', Eval('analysis_domain'))],
|
|
depends=['analysis_domain'])
|
|
analysis_domain = fields.One2Many('lims.analysis', None,
|
|
'Analysis domain')
|
|
|
|
|
|
class NotebookLineRepeatAnalysis(Wizard):
|
|
'Repeat Analysis'
|
|
__name__ = 'lims.notebook.line.repeat_analysis'
|
|
|
|
start = StateView('lims.notebook.line.repeat_analysis.start',
|
|
'lims.lims_notebook_line_repeat_analysis_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Repeat', 'repeat', 'tryton-ok', default=True),
|
|
])
|
|
repeat = StateTransition()
|
|
|
|
@classmethod
|
|
def check_access(cls):
|
|
pass
|
|
|
|
def _get_notebook_line_id(self):
|
|
return Transaction().context.get('active_id', None)
|
|
|
|
def default_start(self, fields):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Analysis = pool.get('lims.analysis')
|
|
|
|
line_id = self._get_notebook_line_id()
|
|
notebook_line = NotebookLine(line_id)
|
|
|
|
analysis_origin = notebook_line.analysis_origin
|
|
analysis_origin_list = analysis_origin.split(' > ')
|
|
|
|
analysis_code = notebook_line.analysis.code
|
|
analysis_origin_list.append(analysis_code)
|
|
|
|
analysis = Analysis.search([
|
|
('code', 'in', analysis_origin_list),
|
|
('behavior', '=', 'normal'),
|
|
])
|
|
notebook_analysis = [a.id for a in analysis]
|
|
default = {
|
|
'analysis_domain': notebook_analysis,
|
|
}
|
|
if len(notebook_analysis) == 1:
|
|
default['analysis'] = notebook_analysis[0]
|
|
return default
|
|
|
|
def _unaccept_original(self):
|
|
return True
|
|
|
|
def transition_repeat(self):
|
|
pool = Pool()
|
|
Analysis = pool.get('lims.analysis')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Notebook = pool.get('lims.notebook')
|
|
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
|
|
Config = pool.get('lims.configuration')
|
|
|
|
analysis = self.start.analysis
|
|
if analysis.type == 'analysis':
|
|
analysis_to_repeat = [analysis.id]
|
|
else:
|
|
analysis_to_repeat = Analysis.get_included_analysis_analysis(
|
|
analysis.id)
|
|
|
|
line_id = self._get_notebook_line_id()
|
|
notebook_line = NotebookLine(line_id)
|
|
notebook = Notebook(notebook_line.notebook.id)
|
|
|
|
rm_type = (notebook.fraction.special_type == 'rm')
|
|
if rm_type:
|
|
config = Config(1)
|
|
rm_start_uom = (config.rm_start_uom.id if config.rm_start_uom
|
|
else None)
|
|
|
|
to_create = []
|
|
to_update = []
|
|
details_to_update = []
|
|
for analysis_id in analysis_to_repeat:
|
|
nlines = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('analysis', '=', analysis_id),
|
|
('analysis.behavior', '=', 'normal'),
|
|
])
|
|
if not nlines:
|
|
continue
|
|
nline_to_repeat = nlines[0]
|
|
for nline in nlines:
|
|
if nline.repetition > nline_to_repeat.repetition:
|
|
nline_to_repeat = nline
|
|
|
|
defaults = self._get_repetition_defaults(nline_to_repeat)
|
|
if rm_type:
|
|
defaults['final_concentration'] = None
|
|
defaults['initial_unit'] = rm_start_uom
|
|
defaults['final_unit'] = None
|
|
defaults['detection_limit'] = None
|
|
defaults['quantification_limit'] = None
|
|
defaults['lower_limit'] = None
|
|
defaults['upper_limit'] = None
|
|
to_create.append(defaults)
|
|
to_update.append(nline_to_repeat)
|
|
details_to_update.append(nline_to_repeat.analysis_detail.id)
|
|
|
|
Notebook.write([notebook], {
|
|
'lines': [('create', to_create)],
|
|
})
|
|
if self._unaccept_original():
|
|
NotebookLine.write(to_update, {
|
|
'accepted': False,
|
|
'acceptance_date': None,
|
|
'report': False,
|
|
})
|
|
|
|
details = EntryDetailAnalysis.search([
|
|
('id', 'in', details_to_update),
|
|
])
|
|
if details:
|
|
EntryDetailAnalysis.write(details, {
|
|
'state': 'unplanned',
|
|
})
|
|
|
|
return 'end'
|
|
|
|
def _get_repetition_defaults(self, line):
|
|
defaults = {
|
|
'analysis_detail': line.analysis_detail.id,
|
|
'service': line.service.id,
|
|
'analysis': line.analysis.id,
|
|
'analysis_origin': line.analysis_origin,
|
|
'urgent': line.urgent,
|
|
'repetition': line.repetition + 1,
|
|
'laboratory': line.laboratory.id,
|
|
'method': line.method.id,
|
|
'device': line.device.id if line.device else None,
|
|
'decimals': line.decimals,
|
|
'significant_digits': line.significant_digits,
|
|
'report': line.report,
|
|
'results_estimated_waiting': (
|
|
line.results_estimated_waiting),
|
|
'department': line.department,
|
|
'concentration_level': (line.concentration_level.id
|
|
if line.concentration_level else None),
|
|
'initial_concentration': line.initial_concentration,
|
|
'final_concentration': line.final_concentration,
|
|
'initial_unit': (line.initial_unit.id
|
|
if line.initial_unit else None),
|
|
'final_unit': line.final_unit.id if line.final_unit else None,
|
|
'detection_limit': line.detection_limit,
|
|
'quantification_limit': line.quantification_limit,
|
|
'lower_limit': line.lower_limit,
|
|
'upper_limit': line.upper_limit,
|
|
}
|
|
if line.accepted and not self._unaccept_original():
|
|
defaults['report'] = False
|
|
return defaults
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookAcceptLinesStart(ModelView):
|
|
'Accept Lines'
|
|
__name__ = 'lims.notebook.accept_lines.start'
|
|
|
|
|
|
class NotebookAcceptLines(Wizard):
|
|
'Accept Lines'
|
|
__name__ = 'lims.notebook.accept_lines'
|
|
|
|
start_state = 'ok'
|
|
start = StateView('lims.notebook.accept_lines.start',
|
|
'lims.lims_notebook_accept_lines_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
pool = Pool()
|
|
Notebook = pool.get('lims.notebook')
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
|
|
repeated_analysis = []
|
|
repetitions = NotebookLine.search([
|
|
('notebook', '=', notebook.id),
|
|
('repetition', '>', 0),
|
|
])
|
|
if repetitions:
|
|
repeated_analysis = [l.analysis.id for l in repetitions]
|
|
notebook_lines = [l for l in notebook.lines
|
|
if l.analysis.id not in repeated_analysis]
|
|
|
|
self.lines_accept(notebook_lines)
|
|
return 'end'
|
|
|
|
def lines_accept(self, notebook_lines):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
accepted_analysis = {}
|
|
lines_to_write = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
if not notebook_line.report:
|
|
continue
|
|
if notebook_line.annulled:
|
|
continue
|
|
if not notebook_line.end_date:
|
|
continue
|
|
if not (notebook_line.result or notebook_line.converted_result or
|
|
notebook_line.literal_result or
|
|
notebook_line.result_modifier in
|
|
('d', 'nd', 'pos', 'neg', 'ni', 'abs', 'pre') or
|
|
notebook_line.converted_result_modifier in
|
|
('d', 'nd', 'pos', 'neg', 'ni', 'abs', 'pre')):
|
|
continue
|
|
if (notebook_line.converted_result and
|
|
notebook_line.converted_result_modifier
|
|
not in ('ni', 'eq', 'low')):
|
|
continue
|
|
if (notebook_line.result and notebook_line.result_modifier
|
|
not in ('ni', 'eq', 'low')):
|
|
continue
|
|
|
|
notebook_id = notebook_line.notebook.id
|
|
if notebook_id not in accepted_analysis:
|
|
accepted_lines = NotebookLine.search([
|
|
('notebook', '=', notebook_id),
|
|
('accepted', '=', True),
|
|
])
|
|
accepted_analysis[notebook_id] = [l.analysis.id
|
|
for l in accepted_lines]
|
|
if notebook_line.analysis.id in accepted_analysis[notebook_id]:
|
|
continue
|
|
|
|
accepted_analysis[notebook_id].append(notebook_line.analysis.id)
|
|
lines_to_write.append(notebook_line)
|
|
|
|
if lines_to_write:
|
|
acceptance_date = datetime.now()
|
|
NotebookLine.write(lines_to_write, {
|
|
'accepted': True,
|
|
'acceptance_date': acceptance_date,
|
|
})
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineAcceptLines(NotebookAcceptLines):
|
|
'Accept Lines'
|
|
__name__ = 'lims.notebook_line.accept_lines'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_accept(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class NotebookLineUnacceptLines(Wizard):
|
|
'Unaccept Lines'
|
|
__name__ = 'lims.notebook_line.unaccept_lines'
|
|
|
|
start_state = 'ok'
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_unaccept(notebook_lines)
|
|
return 'end'
|
|
|
|
def lines_unaccept(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
ResultsLine = pool.get('lims.results_report.version.detail.line')
|
|
|
|
lines_to_write = []
|
|
for notebook_line in notebook_lines:
|
|
if not notebook_line.accepted:
|
|
continue
|
|
report_lines = ResultsLine.search([
|
|
('notebook_line', '=', notebook_line.id),
|
|
('detail_sample.version_detail.state', '!=', 'annulled'),
|
|
])
|
|
if report_lines:
|
|
continue
|
|
|
|
lines_to_write.append(notebook_line)
|
|
|
|
if lines_to_write:
|
|
NotebookLine.write(lines_to_write, {
|
|
'accepted': False,
|
|
'acceptance_date': None,
|
|
})
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookResultsVerificationStart(ModelView):
|
|
'Results Verification'
|
|
__name__ = 'lims.notebook.results_verification.start'
|
|
|
|
range_type = fields.Many2One('lims.range.type', 'Origin', required=True,
|
|
domain=[('use', '=', 'results_verification')])
|
|
|
|
|
|
class NotebookResultsVerification(Wizard):
|
|
'Results Verification'
|
|
__name__ = 'lims.notebook.results_verification'
|
|
|
|
start = StateView('lims.notebook.results_verification.start',
|
|
'lims.lims_notebook_results_verification_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def default_start(self, fields):
|
|
RangeType = Pool().get('lims.range.type')
|
|
|
|
default = {}
|
|
default_range_type = RangeType.search([
|
|
('use', '=', 'results_verification'),
|
|
('by_default', '=', True),
|
|
])
|
|
if default_range_type:
|
|
default['range_type'] = default_range_type[0].id
|
|
return default
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
self.lines_results_verification(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_results_verification(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Range = pool.get('lims.range')
|
|
UomConversion = pool.get('lims.uom.conversion')
|
|
VolumeConversion = pool.get('lims.volume.conversion')
|
|
|
|
verifications = self._get_verifications()
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
|
|
result = notebook_line.converted_result
|
|
if not result:
|
|
result = notebook_line.result
|
|
iu = notebook_line.initial_unit
|
|
if not iu:
|
|
continue
|
|
try:
|
|
ic = float(notebook_line.initial_concentration)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
else:
|
|
iu = notebook_line.final_unit
|
|
if not iu:
|
|
continue
|
|
try:
|
|
ic = float(notebook_line.final_concentration)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
try:
|
|
result = float(result)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
ranges = Range.search([
|
|
('range_type', '=', self.start.range_type),
|
|
('analysis', '=', notebook_line.analysis.id),
|
|
('product_type', '=', notebook_line.notebook.product_type.id),
|
|
('matrix', '=', notebook_line.notebook.matrix.id),
|
|
])
|
|
if not ranges:
|
|
continue
|
|
fu = ranges[0].uom
|
|
try:
|
|
fc = float(ranges[0].concentration)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
if fu and fu.rec_name != '-':
|
|
converted_result = None
|
|
if (iu == fu and ic == fc):
|
|
converted_result = result
|
|
elif (iu != fu and ic == fc):
|
|
formula = UomConversion.get_conversion_formula(iu,
|
|
fu)
|
|
if not formula:
|
|
continue
|
|
variables = self._get_variables(formula, notebook_line)
|
|
parser = FormulaParser(formula, variables)
|
|
formula_result = parser.getValue()
|
|
|
|
converted_result = result * formula_result
|
|
elif (iu == fu and ic != fc):
|
|
converted_result = result * (fc / ic)
|
|
else:
|
|
formula = None
|
|
conversions = UomConversion.search([
|
|
('initial_uom', '=', iu),
|
|
('final_uom', '=', fu),
|
|
])
|
|
if conversions:
|
|
formula = conversions[0].conversion_formula
|
|
if not formula:
|
|
continue
|
|
variables = self._get_variables(formula, notebook_line)
|
|
parser = FormulaParser(formula, variables)
|
|
formula_result = parser.getValue()
|
|
|
|
if (conversions[0].initial_uom_volume and
|
|
conversions[0].final_uom_volume):
|
|
d_ic = VolumeConversion.brixToDensity(ic)
|
|
d_fc = VolumeConversion.brixToDensity(fc)
|
|
converted_result = (result * (fc / ic) *
|
|
(d_fc / d_ic) * formula_result)
|
|
else:
|
|
converted_result = (result * (fc / ic) *
|
|
formula_result)
|
|
result = float(converted_result)
|
|
|
|
verification = self._verificate_result(result, ranges[0])
|
|
notebook_line.verification = verifications.get(verification)
|
|
lines_to_save.append(notebook_line)
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def _get_variables(self, formula, notebook_line):
|
|
VolumeConversion = Pool().get('lims.volume.conversion')
|
|
|
|
variables = {}
|
|
for var in ('DI',):
|
|
while True:
|
|
idx = formula.find(var)
|
|
if idx >= 0:
|
|
variables[var] = 0
|
|
formula = formula.replace(var, '_')
|
|
else:
|
|
break
|
|
for var in variables.keys():
|
|
if var == 'DI':
|
|
ic = float(notebook_line.final_concentration)
|
|
result = VolumeConversion.brixToDensity(ic)
|
|
if result:
|
|
variables[var] = result
|
|
return variables
|
|
|
|
def _verificate_result(self, result, range_):
|
|
if range_.min95 and range_.max95:
|
|
if result < range_.min:
|
|
return 'out'
|
|
elif result < range_.min95:
|
|
return 'ok*'
|
|
elif result <= range_.max95:
|
|
return 'ok'
|
|
elif result <= range_.max:
|
|
return 'ok*'
|
|
else:
|
|
return 'out'
|
|
else:
|
|
if (range_.min and result < range_.min):
|
|
return 'out'
|
|
elif (range_.max and result <= range_.max):
|
|
return 'ok'
|
|
else:
|
|
return 'out'
|
|
|
|
def _get_verifications(self):
|
|
pool = Pool()
|
|
User = pool.get('res.user')
|
|
Lang = pool.get('ir.lang')
|
|
|
|
lang = User(Transaction().user).language
|
|
if not lang:
|
|
lang, = Lang.search([
|
|
('code', '=', 'en'),
|
|
], limit=1)
|
|
|
|
verifications = {}
|
|
with Transaction().set_context(language=lang.code):
|
|
verifications['ok'] = gettext('lims.msg_ok')
|
|
verifications['ok*'] = gettext('lims.msg_ok*')
|
|
verifications['out'] = gettext('lims.msg_out')
|
|
|
|
return verifications
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineResultsVerification(NotebookResultsVerification):
|
|
'Results Verification'
|
|
__name__ = 'lims.notebook_line.results_verification'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_results_verification(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class UncertaintyCalcStart(ModelView):
|
|
'Uncertainty Calculation'
|
|
__name__ = 'lims.notebook.uncertainty_calc.start'
|
|
|
|
range_type = fields.Many2One('lims.range.type', 'Origin', required=True,
|
|
domain=[('use', '=', 'uncertainty_calc')])
|
|
|
|
|
|
class UncertaintyCalc(Wizard):
|
|
'Uncertainty Calculation'
|
|
__name__ = 'lims.notebook.uncertainty_calc'
|
|
|
|
start_state = 'check'
|
|
check = StateTransition()
|
|
start = StateView('lims.notebook.uncertainty_calc.start',
|
|
'lims.lims_notebook_uncertainty_calc_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_check(self):
|
|
RangeType = Pool().get('lims.range.type')
|
|
|
|
default_range_type = RangeType.search([
|
|
('use', '=', 'uncertainty_calc'),
|
|
('by_default', '=', True),
|
|
])
|
|
if default_range_type:
|
|
self.start.range_type = default_range_type[0].id
|
|
return 'ok'
|
|
return 'start'
|
|
|
|
def default_start(self, fields):
|
|
RangeType = Pool().get('lims.range.type')
|
|
|
|
default = {}
|
|
default_range_type = RangeType.search([
|
|
('use', '=', 'uncertainty_calc'),
|
|
('by_default', '=', True),
|
|
])
|
|
if default_range_type:
|
|
default['range_type'] = default_range_type[0].id
|
|
return default
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
if not notebook.lines:
|
|
continue
|
|
self.lines_uncertainty_calc(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_uncertainty_calc(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Range = pool.get('lims.range')
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.accepted:
|
|
continue
|
|
result = notebook_line.converted_result
|
|
if not result:
|
|
result = notebook_line.result
|
|
try:
|
|
result = float(result)
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
ranges = Range.search([
|
|
('range_type', '=', self.start.range_type),
|
|
('analysis', '=', notebook_line.analysis.id),
|
|
('product_type', '=', notebook_line.notebook.product_type.id),
|
|
('matrix', '=', notebook_line.notebook.matrix.id),
|
|
])
|
|
if not ranges:
|
|
continue
|
|
|
|
uncertainty = self._get_uncertainty(result, notebook_line,
|
|
ranges[0])
|
|
if uncertainty is None:
|
|
continue
|
|
notebook_line.uncertainty = str(uncertainty)
|
|
lines_to_save.append(notebook_line)
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def _get_uncertainty(self, result, notebook_line, range_):
|
|
dilution_factor = notebook_line.dilution_factor
|
|
if not dilution_factor or dilution_factor == 0.0:
|
|
dilution_factor = 1.0
|
|
diluted_result = result / dilution_factor
|
|
try:
|
|
factor = range_.factor or 1.0
|
|
low_level = range_.low_level or 0.0 * factor
|
|
middle_level = range_.middle_level or 0.0 * factor
|
|
high_level = range_.high_level or 0.0 * factor
|
|
except TypeError:
|
|
return None
|
|
|
|
uncertainty = 0.0
|
|
if (range_.low_level_value and
|
|
not (range_.middle_level_value and range_.high_level_value) and
|
|
(diluted_result > low_level)):
|
|
uncertainty = range_.low_level_value
|
|
elif (range_.low_level_value and range_.middle_level_value and
|
|
range_.high_level_value):
|
|
if (low_level <= diluted_result and
|
|
diluted_result < middle_level):
|
|
uncertainty = range_.low_level_value
|
|
elif (middle_level <= diluted_result and
|
|
diluted_result < high_level):
|
|
uncertainty = range_.middle_level_value
|
|
elif diluted_result >= high_level:
|
|
uncertainty = range_.high_level_value
|
|
if (range_.low_level_value and range_.middle_level_value and not
|
|
range_.high_level_value):
|
|
if (low_level <= diluted_result and
|
|
diluted_result < middle_level):
|
|
uncertainty = range_.low_level_value
|
|
elif (middle_level <= diluted_result and
|
|
diluted_result < high_level):
|
|
uncertainty = range_.middle_level_value
|
|
|
|
if uncertainty > 0.0:
|
|
uncertainty = result * uncertainty / 100
|
|
|
|
return uncertainty
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineUncertaintyCalc(UncertaintyCalc):
|
|
'Uncertainty Calculation'
|
|
__name__ = 'lims.notebook_line.uncertainty_calc'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_uncertainty_calc(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class NotebookPrecisionControlStart(ModelView):
|
|
'Precision Control'
|
|
__name__ = 'lims.notebook.precision_control.start'
|
|
|
|
range_type = fields.Many2One('lims.range.type', 'Origin', required=True,
|
|
domain=[('use', '=', 'repeatability_calc')])
|
|
product_type = fields.Many2One('lims.product.type', 'Product type',
|
|
required=True)
|
|
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
|
|
domain=[
|
|
('id', 'in', Eval('matrix_domain')),
|
|
], depends=['matrix_domain'])
|
|
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
|
|
None, None, 'Matrix domain'),
|
|
'on_change_with_matrix_domain')
|
|
factor = fields.Float('Factor', required=True)
|
|
|
|
@fields.depends('product_type')
|
|
def on_change_with_matrix_domain(self, name=None):
|
|
cursor = Transaction().connection.cursor()
|
|
Typification = Pool().get('lims.typification')
|
|
|
|
if not self.product_type:
|
|
return []
|
|
|
|
cursor.execute('SELECT DISTINCT(matrix) '
|
|
'FROM "' + Typification._table + '" '
|
|
'WHERE product_type = %s '
|
|
'AND valid',
|
|
(self.product_type.id,))
|
|
res = cursor.fetchall()
|
|
if not res:
|
|
return []
|
|
return [x[0] for x in res]
|
|
|
|
|
|
class NotebookPrecisionControl(Wizard):
|
|
'Precision Control'
|
|
__name__ = 'lims.notebook.precision_control'
|
|
|
|
start_state = 'check'
|
|
check = StateTransition()
|
|
start = StateView('lims.notebook.precision_control.start',
|
|
'lims.lims_notebook_precision_control_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_check(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
if notebook.fraction.special_type in ('rm', 'con'):
|
|
return 'start'
|
|
return 'end'
|
|
|
|
def default_start(self, fields):
|
|
pool = Pool()
|
|
Notebook = pool.get('lims.notebook')
|
|
RangeType = pool.get('lims.range.type')
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
default = {
|
|
'product_type': notebook.product_type.id,
|
|
'matrix': notebook.matrix.id,
|
|
'factor': 2,
|
|
}
|
|
default_range_type = RangeType.search([
|
|
('use', '=', 'repeatability_calc'),
|
|
('by_default', '=', True),
|
|
])
|
|
if default_range_type:
|
|
default['range_type'] = default_range_type[0].id
|
|
return default
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
notebook = Notebook(Transaction().context['active_id'])
|
|
if not notebook.lines:
|
|
return 'end'
|
|
|
|
self.lines_precision_control(notebook.lines)
|
|
return 'end'
|
|
|
|
def lines_precision_control(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
Range = pool.get('lims.range')
|
|
|
|
lines_to_save = []
|
|
for notebook_line in notebook_lines:
|
|
if notebook_line.verification:
|
|
continue
|
|
concentration_level = notebook_line.concentration_level
|
|
if not concentration_level:
|
|
continue
|
|
|
|
ranges = Range.search([
|
|
('range_type', '=', self.start.range_type.id),
|
|
('analysis', '=', notebook_line.analysis.id),
|
|
('product_type', '=', self.start.product_type.id),
|
|
('matrix', '=', self.start.matrix.id),
|
|
])
|
|
if not ranges:
|
|
continue
|
|
|
|
if concentration_level.code == 'NC':
|
|
cv = ranges[0].low_level_coefficient_variation
|
|
elif concentration_level.code == 'NM':
|
|
cv = ranges[0].middle_level_coefficient_variation
|
|
elif concentration_level.code == 'NA':
|
|
cv = ranges[0].high_level_coefficient_variation
|
|
else:
|
|
continue
|
|
if not cv:
|
|
continue
|
|
|
|
try:
|
|
if notebook_line.repetition == 0:
|
|
rep_0 = float(notebook_line.result)
|
|
rep_1 = float(self._get_repetition_result(notebook_line,
|
|
1))
|
|
elif notebook_line.repetition == 1:
|
|
rep_0 = float(self._get_repetition_result(notebook_line,
|
|
0))
|
|
rep_1 = float(notebook_line.result)
|
|
else:
|
|
continue
|
|
except (TypeError, ValueError):
|
|
continue
|
|
|
|
if not rep_0 or not rep_1:
|
|
continue
|
|
|
|
average = (rep_0 + rep_1) / 2
|
|
error = abs(rep_0 - rep_1) / average * 100
|
|
|
|
if error < (cv * self.start.factor):
|
|
res = gettext(
|
|
'lims.msg_acceptable', factor=self.start.factor, cv=cv)
|
|
else:
|
|
res = gettext(
|
|
'lims.msg_unacceptable', factor=self.start.factor, cv=cv)
|
|
notebook_line.verification = res
|
|
lines_to_save.append(notebook_line)
|
|
if lines_to_save:
|
|
NotebookLine.save(lines_to_save)
|
|
|
|
def _get_repetition_result(self, notebook_line, repetition):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
repetition = NotebookLine.search([
|
|
('notebook', '=', notebook_line.notebook.id),
|
|
('analysis', '=', notebook_line.analysis.id),
|
|
('repetition', '=', repetition),
|
|
])
|
|
if not repetition:
|
|
return None
|
|
return repetition[0].result
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLinePrecisionControl(NotebookPrecisionControl):
|
|
'Precision Control'
|
|
__name__ = 'lims.notebook_line.precision_control'
|
|
|
|
def transition_check(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
reference_line = NotebookLine(Transaction().context['active_id'])
|
|
if reference_line.notebook.fraction.special_type in ('rm', 'con'):
|
|
return 'start'
|
|
return 'end'
|
|
|
|
def default_start(self, fields):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
reference_line = NotebookLine(Transaction().context['active_id'])
|
|
with Transaction().set_context(active_id=reference_line.notebook.id):
|
|
return super().default_start(fields)
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
reference_line = NotebookLine(Transaction().context['active_id'])
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
notebook_lines = [l for l in notebook_lines
|
|
if l.notebook.id == reference_line.notebook.id]
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.lines_precision_control(notebook_lines)
|
|
return 'end'
|
|
|
|
|
|
class OpenNotebookLines(Wizard):
|
|
'Open Notebook Lines'
|
|
__name__ = 'lims.open_notebook_lines'
|
|
start_state = 'open_'
|
|
open_ = StateAction('lims.act_lims_notebook_line_related1')
|
|
|
|
def do_open_(self, action):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
notebook = Notebook.browse(Transaction().context['active_ids'])[0]
|
|
action['pyson_domain'] = PYSONEncoder().encode(
|
|
[('notebook', 'in', Transaction().context['active_ids'])])
|
|
|
|
action['name'] = \
|
|
'%s - %s - %s - %s - %s' % (notebook.fraction.number,
|
|
notebook.party.name, notebook.product_type.description,
|
|
notebook.matrix.description, notebook.label)
|
|
return action, {}
|
|
|
|
|
|
class ChangeEstimatedDaysForResultsStart(ModelView):
|
|
'Change Estimated Days For Results'
|
|
__name__ = 'lims.change_results_estimated_waiting.start'
|
|
|
|
date_from = fields.Date('Confirmation date From', required=True)
|
|
date_to = fields.Date('Confirmation date To', required=True)
|
|
methods = fields.Many2Many('lims.lab.method',
|
|
None, None, 'Methods', required=True)
|
|
results_estimated_waiting = fields.Integer('Days to add')
|
|
party = fields.Many2One('party.party', 'Party')
|
|
|
|
|
|
class ChangeEstimatedDaysForResults(Wizard):
|
|
'Change Estimated Days For Results'
|
|
__name__ = 'lims.change_results_estimated_waiting'
|
|
|
|
start = StateView('lims.change_results_estimated_waiting.start',
|
|
'lims.change_results_estimated_waiting_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Change', 'change', 'tryton-ok', default=True),
|
|
])
|
|
change = StateTransition()
|
|
|
|
def transition_change(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
methods_ids = [m.id for m in self.start.methods]
|
|
clause = [('method', 'in', methods_ids)]
|
|
clause.append(('analysis_detail.confirmation_date',
|
|
'>=', self.start.date_from))
|
|
clause.append(('analysis_detail.confirmation_date',
|
|
'<=', self.start.date_to))
|
|
clause.append(('accepted', '=', False))
|
|
party_id = self.start.party and self.start.party.id or None
|
|
if party_id:
|
|
clause.append(('party', '=', party_id))
|
|
|
|
notebook_lines = NotebookLine.search(clause)
|
|
if notebook_lines:
|
|
lines_to_save = []
|
|
for line in notebook_lines:
|
|
line.results_estimated_waiting = ((
|
|
line.results_estimated_waiting or 0) +
|
|
self.start.results_estimated_waiting)
|
|
lines_to_save.append(line)
|
|
NotebookLine.save(lines_to_save)
|
|
return 'end'
|
|
|
|
|
|
class PrintAnalysisPendingInformStart(ModelView):
|
|
'Analysis Pending of Inform'
|
|
__name__ = 'lims.print_analysis_pending_inform.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')
|
|
include_comments_of_fraction = fields.Boolean(
|
|
'Include comments of the fraction')
|
|
|
|
|
|
class PrintAnalysisPendingInform(Wizard):
|
|
'Analysis Pending of Inform'
|
|
__name__ = 'lims.print_analysis_pending_inform'
|
|
|
|
start = StateView('lims.print_analysis_pending_inform.start',
|
|
'lims.print_analysis_pending_inform_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-print', default=True),
|
|
])
|
|
print_ = StateReport('lims.analysis_pending_inform')
|
|
|
|
def do_print_(self, action):
|
|
data = {
|
|
'date_from': self.start.date_from,
|
|
'date_to': self.start.date_to,
|
|
'laboratory': self.start.laboratory.id,
|
|
'party': self.start.party and self.start.party.id or None,
|
|
'include_comments_of_fraction': (
|
|
self.start.include_comments_of_fraction),
|
|
}
|
|
return action, data
|
|
|
|
def transition_print_(self):
|
|
return 'end'
|
|
|
|
|
|
class AnalysisPendingInform(Report):
|
|
'Analysis Pending of Inform'
|
|
__name__ = 'lims.analysis_pending_inform'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Laboratory = pool.get('lims.laboratory')
|
|
Party = pool.get('party.party')
|
|
|
|
report_context = super().get_context(records, data)
|
|
|
|
today = get_print_date()
|
|
report_context['today'] = today
|
|
report_context['company'] = report_context['user'].company
|
|
report_context['date_from'] = data['date_from']
|
|
report_context['date_to'] = data['date_to']
|
|
report_context['laboratory'] = Laboratory(data['laboratory']).rec_name
|
|
report_context['party'] = ''
|
|
|
|
if data['party']:
|
|
report_context['party'] = Party(data['party']).rec_name
|
|
report_context['include_comments_of_fraction'] = \
|
|
data['include_comments_of_fraction']
|
|
|
|
objects = cls._get_report_records(data['date_from'], data['date_to'],
|
|
data['laboratory'], data['party'])
|
|
|
|
report_context['records'] = objects
|
|
|
|
return report_context
|
|
|
|
@classmethod
|
|
def _get_report_records(cls, date_from, date_to, laboratory, party):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
res = []
|
|
|
|
excluded_notebooks = cls._get_excluded_notebooks(date_from, date_to,
|
|
laboratory, party)
|
|
if excluded_notebooks:
|
|
for n_id, a_ids in excluded_notebooks.items():
|
|
clause = [
|
|
('notebook.id', '=', n_id),
|
|
('analysis', 'in', a_ids),
|
|
('laboratory', '=', laboratory),
|
|
('report', '=', True),
|
|
('annulled', '=', False),
|
|
]
|
|
excluded_lines = NotebookLine.search(clause)
|
|
if excluded_lines:
|
|
res.extend(excluded_lines)
|
|
return res
|
|
|
|
@classmethod
|
|
def _get_excluded_notebooks(cls, date_from, date_to, laboratory, party):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
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 party:
|
|
party_clause = 'AND e.party = ' + str(party)
|
|
|
|
cursor.execute('SELECT nl.notebook, nl.analysis, nl.accepted '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'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,
|
|
(date_from, date_to, laboratory,))
|
|
notebook_lines = cursor.fetchall()
|
|
|
|
# Check accepted repetitions
|
|
to_check = []
|
|
oks = []
|
|
for line in notebook_lines:
|
|
key = (line[0], line[1])
|
|
if not line[2]:
|
|
to_check.append(key)
|
|
else:
|
|
oks.append(key)
|
|
|
|
to_check = list(set(to_check) - set(oks))
|
|
|
|
excluded_notebooks = {}
|
|
for n_id, a_id in to_check:
|
|
if n_id not in excluded_notebooks:
|
|
excluded_notebooks[n_id] = []
|
|
excluded_notebooks[n_id].append(a_id)
|
|
return excluded_notebooks
|
|
|
|
|
|
class PrintAnalysisCheckedPendingInformStart(ModelView):
|
|
'Analysis checked pending of Inform'
|
|
__name__ = 'lims.print_analysis_checked_pending_inform.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')
|
|
|
|
|
|
class PrintAnalysisCheckedPendingInform(Wizard):
|
|
'Analysis Checked pending of Inform'
|
|
__name__ = 'lims.print_analysis_checked_pending_inform'
|
|
|
|
start = StateView('lims.print_analysis_checked_pending_inform.start',
|
|
'lims.print_analysis_checked_pending_inform_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Print', 'print_', 'tryton-print', default=True),
|
|
])
|
|
print_ = StateReport('lims.analysis_checked_pending_inform')
|
|
|
|
def do_print_(self, action):
|
|
data = {
|
|
'date_from': self.start.date_from,
|
|
'date_to': self.start.date_to,
|
|
'laboratory': self.start.laboratory.id,
|
|
'party': self.start.party and self.start.party.id or None,
|
|
}
|
|
return action, data
|
|
|
|
def transition_print_(self):
|
|
return 'end'
|
|
|
|
|
|
class AnalysisCheckedPendingInform(Report):
|
|
'Analysis checked pending of Inform'
|
|
__name__ = 'lims.analysis_checked_pending_inform'
|
|
|
|
@classmethod
|
|
def get_context(cls, records, data):
|
|
pool = Pool()
|
|
Laboratory = pool.get('lims.laboratory')
|
|
Party = pool.get('party.party')
|
|
|
|
report_context = super().get_context(records, data)
|
|
|
|
today = get_print_date()
|
|
report_context['today'] = today
|
|
report_context['company'] = report_context['user'].company
|
|
report_context['date_from'] = data['date_from']
|
|
report_context['date_to'] = data['date_to']
|
|
report_context['laboratory'] = Laboratory(data['laboratory']).rec_name
|
|
report_context['party'] = ''
|
|
if data['party']:
|
|
report_context['party'] = Party(data['party']).rec_name
|
|
|
|
objects = cls._get_report_records(data['date_from'], data['date_to'],
|
|
data['laboratory'], data['party'])
|
|
|
|
report_context['records'] = objects
|
|
return report_context
|
|
|
|
@classmethod
|
|
def _get_report_records(cls, date_from, date_to, laboratory, party):
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
|
|
res = []
|
|
|
|
included_notebooks = cls._get_included_notebooks(date_from, date_to,
|
|
laboratory, party)
|
|
if included_notebooks:
|
|
for n_id, a_ids in included_notebooks:
|
|
clause = [
|
|
('notebook.id', '=', n_id),
|
|
('analysis', 'in', [a_ids]),
|
|
('laboratory', '=', laboratory),
|
|
('report', '=', True),
|
|
('annulled', '=', False),
|
|
]
|
|
included_lines = NotebookLine.search(clause)
|
|
if included_lines:
|
|
res.extend(line for line in included_lines)
|
|
return res
|
|
|
|
@classmethod
|
|
def _get_included_notebooks(cls, date_from, date_to, laboratory, party):
|
|
cursor = Transaction().connection.cursor()
|
|
pool = Pool()
|
|
NotebookLine = pool.get('lims.notebook.line')
|
|
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 party:
|
|
party_clause = 'AND e.party = ' + str(party)
|
|
|
|
cursor.execute('SELECT nl.notebook, nl.analysis '
|
|
'FROM "' + NotebookLine._table + '" nl '
|
|
'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.accepted = TRUE '
|
|
'AND nl.results_report IS NULL '
|
|
'AND nl.annulled = FALSE ' +
|
|
party_clause,
|
|
(date_from, date_to, laboratory,))
|
|
|
|
notebook_lines = cursor.fetchall()
|
|
return notebook_lines
|
|
|
|
|
|
class NotebookEvaluateRulesStart(ModelView):
|
|
'Evaluate Rules'
|
|
__name__ = 'lims.notebook.evaluate_rules.start'
|
|
|
|
|
|
class NotebookEvaluateRules(Wizard):
|
|
'Evaluate Rules'
|
|
__name__ = 'lims.notebook.evaluate_rules'
|
|
|
|
start_state = 'ok'
|
|
start = StateView('lims.notebook.evaluate_rules.start',
|
|
'lims.notebook_evaluate_rules_start_view_form', [
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
Button('Ok', 'ok', 'tryton-ok', default=True),
|
|
])
|
|
ok = StateTransition()
|
|
|
|
def transition_ok(self):
|
|
Notebook = Pool().get('lims.notebook')
|
|
|
|
for active_id in Transaction().context['active_ids']:
|
|
notebook = Notebook(active_id)
|
|
self.evaluate_rules(notebook.lines)
|
|
return 'end'
|
|
|
|
def evaluate_rules(self, notebook_lines):
|
|
pool = Pool()
|
|
NotebookRule = pool.get('lims.rule')
|
|
|
|
for line in notebook_lines:
|
|
rules = NotebookRule.search([
|
|
('analysis', '=', line.analysis),
|
|
])
|
|
for rule in rules:
|
|
if rule.eval_condition(line):
|
|
rule.exec_action(line)
|
|
|
|
def end(self):
|
|
return 'reload'
|
|
|
|
|
|
class NotebookLineEvaluateRules(NotebookEvaluateRules):
|
|
'Evaluate Rules'
|
|
__name__ = 'lims.notebook_line.evaluate_rules'
|
|
|
|
def transition_ok(self):
|
|
NotebookLine = Pool().get('lims.notebook.line')
|
|
|
|
notebook_lines = NotebookLine.browse(
|
|
Transaction().context['active_ids'])
|
|
if not notebook_lines:
|
|
return 'end'
|
|
|
|
self.evaluate_rules(notebook_lines)
|
|
return 'end'
|