kalenislims/lims/planification.py

6651 lines
266 KiB
Python
Raw Normal View History

2017-10-08 02:23:22 +02:00
# -*- coding: utf-8 -*-
# This file is part of lims module for Tryton.
2017-10-08 02:23:22 +02:00
# The COPYRIGHT file at the top level of this repository contains
# the full copyright notices and license terms.
import logging
2017-10-08 02:23:22 +02:00
from datetime import datetime, date
from dateutil.relativedelta import relativedelta
from trytond.model import Workflow, ModelView, ModelSQL, fields, Unique
2017-10-08 02:23:22 +02:00
from trytond.wizard import Wizard, StateTransition, StateView, StateAction, \
Button
from trytond.report import Report
from trytond.pool import Pool
2017-10-08 02:23:22 +02:00
from trytond.transaction import Transaction
from trytond.pyson import PYSONEncoder, Eval, Equal, Bool, Not, Or
2019-07-23 23:27:33 +02:00
from trytond.exceptions import UserError
from trytond.i18n import gettext
2018-11-24 22:23:55 +01:00
from .results_report import get_print_date
__all__ = ['Planification', 'PlanificationTechnician',
'PlanificationTechnicianDetail', 'PlanificationDetail',
'PlanificationServiceDetail', 'NotebookLineFraction',
'PlanificationServiceDetailLaboratoryProfessional',
'PlanificationAnalysis', 'PlanificationFraction', 'FractionReagent',
'LabProfessionalMethod', 'LabProfessionalMethodRequalification',
'LabProfessionalMethodRequalificationSupervisor',
'LabProfessionalMethodRequalificationControl', 'BlindSample',
'RelateTechniciansStart', 'RelateTechniciansResult',
'RelateTechniciansDetail1', 'RelateTechniciansDetail2',
'RelateTechniciansDetail3', 'RelateTechnicians', 'UnlinkTechniciansStart',
'UnlinkTechniciansDetail1', 'UnlinkTechnicians', 'AddFractionControlStart',
'AddFractionControl', 'AddFractionRMBMZStart', 'AddFractionRMBMZ',
'AddFractionBREStart', 'AddFractionBRE', 'AddFractionMRTStart',
'AddFractionMRT', 'RemoveControlStart', 'RemoveControl',
'AddAnalysisStart', 'AddAnalysis', 'SearchFractionsNext',
'SearchFractionsDetail', 'SearchFractions', 'SearchPlannedFractionsStart',
'SearchPlannedFractionsNext', 'SearchPlannedFractions',
'CreateFractionControlStart', 'CreateFractionControl',
'ReleaseFractionStart', 'ReleaseFractionEmpty', 'ReleaseFractionResult',
'ReleaseFraction', 'QualificationSituations', 'QualificationSituation',
'QualificationAction', 'QualificationSituation2',
'QualificationSituation3', 'QualificationSituation4',
'TechniciansQualification', 'ReplaceTechnicianStart', 'ReplaceTechnician',
'LoadServices', 'PlanificationSequenceReport',
'PlanificationWorksheetAnalysisReport',
'PlanificationWorksheetMethodReport', 'PlanificationWorksheetReport',
'PendingServicesUnplannedReport', 'PendingServicesUnplannedSpreadsheet',
'PrintBlindSampleReportStart', 'PrintBlindSampleReport',
'BlindSampleReport', 'PrintPendingServicesUnplannedReportStart',
'PrintPendingServicesUnplannedReport',
'PlanificationSequenceAnalysisReport']
class Planification(Workflow, ModelSQL, ModelView):
'Planification'
__name__ = 'lims.planification'
_rec_name = 'code'
code = fields.Char('Code', select=True, readonly=True)
date = fields.Date('Date', readonly=True)
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
required=True)
analysis = fields.Many2Many('lims.planification-analysis',
'planification', 'analysis', 'Analysis/Sets/Groups',
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))},
context={'date_from': Eval('date_from'), 'date_to': Eval('date_to'),
'calculate': Bool(Equal(Eval('state'), 'draft'))},
domain=['OR', ('id', 'in', Eval('analysis')), [
('id', 'in', Eval('analysis_domain'))]],
depends=['state', 'date_from', 'date_to', 'analysis_domain'])
analysis_domain = fields.Function(fields.Many2Many('lims.analysis',
None, None, 'Analysis domain'),
'on_change_with_analysis_domain')
technicians = fields.One2Many('lims.planification.technician',
'planification', 'Technicians', depends=['method_domain',
'technicians_domain'])
date_from = fields.Date('Date from', depends=['state'], required=True,
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))})
date_to = fields.Date('Date to', depends=['state'], required=True,
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))})
start_date = fields.Date('Start date', depends=['state'],
states={'readonly': Bool(Equal(Eval('state'), 'confirmed'))})
details = fields.One2Many('lims.planification.detail',
'planification', 'Fractions to plan',
states={'readonly': Not(Bool(Equal(Eval('state'), 'draft')))},
depends=['state'])
controls = fields.Many2Many('lims.planification-fraction',
'planification', 'fraction', 'Controls', readonly=True)
state = fields.Selection([
('draft', 'Draft'),
('preplanned', 'Pre-Planned'),
('confirmed', 'Confirmed'),
('not_executed', 'Not executed'),
], 'State', required=True, readonly=True)
waiting_process = fields.Boolean('Waiting process')
method_domain = fields.Function(fields.One2Many('lims.lab.method',
None, 'Method domain'),
'on_change_with_method_domain', setter='set_method_domain')
technicians_domain = fields.Function(fields.One2Many(
'lims.laboratory.professional', None, 'Technicians domain'),
'on_change_with_technicians_domain', setter='set_technicians_domain')
comments = fields.Text('Comments')
@classmethod
def __setup__(cls):
super(Planification, cls).__setup__()
cls._order.insert(0, ('code', 'DESC'))
cls._transitions |= set((
('draft', 'preplanned'),
('preplanned', 'confirmed'),
('confirmed', 'not_executed'),
))
cls._buttons.update({
'add_analysis': {
'readonly': (Eval('state') != 'draft'),
},
'search_fractions': {
'readonly': (Eval('state') != 'draft'),
},
'search_planned_fractions': {
'readonly': (Eval('state') != 'draft'),
},
'preplan': {
'invisible': (Eval('state') != 'draft'),
},
'confirm': {
'invisible': (Eval('state') != 'preplanned'),
},
'release_controls': {
'invisible': (Eval('state') != 'confirmed'),
},
'relate_technicians': {
'readonly': (Eval('state') != 'preplanned'),
},
'unlink_technicians': {
'readonly': (Eval('state') != 'preplanned'),
},
'replace_technician': {
'readonly': (Eval('state') != 'confirmed'),
},
'add_fraction_con': {
'readonly': (Eval('state') != 'preplanned'),
},
'add_fraction_rm_bmz': {
'readonly': (Eval('state') != 'preplanned'),
},
'add_fraction_bre': {
'readonly': (Eval('state') != 'preplanned'),
},
'add_fraction_mrt': {
'readonly': (Eval('state') != 'preplanned'),
},
'remove_control': {
'readonly': (Eval('state') != 'preplanned'),
},
})
@staticmethod
def default_state():
return 'draft'
@staticmethod
def default_waiting_process():
return False
@staticmethod
def default_date():
Date = Pool().get('ir.date')
return Date.today()
@staticmethod
def default_laboratory():
return Transaction().context.get('laboratory', None)
2020-03-05 05:24:51 +01:00
@fields.depends('laboratory', 'state',
methods=['_get_analysis_domain'])
def on_change_with_analysis_domain(self, name=None):
if not self.laboratory or self.state != 'draft':
return []
return self._get_analysis_domain(self.laboratory)
@staticmethod
def _get_analysis_domain(laboratory):
cursor = Transaction().connection.cursor()
pool = Pool()
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
Analysis = pool.get('lims.analysis')
if not laboratory:
return []
cursor.execute('SELECT al.analysis '
'FROM "' + AnalysisLaboratory._table + '" al '
'INNER JOIN "' + Analysis._table + '" a '
'ON a.id = al.analysis '
'WHERE al.laboratory = %s '
'AND a.behavior != \'internal_relation\'',
(laboratory.id,))
analysis_sets_list = [a[0] for a in cursor.fetchall()]
2017-10-08 02:23:22 +02:00
groups_list = []
cursor.execute('SELECT id '
'FROM "' + Analysis._table + '" '
'WHERE type = \'group\'')
groups_list_ids = [g[0] for g in cursor.fetchall()]
for group_id in groups_list_ids:
if Planification._get_group_available(group_id,
analysis_sets_list):
groups_list.append(group_id)
return analysis_sets_list + groups_list
@staticmethod
def _get_group_available(group_id, analysis_sets_list):
cursor = Transaction().connection.cursor()
pool = Pool()
AnalysisIncluded = pool.get('lims.analysis.included')
Analysis = pool.get('lims.analysis')
cursor.execute('SELECT ia.included_analysis, a.type '
'FROM "' + AnalysisIncluded._table + '" ia '
'INNER JOIN "' + Analysis._table + '" a '
'ON a.id = ia.included_analysis '
'WHERE ia.analysis = %s '
'AND a.behavior != \'internal_relation\'',
(group_id,))
included_analysis = cursor.fetchall()
if not included_analysis:
return False
for analysis in included_analysis:
if (analysis[1] != 'group' and analysis[0] not in
analysis_sets_list):
return False
if (analysis[1] == 'group' and not
Planification._get_group_available(analysis[0],
analysis_sets_list)):
return False
return True
@classmethod
def create(cls, vlist):
pool = Pool()
Config = pool.get('lims.configuration')
Sequence = pool.get('ir.sequence')
vlist = [x.copy() for x in vlist]
config = Config(1)
for values in vlist:
values['code'] = Sequence.get_id(
config.planification_sequence.id)
return super(Planification, cls).create(vlist)
@classmethod
def check_delete(cls, planifications):
for planification in planifications:
if planification.state not in ['draft', 'preplanned']:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_delete_planification',
planification=planification.rec_name))
@classmethod
def delete(cls, planifications):
cls.check_delete(planifications)
super(Planification, cls).delete(planifications)
2017-10-08 02:23:22 +02:00
@classmethod
def copy(cls, planifications, default=None):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_copy_planification'))
@classmethod
@ModelView.button_action('lims.wiz_lims_add_analysis')
def add_analysis(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_search_fractions')
def search_fractions(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_search_planned_fractions')
def search_planned_fractions(cls, planifications):
pass
@classmethod
@ModelView.button
@Workflow.transition('preplanned')
def preplan(cls, planifications):
for planification in planifications:
planification.check_start_date()
@classmethod
@ModelView.button_action('lims.wiz_lims_technicians_qualification')
def confirm(cls, planifications):
for planification in planifications:
planification.check_start_date()
planification.check_technicians()
def check_start_date(self):
if not self.start_date:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_not_start_date'))
for detail in self.details:
if detail.fraction.sample.date2 > self.start_date:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_invalid_start_date',
date=detail.fraction.sample.date2))
def check_technicians(self):
fractions = {}
for detail in self.details:
for service_detail in detail.details:
if not service_detail.staff_responsible:
key = (detail.fraction.id,
service_detail.notebook_line.method.id)
if key not in fractions:
fractions[key] = '%s (%s)' % (detail.fraction.rec_name,
service_detail.notebook_line.method.code)
if fractions:
2019-07-23 23:27:33 +02:00
sorted_fractions = sorted(
list(fractions.values()), key=lambda x: x)
raise UserError(gettext('lims.msg_no_technician',
fractions='\n' + '\n'.join(sorted_fractions) + '\n'))
@classmethod
def process_waiting_planifications(cls):
'''
Cron - Process Waiting Planifications
'''
logger = logging.getLogger('lims_planification')
planifications = cls.search([
('waiting_process', '=', True),
], order=[('id', 'ASC')])
if planifications:
logger.info('Cron - Processing planifications:INIT')
for planification in planifications:
if planification.state == 'confirmed':
cls.do_confirm([planification])
elif planification.state == 'not_executed':
cls.do_release_controls([planification])
logger.info('Cron - Processing planifications:END')
@classmethod
def do_confirm(cls, planifications):
for planification in planifications:
planification.update_laboratory_notebook()
planification.update_analysis_detail()
if planification.waiting_process:
planification.waiting_process = False
planification.save()
def update_laboratory_notebook(self):
pool = Pool()
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
NotebookLine = pool.get('lims.notebook.line')
notebook_lines = []
service_details = PlanificationServiceDetail.search([
('detail.planification', '=', self.id),
('notebook_line', '!=', None),
])
for service_detail in service_details:
notebook_line = NotebookLine(
service_detail.notebook_line.id)
notebook_line.start_date = self.start_date
notebook_line.laboratory_professionals = [p.id
for p in service_detail.staff_responsible]
notebook_line.planification = self.id
if not service_detail.is_control:
notebook_line.controls = [f.id
for f in self.controls]
notebook_lines.append(notebook_line)
if notebook_lines:
NotebookLine.save(notebook_lines)
def update_analysis_detail(self):
pool = Pool()
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
analysis_detail_ids = []
service_details = PlanificationServiceDetail.search([
('detail.planification', '=', self.id),
('notebook_line.analysis_detail', '!=', None),
])
for service_detail in service_details:
analysis_detail_ids.append(
service_detail.notebook_line.analysis_detail.id)
analysis_details = EntryDetailAnalysis.search([
('id', 'in', analysis_detail_ids),
])
if analysis_details:
EntryDetailAnalysis.write(analysis_details, {
'state': 'planned',
})
@classmethod
@ModelView.button
@Workflow.transition('not_executed')
def release_controls(cls, planifications):
Config = Pool().get('lims.configuration')
process_background = Config(1).planification_process_background
for planification in planifications:
# Check if is still waiting for confirmation
if planification.waiting_process:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_waiting_process',
planification=planification.code))
if process_background:
planification.waiting_process = True
planification.save()
if not process_background:
cls.do_release_controls(planifications)
@classmethod
def do_release_controls(cls, planifications):
for planification in planifications:
planification.re_update_laboratory_notebook()
planification.re_update_analysis_detail()
planification.unlink_controls()
if planification.waiting_process:
planification.waiting_process = False
planification.save()
def re_update_laboratory_notebook(self):
NotebookLine = Pool().get('lims.notebook.line')
for detail in self.details:
for service_detail in detail.details:
if service_detail.is_control and service_detail.notebook_line:
notebook_line = NotebookLine(
service_detail.notebook_line.id)
notebook_line.start_date = None
notebook_line.laboratory_professionals = []
notebook_line.planification = None
notebook_line.controls = []
notebook_line.save()
def re_update_analysis_detail(self):
EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
analysis_detail_ids = []
for detail in self.details:
for service_detail in detail.details:
if (service_detail.is_control and
service_detail.notebook_line and
service_detail.notebook_line.analysis_detail):
analysis_detail_ids.append(
service_detail.notebook_line.analysis_detail.id)
analysis_details = EntryDetailAnalysis.search([
('id', 'in', analysis_detail_ids),
])
if analysis_details:
EntryDetailAnalysis.write(analysis_details, {
'state': 'unplanned',
})
def unlink_controls(self):
pool = Pool()
PlanificationFraction = pool.get('lims.planification-fraction')
PlanificationDetail = pool.get('lims.planification.detail')
controls = PlanificationFraction.search([
('planification', '=', self.id),
])
if controls:
PlanificationFraction.delete(controls)
controls_details = PlanificationDetail.search([
('planification', '=', self.id),
('details.is_control', '=', True),
])
if controls_details:
PlanificationDetail.delete(controls_details)
@fields.depends('analysis')
def on_change_with_method_domain(self, name=None):
methods = []
if self.analysis:
for a in self.analysis:
if a.methods:
methods.extend([m.id for m in a.methods])
return methods
@classmethod
def set_method_domain(cls, records, name, value):
return
@fields.depends('laboratory')
def on_change_with_technicians_domain(self, name=None):
pool = Pool()
UserLaboratory = pool.get('lims.user-laboratory')
LaboratoryProfessional = pool.get('lims.laboratory.professional')
if not self.laboratory:
return []
users = UserLaboratory.search([
('laboratory', '=', self.laboratory.id),
])
if not users:
return []
professionals = LaboratoryProfessional.search([
('party.lims_user', 'in', [u.user.id for u in users]),
])
if not professionals:
return []
return [p.id for p in professionals]
@classmethod
def set_technicians_domain(cls, records, name, value):
return
@classmethod
@ModelView.button_action('lims.wiz_lims_relate_technicians')
def relate_technicians(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_unlink_technicians')
def unlink_technicians(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_replace_technician')
def replace_technician(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_add_fraction_con')
def add_fraction_con(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_add_fraction_rm_bmz')
def add_fraction_rm_bmz(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_add_fraction_bre')
def add_fraction_bre(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_add_fraction_mrt')
def add_fraction_mrt(cls, planifications):
pass
@classmethod
@ModelView.button_action('lims.wiz_lims_remove_control')
def remove_control(cls, planifications):
pass
class PlanificationTechnician(ModelSQL, ModelView):
'Technician'
__name__ = 'lims.planification.technician'
planification = fields.Many2One('lims.planification', 'Planification',
ondelete='CASCADE', select=True, required=True)
laboratory_professional = fields.Many2One('lims.laboratory.professional',
'Laboratory professional', required=True, domain=[
('id', 'in', Eval('_parent_planification',
{}).get('technicians_domain')),
])
details = fields.Function(fields.One2Many(
'lims.planification.technician.detail', 'technician',
'Fractions to plan', readonly=True),
'get_details', setter='set_details')
@classmethod
def __setup__(cls):
super(PlanificationTechnician, cls).__setup__()
t = cls.__table__()
cls._sql_constraints += [
('planification_professional_uniq',
Unique(t, t.planification, t.laboratory_professional),
'lims.msg_planification_professional_unique_id'),
]
def get_details(self, name=None):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
PlanificationDetail = pool.get('lims.planification.detail')
NotebookLine = pool.get('lims.notebook.line')
Fraction = pool.get('lims.fraction')
LabMethod = pool.get('lims.lab.method')
cursor.execute('SELECT DISTINCT(f.number, nl.analysis_origin, '
'm.code||\' - \'||m.name) '
'FROM "' + PlanificationDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
'ON sd.id = sdp.detail '
'INNER JOIN "' + PlanificationDetail._table + '" d '
'ON d.id = sd.detail '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON nl.id = sd.notebook_line '
'INNER JOIN "' + LabMethod._table + '" m '
'ON m.id = nl.method '
'INNER JOIN "' + Fraction._table + '" f '
'ON f.id = d.fraction '
'WHERE sdp.professional = %s '
'AND d.planification = %s',
(self.laboratory_professional.id, self.planification.id))
fractions = []
for d in cursor.fetchall():
r = d[0].split(',')
fractions.append({
'fraction': str(r[0][1:]),
'analysis_origin': str(r[1]).replace('"', ''),
'method': str(','.join(r[2:])[:-1].replace('"', '')),
})
return fractions
@classmethod
def set_details(cls, records, name, value):
return
class PlanificationTechnicianDetail(ModelView):
'Technician Detail'
__name__ = 'lims.planification.technician.detail'
fraction = fields.Char('Fraction')
analysis_origin = fields.Char('Analysis origin')
method = fields.Char('Method')
class PlanificationDetail(ModelSQL, ModelView):
'Fraction to Plan'
__name__ = 'lims.planification.detail'
planification = fields.Many2One('lims.planification', 'Planification',
ondelete='CASCADE', select=True, required=True)
fraction = fields.Many2One('lims.fraction', 'Fraction', required=True)
service_analysis = fields.Many2One('lims.analysis', 'Service',
required=True)
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_fraction_field',
searcher='search_fraction_field')
label = fields.Function(fields.Char('Label'), 'get_fraction_field',
searcher='search_fraction_field')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_fraction_field')
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
'get_fraction_field')
details = fields.One2Many('lims.planification.service_detail',
'detail', 'Planification detail', states={'readonly': True})
urgent = fields.Function(fields.Boolean('Urgent'), 'get_service_field',
setter='set_urgent')
priority = fields.Function(fields.Integer('Priority'), 'get_service_field')
report_date = fields.Function(fields.Date('Date agreed for result'),
'get_service_field')
comments = fields.Function(fields.Text('Comments'), 'get_fraction_field')
icon = fields.Function(fields.Char("Icon"), 'get_icon')
@classmethod
def __setup__(cls):
super(PlanificationDetail, cls).__setup__()
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('service_analysis', 'ASC'))
@classmethod
def get_fraction_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if (name == 'label' or name == 'comments'):
for d in details:
result[name][d.id] = getattr(d.fraction, name, None)
elif name == 'fraction_type':
for d in details:
field = getattr(d.fraction, 'type', None)
result[name][d.id] = field.id if field else None
else:
for d in details:
field = getattr(d.fraction, name, None)
result[name][d.id] = field.id if field else None
return result
@classmethod
def search_fraction_field(cls, name, clause):
if name == 'fraction_type':
name = 'type'
return [('fraction.' + name,) + tuple(clause[1:])]
@classmethod
def get_service_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name == 'urgent':
for d in details:
result[name][d.id] = False
elif name == 'priority':
for d in details:
result[name][d.id] = 0
else:
for d in details:
result[name][d.id] = None
for d in details:
if d.fraction and d.service_analysis:
for service in d.fraction.services:
if service.analysis == d.service_analysis:
for name in names:
result[name][d.id] = getattr(service, name)
return result
@classmethod
def set_urgent(cls, details, name, value):
Service = Pool().get('lims.service')
for d in details:
if d.fraction and d.service_analysis:
for service in d.fraction.services:
if service.analysis == d.service_analysis:
Service.write([service], {'urgent': value})
def get_icon(self, name):
if self.comments:
return 'lims-blue'
2019-02-22 18:08:32 +01:00
return 'lims-white'
class PlanificationServiceDetail(ModelSQL, ModelView):
'Planification Detail'
__name__ = 'lims.planification.service_detail'
detail = fields.Many2One('lims.planification.detail', 'Planification',
ondelete='CASCADE', select=True, required=True)
planification = fields.Function(fields.Many2One('lims.planification',
'Planification'), 'get_planification', searcher='search_planification')
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook line',
required=True, select=True)
staff_responsible = fields.Many2Many(
'lims.planification.service_detail-laboratory.professional', 'detail',
'professional', 'Laboratory professionals')
is_control = fields.Boolean('Is Control')
is_replanned = fields.Boolean('Is Replanned')
planned_service = fields.Many2One('lims.analysis', 'Planned service')
repetition = fields.Function(fields.Integer('Repetition'),
'get_repetition')
@staticmethod
def default_is_control():
return False
@staticmethod
def default_is_replanned():
return False
def get_planification(self, name=None):
if self.detail:
if self.detail.planification:
return self.detail.planification.id
return None
@classmethod
def search_planification(cls, name, clause):
return [('detail.' + name,) + tuple(clause[1:])]
def get_repetition(self, name=None):
if self.notebook_line:
return self.notebook_line.repetition
return None
class PlanificationServiceDetailLaboratoryProfessional(ModelSQL):
'Planification Detail - Laboratory Professional'
__name__ = 'lims.planification.service_detail-laboratory.professional'
_table = 'lims_plan_service_d-laboratory_professional'
detail = fields.Many2One('lims.planification.service_detail',
'Planification detail', ondelete='CASCADE', select=True,
required=True)
professional = fields.Many2One('lims.laboratory.professional',
'Laboratory professional', ondelete='CASCADE', select=True,
required=True)
class PlanificationAnalysis(ModelSQL):
'Planification - Analysis'
__name__ = 'lims.planification-analysis'
planification = fields.Many2One('lims.planification', 'Planification',
ondelete='CASCADE', select=True, required=True)
analysis = fields.Many2One('lims.analysis', 'Analysis',
ondelete='CASCADE', select=True, required=True)
class PlanificationFraction(ModelSQL):
'Planification - Fraction'
__name__ = 'lims.planification-fraction'
planification = fields.Many2One('lims.planification', 'Planification',
ondelete='CASCADE', select=True, required=True)
fraction = fields.Many2One('lims.fraction', 'Fraction',
ondelete='CASCADE', select=True, required=True)
class NotebookLineFraction(ModelSQL):
'Laboratory Notebook Line - Fraction'
__name__ = 'lims.notebook.line-fraction'
notebook_line = fields.Many2One('lims.notebook.line', 'Notebook Line',
ondelete='CASCADE', select=True, required=True)
fraction = fields.Many2One('lims.fraction', 'Fraction',
ondelete='CASCADE', select=True, required=True)
class FractionReagent(ModelSQL, ModelView):
'Fraction Reagent'
__name__ = 'lims.fraction.reagent'
fraction = fields.Many2One('lims.fraction', 'Fraction', required=True,
ondelete='CASCADE', select=True)
product = fields.Many2One('product.product', 'Reagent', required=True,
domain=[('account_category', 'in', Eval('reagent_domain'))],
depends=['reagent_domain'])
reagent_domain = fields.Function(fields.Many2Many('product.category',
None, None, 'Reagent domain'), 'get_reagent_domain')
lot = fields.Many2One('stock.lot', 'Lot',
domain=[('product', '=', Eval('product'))],
depends=['product'])
quantity = fields.Function(fields.Float('Current quantity'),
'on_change_with_quantity')
default_uom = fields.Function(fields.Many2One('product.uom',
'Default UOM'),
'on_change_with_default_uom')
@staticmethod
def default_reagent_domain():
Config = Pool().get('lims.configuration')
config = Config(1)
return config.get_reagents()
def get_reagent_domain(self, name=None):
return self.default_reagent_domain()
2020-03-05 05:24:51 +01:00
@fields.depends('product', '_parent_product.quantity')
def on_change_with_quantity(self, name=None):
pool = Pool()
Location = pool.get('stock.location')
Product = pool.get('product.product')
Date = pool.get('ir.date')
if not self.product:
return 0
locations = Location.search([('type', '=', 'storage')])
with Transaction().set_context(locations=[l.id for l in locations],
stock_date_end=Date.today()):
product = Product(self.product.id)
return product.quantity or 0
2020-03-05 05:24:51 +01:00
@fields.depends('product', '_parent_product.default_uom')
def on_change_with_default_uom(self, name=None):
if self.product:
return self.product.default_uom.id
class LabProfessionalMethod(ModelSQL, ModelView):
'Laboratory Professional Method'
__name__ = 'lims.lab.professional.method'
professional = fields.Many2One('lims.laboratory.professional',
'Professional', required=True)
method = fields.Many2One('lims.lab.method', 'Method', required=True)
state = fields.Selection([
('training', 'Training'),
('qualified', 'Qualified'),
('requalified', 'Requalified'),
], 'State', sort=False)
type = fields.Selection([
('preparation', 'Preparation'),
('analytical', 'Analytical'),
], 'Type', sort=False)
requalification_history = fields.One2Many(
'lims.lab.professional.method.requalification', 'professional_method',
'Trainings/Qualifications/Requalifications')
determination = fields.Function(fields.Char('Determination'),
'get_determination', searcher='search_determination')
@classmethod
def __setup__(cls):
super(LabProfessionalMethod, cls).__setup__()
cls._order.insert(0, ('professional', 'ASC'))
cls._order.insert(1, ('method', 'ASC'))
t = cls.__table__()
cls._sql_constraints += [
('professional_method_type_uniq',
Unique(t, t.professional, t.method, t.type),
'lims.msg_professional_method_unique_id'),
]
def get_determination(self, name=None):
if self.method:
return self.method.determination
return None
@classmethod
def search_determination(cls, name, clause):
return [('method.' + name,) + tuple(clause[1:])]
class LabProfessionalMethodRequalification(ModelSQL, ModelView):
'Laboratory Professional Method Requalification'
__name__ = 'lims.lab.professional.method.requalification'
_table = 'lims_lab_pro_method_req'
professional_method = fields.Many2One('lims.lab.professional.method',
'Professional Method', ondelete='CASCADE', select=True, required=True)
type = fields.Selection([
('training', 'Training'),
('qualification', 'Qualification'),
('requalification', 'Requalification'),
], 'Type', sort=False)
date = fields.Date('Date', required=True)
last_execution_date = fields.Date('Last execution Date')
supervisors = fields.One2Many(
'lims.lab.professional.method.requalification.supervisor',
'method_requalification', 'Supervisors')
controls = fields.One2Many(
'lims.lab.professional.method.requalification.control',
'method_requalification', 'Controls')
class LabProfessionalMethodRequalificationSupervisor(ModelSQL, ModelView):
'Laboratory Professional Method Requalification Supervisor'
__name__ = 'lims.lab.professional.method.requalification.supervisor'
_table = 'lims_lab_pro_method_req_supervisor'
method_requalification = fields.Many2One(
'lims.lab.professional.method.requalification',
'Professional method requalification', ondelete='CASCADE',
select=True, required=True)
supervisor = fields.Many2One('lims.laboratory.professional', 'Supervisor',
required=True)
class LabProfessionalMethodRequalificationControl(ModelSQL, ModelView):
'Laboratory Professional Method Requalification Control'
__name__ = 'lims.lab.professional.method.requalification.control'
_table = 'lims_lab_pro_method_req_control'
method_requalification = fields.Many2One(
'lims.lab.professional.method.requalification',
'Professional method requalification', ondelete='CASCADE',
select=True, required=True)
control = fields.Many2One('lims.fraction', 'Control',
required=True)
class BlindSample(ModelSQL, ModelView):
'Blind Sample'
__name__ = 'lims.blind_sample'
line = fields.Many2One('lims.notebook.line', 'Line', required=True,
readonly=True, ondelete='CASCADE', select=True)
entry = fields.Many2One('lims.entry', 'Entry', readonly=True)
sample = fields.Many2One('lims.sample', 'Sample', readonly=True)
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
service = fields.Many2One('lims.service', 'Service', readonly=True)
analysis = fields.Many2One('lims.analysis', 'Analysis', readonly=True)
repetition = fields.Integer('Repetition', readonly=True)
date = fields.Date('Date', readonly=True)
original_line = fields.Many2One('lims.notebook.line', 'Original line')
original_sample = fields.Many2One('lims.sample', 'Original sample',
readonly=True)
original_fraction = fields.Many2One('lims.fraction', 'Original fraction',
readonly=True)
original_repetition = fields.Integer('Repetition', readonly=True)
min_value = fields.Char('Minimum value', readonly=True)
max_value = fields.Char('Maximum value', readonly=True)
class RelateTechniciansStart(ModelView):
2017-10-08 02:23:22 +02:00
'Relate Technicians'
__name__ = 'lims.planification.relate_technicians.start'
exclude_relateds = fields.Boolean('Exclude fractions already related')
grouping = fields.Selection([
('none', 'None'),
('origin_method', 'Analysis origin and Method'),
('origin', 'Analysis origin'),
], 'Grouping', sort=False, required=True)
class RelateTechniciansResult(ModelView):
2017-10-08 02:23:22 +02:00
'Relate Technicians'
__name__ = 'lims.planification.relate_technicians.result'
technicians = fields.Many2Many('lims.laboratory.professional',
None, None, 'Technicians', required=True,
domain=[('id', 'in', Eval('technicians_domain'))],
depends=['technicians_domain'])
technicians_domain = fields.One2Many('lims.laboratory.professional',
None, 'Technicians domain')
grouping = fields.Selection([
('none', 'None'),
('origin_method', 'Analysis origin and Method'),
('origin', 'Analysis origin'),
], 'Grouping', sort=False, readonly=True)
details1 = fields.Many2Many(
'lims.planification.relate_technicians.detail1', None, None,
'Fractions to plan', domain=[('id', 'in', Eval('details1_domain'))],
states={'invisible': Not(Bool(Equal(Eval('grouping'), 'none')))},
depends=['details1_domain', 'grouping'])
details1_domain = fields.One2Many(
'lims.planification.relate_technicians.detail1', None,
'Fractions domain')
details2 = fields.Many2Many(
'lims.planification.relate_technicians.detail2', None, None,
'Fractions to plan', domain=[('id', 'in', Eval('details2_domain'))],
states={
'invisible': Not(Bool(Equal(Eval('grouping'), 'origin_method'))),
}, depends=['details2_domain', 'grouping'])
details2_domain = fields.One2Many(
'lims.planification.relate_technicians.detail2', None,
'Fractions domain')
details3 = fields.Many2Many(
'lims.planification.relate_technicians.detail3', None, None,
'Fractions to plan', domain=[('id', 'in', Eval('details3_domain'))],
states={'invisible': Not(Bool(Equal(Eval('grouping'), 'origin')))},
depends=['details3_domain', 'grouping'])
details3_domain = fields.One2Many(
'lims.planification.relate_technicians.detail3', None,
'Fractions domain')
@fields.depends('grouping')
def on_change_grouping(self):
self.details1 = []
self.details2 = []
self.details3 = []
class RelateTechniciansDetail1(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Fraction Detail'
__name__ = 'lims.planification.relate_technicians.detail1'
_table = 'lims_planification_relate_technicians_d1'
fraction = fields.Many2One('lims.fraction', 'Fraction')
service_analysis = fields.Many2One('lims.analysis', 'Service')
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_fraction_field')
label = fields.Function(fields.Char('Label'), 'get_fraction_field')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(RelateTechniciansDetail1,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def __setup__(cls):
super(RelateTechniciansDetail1, cls).__setup__()
2017-10-08 02:23:22 +02:00
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('service_analysis', 'ASC'))
@classmethod
def get_fraction_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name == 'label':
for d in details:
result[name][d.id] = getattr(d.fraction, name, None)
elif name == 'fraction_type':
for d in details:
field = getattr(d.fraction, 'type', None)
result[name][d.id] = field.id if field else None
else:
for d in details:
field = getattr(d.fraction, name, None)
result[name][d.id] = field.id if field else None
return result
class RelateTechniciansDetail2(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Fraction Detail'
__name__ = 'lims.planification.relate_technicians.detail2'
_table = 'lims_planification_relate_technicians_d2'
fraction = fields.Many2One('lims.fraction', 'Fraction')
analysis_origin = fields.Char('Analysis origin')
method = fields.Many2One('lims.lab.method', 'Method')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(RelateTechniciansDetail2,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def __setup__(cls):
super(RelateTechniciansDetail2, cls).__setup__()
2017-10-08 02:23:22 +02:00
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('analysis_origin', 'ASC'))
cls._order.insert(2, ('method', 'ASC'))
class RelateTechniciansDetail3(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Fraction Detail'
__name__ = 'lims.planification.relate_technicians.detail3'
_table = 'lims_planification_relate_technicians_d3'
fraction = fields.Many2One('lims.fraction', 'Fraction')
analysis_origin = fields.Char('Analysis origin')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(RelateTechniciansDetail3,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def __setup__(cls):
super(RelateTechniciansDetail3, cls).__setup__()
2017-10-08 02:23:22 +02:00
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('analysis_origin', 'ASC'))
class RelateTechnicians(Wizard):
2017-10-08 02:23:22 +02:00
'Relate Technicians'
__name__ = 'lims.planification.relate_technicians'
start = StateView('lims.planification.relate_technicians.start',
'lims.lims_relate_technicians_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Search', 'search', 'tryton-ok', default=True),
])
search = StateTransition()
result = StateView('lims.planification.relate_technicians.result',
'lims.lims_relate_technicians_result_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Relate', 'relate', 'tryton-ok', default=True),
])
relate = StateTransition()
def default_start(self, fields):
return {
'exclude_relateds': True,
'grouping': 'none',
}
def transition_search(self):
planification_id = Transaction().context['active_id']
self.result.grouping = self.start.grouping
self.result.details1_domain = []
self.result.details2_domain = []
self.result.details3_domain = []
if self.start.grouping == 'none':
self.result.details1_domain = self._view_details1(planification_id,
self.start.exclude_relateds)
elif self.start.grouping == 'origin_method':
self.result.details2_domain = self._view_details2(planification_id,
self.start.exclude_relateds)
elif self.start.grouping == 'origin':
self.result.details3_domain = self._view_details3(planification_id,
self.start.exclude_relateds)
return 'result'
def default_result(self, fields):
Planification = Pool().get('lims.planification')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
details1_domain = []
if self.result.details1_domain:
details1_domain = [d.id for d in self.result.details1_domain]
details2_domain = []
if self.result.details2_domain:
details2_domain = [d.id for d in self.result.details2_domain]
details3_domain = []
if self.result.details3_domain:
details3_domain = [d.id for d in self.result.details3_domain]
return {
'technicians_domain': [t.laboratory_professional.id
for t in planification.technicians],
'details1_domain': details1_domain,
'details2_domain': details2_domain,
'details3_domain': details3_domain,
'grouping': self.start.grouping,
}
def _view_details1(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
RelateTechniciansDetail1 = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.relate_technicians.detail1')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details1 = {}
cursor.execute('SELECT d.fraction, d.service_analysis, sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
'ON sd.detail = d.id '
'WHERE d.planification = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id,))
for x in cursor.fetchall():
f, s = x[0], x[1]
if (f, s) not in details1:
details1[(f, s)] = {
'fraction': f,
'service_analysis': s,
}
to_create = []
2019-03-04 15:41:58 +01:00
for d in details1.values():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': d['fraction'],
'service_analysis': d['service_analysis'],
})
return RelateTechniciansDetail1.create(to_create)
2017-10-08 02:23:22 +02:00
def _view_details2(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
NotebookLine = pool.get(
'lims.notebook.line')
RelateTechniciansDetail2 = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.relate_technicians.detail2')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details2 = {}
cursor.execute('SELECT d.fraction, nl.analysis_origin, nl.method, '
'sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
'ON sd.detail = d.id '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON sd.notebook_line = nl.id '
'WHERE d.planification = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id,))
for x in cursor.fetchall():
f, a, m = x[0], x[1], x[2]
if (f, a, m) not in details2:
details2[(f, a, m)] = {
'fraction': f,
'analysis_origin': a,
'method': m,
}
to_create = []
2019-03-04 15:41:58 +01:00
for d in details2.values():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': d['fraction'],
'analysis_origin': d['analysis_origin'],
'method': d['method'],
})
return RelateTechniciansDetail2.create(to_create)
2017-10-08 02:23:22 +02:00
def _view_details3(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
NotebookLine = pool.get(
'lims.notebook.line')
RelateTechniciansDetail3 = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.relate_technicians.detail3')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details3 = {}
cursor.execute('SELECT d.fraction, nl.analysis_origin, sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
'ON sd.detail = d.id '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON sd.notebook_line = nl.id '
'WHERE d.planification = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id,))
for x in cursor.fetchall():
f, a = x[0], x[1]
if (f, a) not in details3:
details3[(f, a)] = {
'fraction': f,
'analysis_origin': a,
}
to_create = []
2019-03-04 15:41:58 +01:00
for d in details3.values():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': d['fraction'],
'analysis_origin': d['analysis_origin'],
})
return RelateTechniciansDetail3.create(to_create)
2017-10-08 02:23:22 +02:00
def transition_relate(self):
PlanificationServiceDetail = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
planification_id = Transaction().context['active_id']
details = self._get_details(planification_id)
if not details:
2017-10-08 02:23:22 +02:00
return 'end'
PlanificationServiceDetail.write(details, {
2017-10-08 02:23:22 +02:00
'staff_responsible': [('remove',
[t.id for t in self.result.technicians])],
})
PlanificationServiceDetail.write(details, {
2017-10-08 02:23:22 +02:00
'staff_responsible': [('add',
[t.id for t in self.result.technicians])],
})
return 'end'
def _get_details(self, planification_id):
details = []
if self.start.grouping == 'none':
details = self._get_details1(planification_id,
self.start.exclude_relateds)
elif self.start.grouping == 'origin_method':
details = self._get_details2(planification_id,
self.start.exclude_relateds)
elif self.start.grouping == 'origin':
details = self._get_details3(planification_id,
self.start.exclude_relateds)
return details
2017-10-08 02:23:22 +02:00
def _get_details1(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details = []
for detail in self.result.details1:
cursor.execute('SELECT sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sd.detail = d.id '
'WHERE d.planification = %s '
'AND d.fraction = %s '
'AND d.service_analysis = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id, detail.fraction.id,
detail.service_analysis.id))
for x in cursor.fetchall():
details.append(x[0])
return PlanificationServiceDetail.browse(details)
def _get_details2(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
NotebookLine = pool.get(
'lims.notebook.line')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details = []
for detail in self.result.details2:
cursor.execute('SELECT sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sd.detail = d.id '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON sd.notebook_line = nl.id '
'WHERE d.planification = %s '
'AND d.fraction = %s '
'AND nl.analysis_origin = %s '
'AND nl.method = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id, detail.fraction.id,
detail.analysis_origin, detail.method.id))
for x in cursor.fetchall():
details.append(x[0])
return PlanificationServiceDetail.browse(details)
def _get_details3(self, planification_id, exclude_relateds):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
ServiceDetailProfessional = pool.get(
'lims.planification.service_detail-laboratory.professional')
NotebookLine = pool.get(
'lims.notebook.line')
exclude_relateds_clause = ''
if exclude_relateds:
exclude_relateds_clause = (' AND sd.id NOT IN ('
'SELECT sdp.detail '
'FROM "' + ServiceDetailProfessional._table + '" sdp '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sdp.detail = sd.id '
'INNER JOIN "' + PlanificationDetail._table + '" d'
' ON sd.detail = d.id '
'WHERE d.planification = %s'
')' % planification_id)
details = []
for detail in self.result.details3:
cursor.execute('SELECT sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sd.detail = d.id '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON sd.notebook_line = nl.id '
'WHERE d.planification = %s '
'AND d.fraction = %s '
'AND nl.analysis_origin = %s' +
exclude_relateds_clause,
2017-10-08 02:23:22 +02:00
(planification_id, detail.fraction.id,
detail.analysis_origin))
for x in cursor.fetchall():
details.append(x[0])
return PlanificationServiceDetail.browse(details)
class UnlinkTechniciansStart(ModelView):
2017-10-08 02:23:22 +02:00
'Unlink Technicians Start'
__name__ = 'lims.planification.unlink_technicians.start'
technicians = fields.Many2Many('lims.laboratory.professional',
None, None, 'Technicians', required=True,
domain=[('id', 'in', Eval('technicians_domain'))],
depends=['technicians_domain'])
technicians_domain = fields.One2Many('lims.laboratory.professional',
None, 'Technicians domain')
details1 = fields.Many2Many(
'lims.planification.unlink_technicians.detail1', None, None,
'Assigned fractions', domain=[('id', 'in', Eval('details1_domain'))],
depends=['details1_domain'])
details1_domain = fields.Many2Many(
'lims.planification.unlink_technicians.detail1', None, None,
'Fractions domain')
class UnlinkTechniciansDetail1(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Fraction Detail Unlink'
__name__ = 'lims.planification.unlink_technicians.detail1'
_table = 'lims_planification_unlink_technicians_d1'
fraction = fields.Many2One('lims.fraction', 'Fraction')
service_analysis = fields.Many2One('lims.analysis', 'Service')
fraction_type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_fraction_field')
label = fields.Function(fields.Char('Label'), 'get_fraction_field')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(UnlinkTechniciansDetail1,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def __setup__(cls):
super(UnlinkTechniciansDetail1, cls).__setup__()
2017-10-08 02:23:22 +02:00
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('service_analysis', 'ASC'))
@classmethod
def get_fraction_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name == 'label':
for d in details:
result[name][d.id] = getattr(d.fraction, name, None)
elif name == 'fraction_type':
for d in details:
field = getattr(d.fraction, 'type', None)
result[name][d.id] = field.id if field else None
else:
for d in details:
field = getattr(d.fraction, name, None)
result[name][d.id] = field.id if field else None
return result
class UnlinkTechnicians(Wizard):
2017-10-08 02:23:22 +02:00
'Unlink Technicians'
__name__ = 'lims.planification.unlink_technicians'
start = StateView('lims.planification.unlink_technicians.start',
'lims.lims_unlink_technicians_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Unlink', 'unlink', 'tryton-ok', default=True),
])
unlink = StateTransition()
def default_start(self, fields):
Planification = Pool().get('lims.planification')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
details_domain = self._view_details(planification.id)
return {
'technicians_domain': [t.laboratory_professional.id
for t in planification.technicians],
'details1_domain': [d.id for d in details_domain],
}
def _view_details(self, planification_id):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
UnlinkTechniciansDetail1 = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.unlink_technicians.detail1')
details1 = {}
cursor.execute('SELECT d.fraction, d.service_analysis, sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd '
'ON sd.detail = d.id '
'WHERE d.planification = %s',
(planification_id,))
for x in cursor.fetchall():
f, s = x[0], x[1]
if (f, s) not in details1:
details1[(f, s)] = {
'fraction': f,
'service_analysis': s,
}
to_create = []
2019-03-04 15:41:58 +01:00
for d in details1.values():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': d['fraction'],
'service_analysis': d['service_analysis'],
})
return UnlinkTechniciansDetail1.create(to_create)
2017-10-08 02:23:22 +02:00
def transition_unlink(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
details = self._get_details(planification.id)
PlanificationServiceDetail.write(details, {
2017-10-08 02:23:22 +02:00
'staff_responsible': [('remove',
[t.id for t in self.start.technicians])],
})
return 'end'
def _get_details(self, planification_id):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationDetail = pool.get(
'lims.planification.detail')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
details = []
for detail in self.start.details1:
cursor.execute('SELECT sd.id '
'FROM "' + PlanificationDetail._table + '" d '
'INNER JOIN "' + PlanificationServiceDetail._table + '" sd'
' ON sd.detail = d.id '
'WHERE d.planification = %s '
'AND d.fraction = %s '
'AND d.service_analysis = %s',
(planification_id, detail.fraction.id,
detail.service_analysis.id))
for x in cursor.fetchall():
details.append(x[0])
return PlanificationServiceDetail.browse(details)
class AddFractionControlStart(ModelView):
2017-10-08 02:23:22 +02:00
'Add Fraction Control'
__name__ = 'lims.planification.add_fraction_con.start'
planification = fields.Many2One('lims.planification', 'Planification')
type = fields.Selection([
('exist', 'Existing CON'),
('coi', 'COI'),
('mrc', 'MRC'),
('sla', 'SLA'),
('itc', 'ITC'),
('itl', 'ITL'),
], 'Control type', sort=False, required=True)
original_fraction = fields.Many2One('lims.fraction', 'Original fraction',
required=True, domain=[('id', 'in', Eval('fraction_domain'))],
depends=['fraction_domain'])
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
None, 'Fraction domain'), 'on_change_with_fraction_domain')
label = fields.Char('Label', depends=['type'],
states={'readonly': Eval('type') == 'exist'})
concentration_level = fields.Many2One('lims.concentration.level',
'Concentration level', states={
'invisible': Bool(Eval('concentration_level_invisible')),
}, depends=['concentration_level_invisible'])
concentration_level_invisible = fields.Boolean(
'Concentration level invisible')
generate_repetition = fields.Boolean('Generate repetition',
states={'readonly': Eval('type') == 'exist'}, depends=['type'])
2020-03-05 05:24:51 +01:00
@fields.depends('planification', 'type', '_parent_planification.analysis')
2017-10-08 02:23:22 +02:00
def on_change_with_fraction_domain(self, name=None):
cursor = Transaction().connection.cursor()
pool = Pool()
Analysis = pool.get('lims.analysis')
Fraction = pool.get('lims.fraction')
NotebookLine = pool.get('lims.notebook.line')
Notebook = pool.get('lims.notebook')
2017-10-08 02:23:22 +02:00
if not self.type:
return []
p_analysis_ids = []
for p_analysis in self.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
stored_fractions_ids = Fraction.get_stored_fractions()
2017-10-08 02:23:22 +02:00
special_type = 'con' if self.type == 'exist' else self.type
clause = [
('notebook.fraction.special_type', '=', special_type),
('notebook.fraction.id', 'in', stored_fractions_ids),
('analysis', 'in', p_analysis_ids),
]
if self.type == 'exist':
deadline = datetime.now() - relativedelta(days=5)
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
('notebook.fraction.sample.date2', '>=', deadline),
])
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if not notebook_lines:
return []
notebook_lines_ids = ', '.join(str(nl.id) for nl in notebook_lines)
cursor.execute('SELECT DISTINCT(n.fraction) '
'FROM "' + Notebook._table + '" n '
'INNER JOIN "' + NotebookLine._table + '" nl '
2017-10-08 02:23:22 +02:00
'ON nl.notebook = n.id '
'WHERE nl.id IN (' + notebook_lines_ids + ')')
return [x[0] for x in cursor.fetchall()]
2020-03-05 05:24:51 +01:00
@fields.depends('type', 'original_fraction', 'concentration_level',
'_parent_original_fraction.label',
'_parent_concentration_level.description')
2017-10-08 02:23:22 +02:00
def on_change_with_label(self, name=None):
Date = Pool().get('ir.date')
if self.type == 'exist':
return ''
label = ''
if self.original_fraction:
label += self.original_fraction.label
if self.concentration_level:
2019-07-23 23:27:33 +02:00
label += (' (' +
self.concentration_level.description + ')')
2017-10-08 02:23:22 +02:00
label += ' ' + str(Date.today())
return label
class AddFractionControl(Wizard):
2017-10-08 02:23:22 +02:00
'Add Fraction Control'
__name__ = 'lims.planification.add_fraction_con'
start = StateView('lims.planification.add_fraction_con.start',
'lims.lims_add_fraction_con_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
Config = Pool().get('lims.configuration')
config = Config(1)
defaults = {
'planification': Transaction().context['active_id'],
'concentration_level_invisible': True,
}
if (config.con_fraction_type and
config.con_fraction_type.control_charts):
defaults['concentration_level_invisible'] = False
return defaults
def transition_add(self):
fraction = self.start.original_fraction
if self.start.type != 'exist':
fraction = self.create_control()
self.add_control(fraction)
self.add_planification_detail(fraction)
return 'end'
def create_control(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
2017-10-08 02:23:22 +02:00
config = Config(1)
fraction_type = config.con_fraction_type
if not fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_con_fraction_type'))
2017-10-08 02:23:22 +02:00
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
entry = Entry(workyear.default_entry_control.id)
2017-10-08 02:23:22 +02:00
original_fraction = self.start.original_fraction
original_sample = Sample(original_fraction.sample.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(original_sample)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.copy([original_sample], default={
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'fractions': [],
})
# new fraction
new_fraction, = Fraction.copy([original_fraction], default={
2017-10-08 02:23:22 +02:00
'sample': new_sample.id,
'type': fraction_type.id,
'services': [],
'con_type': self.start.type,
'con_original_fraction': original_fraction.id,
})
# new services
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
services = Service.search([
2017-10-08 02:23:22 +02:00
('fraction', '=', original_fraction),
])
for service in services:
if not Analysis.is_typified(service.analysis,
2017-10-08 02:23:22 +02:00
new_sample.product_type, new_sample.matrix):
continue
method_id = service.method and service.method.id or None
device_id = service.device and service.device.id or None
if service.analysis.type == 'analysis':
original_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', original_fraction.id),
('analysis', '=', service.analysis.id),
('repetition', '=', 0),
], limit=1)
original_line = original_lines[0] if original_lines else None
if original_line:
method_id = original_line.method.id
if original_line.device:
device_id = original_line.device.id
new_service, = Service.copy([service], default={
2017-10-08 02:23:22 +02:00
'fraction': new_fraction.id,
'method': method_id,
'device': device_id,
})
# delete services/details not related to planification
to_delete = EntryDetailAnalysis.search([
('service', '=', new_service.id),
('analysis', 'not in', p_analysis_ids),
])
if to_delete:
with Transaction().set_user(0, set_context=True):
EntryDetailAnalysis.delete(to_delete)
if EntryDetailAnalysis.search_count([
('service', '=', new_service.id),
]) == 0:
with Transaction().set_user(0, set_context=True):
Service.delete([new_service])
2017-10-08 02:23:22 +02:00
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
# Generate repetition
if self.start.generate_repetition:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
self.generate_repetition(notebook_lines)
return new_fraction
2019-06-14 00:38:02 +02:00
def _get_obj_description(self, sample):
cursor = Transaction().connection.cursor()
ObjectiveDescription = Pool().get('lims.objective_description')
if not sample.product_type or not sample.matrix:
return None
cursor.execute('SELECT id '
'FROM "' + ObjectiveDescription._table + '" '
'WHERE product_type = %s '
'AND matrix = %s',
(sample.product_type.id, sample.matrix.id))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
def generate_repetition(self, notebook_lines):
pool = Pool()
Analysis = pool.get('lims.analysis')
Notebook = pool.get('lims.notebook')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
analysis_to_repeat = {}
for notebook_line in notebook_lines:
if notebook_line.analysis.id not in p_analysis_ids:
continue
if notebook_line.analysis.id not in analysis_to_repeat:
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
elif (notebook_line.repetition >
analysis_to_repeat[notebook_line.analysis.id].repetition):
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
notebook = Notebook(notebook_lines[0].notebook.id)
2017-10-08 02:23:22 +02:00
to_create = []
2019-03-04 15:41:58 +01:00
for analysis_id, nline in analysis_to_repeat.items():
2017-10-08 02:23:22 +02:00
to_create.append({
'analysis_detail': nline.analysis_detail.id,
'service': nline.service.id,
'analysis': analysis_id,
'analysis_origin': nline.analysis_origin,
'repetition': nline.repetition + 1,
'laboratory': nline.laboratory.id,
'method': nline.method.id,
'device': nline.device.id if nline.device else None,
'initial_concentration': nline.initial_concentration,
'final_concentration': nline.final_concentration,
'initial_unit': (nline.initial_unit.id if
nline.initial_unit else None),
'final_unit': (nline.final_unit.id if
nline.final_unit else None),
'detection_limit': nline.detection_limit,
'quantification_limit': nline.quantification_limit,
'decimals': nline.decimals,
'report': nline.report,
'concentration_level': (nline.concentration_level.id if
nline.concentration_level else None),
'results_estimated_waiting': nline.results_estimated_waiting,
'department': (nline.department.id if
nline.department else None),
2017-10-08 02:23:22 +02:00
})
Notebook.write([notebook], {
2017-10-08 02:23:22 +02:00
'lines': [('create', to_create)],
})
def add_control(self, fraction):
Planification = Pool().get('lims.planification')
Planification.write([self.start.planification], {
2017-10-08 02:23:22 +02:00
'controls': [('add', [fraction.id])],
})
def add_planification_detail(self, fraction):
pool = Pool()
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
clause = [
('notebook.fraction', '=', fraction.id),
('analysis', 'in', p_analysis_ids),
('analysis.behavior', '!=', 'internal_relation'),
]
if self.start.type == 'exist':
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
])
else:
clause.append(('planification', '=', None))
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if notebook_lines:
details_to_create = {}
for nl in notebook_lines:
f = nl.notebook.fraction.id
s = nl.service.analysis.id
if (f, s) not in details_to_create:
details_to_create[(f, s)] = []
details_to_create[(f, s)].append({
'notebook_line': nl.id,
'planned_service': s,
'is_control': True,
})
if details_to_create:
2019-03-04 15:41:58 +01:00
for k, v in details_to_create.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', self.start.planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
PlanificationDetail.create([{
2017-10-08 02:23:22 +02:00
'planification': self.start.planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
}])
class AddFractionRMBMZStart(ModelView):
2017-10-08 02:23:22 +02:00
'Add Fraction RM/BMZ'
__name__ = 'lims.planification.add_fraction_rm_bmz.start'
planification = fields.Many2One('lims.planification', 'Planification')
type = fields.Selection([
('rm', 'RM'),
('bmz', 'BMZ'),
], 'Control type', sort=False, required=True)
rm_bmz_type = fields.Selection([
('sla', 'SLA'),
('noref', 'No Reference'),
('exist', 'Existing RM/BMZ'),
], 'RM/BMZ type', sort=False, required=True)
reference_fraction = fields.Many2One('lims.fraction',
'Reference fraction', depends=['fraction_domain', 'rm_bmz_type'],
states={
'readonly': Bool(Equal(Eval('rm_bmz_type'), 'noref')),
'required': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
}, domain=[('id', 'in', Eval('fraction_domain'))])
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
None, 'Fraction domain'), 'on_change_with_fraction_domain')
product_type = fields.Many2One('lims.product.type', 'Product type',
states={
'readonly': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
'required': Bool(Equal(Eval('rm_bmz_type'), 'noref'))},
domain=[('id', 'in', Eval('product_type_domain'))],
depends=['rm_bmz_type', 'product_type_domain'])
product_type_domain = fields.Function(fields.Many2Many(
'lims.product.type', None, None, 'Product type domain'),
'on_change_with_product_type_domain')
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
states={
'readonly': Not(Bool(Equal(Eval('rm_bmz_type'), 'noref'))),
'required': Bool(Equal(Eval('rm_bmz_type'), 'noref'))},
domain=[('id', 'in', Eval('matrix_domain'))],
depends=['rm_bmz_type', 'matrix_domain'])
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
None, None, 'Matrix domain'),
'on_change_with_matrix_domain')
repetitions = fields.Integer('Repetitions',
states={'readonly': Or(Eval('type') == 'bmz',
Eval('rm_bmz_type') == 'exist')},
depends=['type', 'rm_bmz_type'])
2017-10-08 02:23:22 +02:00
label = fields.Char('Label', depends=['rm_bmz_type'], states={
'readonly': Eval('rm_bmz_type') == 'exist'})
concentration_level = fields.Many2One('lims.concentration.level',
'Concentration level', states={
'invisible': Bool(Eval('concentration_level_invisible')),
}, depends=['concentration_level_invisible'])
concentration_level_invisible = fields.Boolean(
'Concentration level invisible')
@fields.depends('type')
def on_change_with_concentration_level_invisible(self, name=None):
Config = Pool().get('lims.configuration')
config = Config(1)
if self.type == 'rm':
if (config.rm_fraction_type and
config.rm_fraction_type.control_charts):
return False
elif self.type == 'bmz':
if (config.bmz_fraction_type and
config.bmz_fraction_type.control_charts):
return False
return True
2020-03-05 05:24:51 +01:00
@fields.depends('planification', 'type', 'rm_bmz_type',
'_parent_planification.analysis')
2017-10-08 02:23:22 +02:00
def on_change_with_fraction_domain(self, name=None):
cursor = Transaction().connection.cursor()
pool = Pool()
Analysis = pool.get('lims.analysis')
Fraction = pool.get('lims.fraction')
NotebookLine = pool.get('lims.notebook.line')
Notebook = pool.get('lims.notebook')
2017-10-08 02:23:22 +02:00
if not self.type or not self.rm_bmz_type:
return []
if self.rm_bmz_type == 'noref':
return []
p_analysis_ids = []
for p_analysis in self.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
stored_fractions_ids = Fraction.get_stored_fractions()
2017-10-08 02:23:22 +02:00
special_type = 'sla' if self.rm_bmz_type == 'sla' else self.type
clause = [
('notebook.fraction.special_type', '=', special_type),
('notebook.fraction.id', 'in', stored_fractions_ids),
('analysis', 'in', p_analysis_ids),
]
if self.rm_bmz_type == 'exist':
deadline = datetime.now() - relativedelta(days=5)
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
('notebook.fraction.sample.date2', '>=', deadline),
])
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if not notebook_lines:
return []
notebook_lines_ids = ', '.join(str(nl.id) for nl in notebook_lines)
cursor.execute('SELECT DISTINCT(n.fraction) '
'FROM "' + Notebook._table + '" n '
'INNER JOIN "' + NotebookLine._table + '" nl '
2017-10-08 02:23:22 +02:00
'ON nl.notebook = n.id '
'WHERE nl.id IN (' + notebook_lines_ids + ')')
return [x[0] for x in cursor.fetchall()]
@staticmethod
def default_product_type_domain():
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
cursor.execute('SELECT DISTINCT(product_type) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE valid')
return [x[0] for x in cursor.fetchall()]
def on_change_with_product_type_domain(self, name=None):
return self.default_product_type_domain()
@fields.depends('product_type')
def on_change_product_type(self):
matrix = None
if self.product_type:
matrixs = self.on_change_with_matrix_domain()
if len(matrixs) == 1:
matrix = matrixs[0]
self.matrix = matrix
@fields.depends('product_type')
def on_change_with_matrix_domain(self, name=None):
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
if not self.product_type:
return []
cursor.execute('SELECT DISTINCT(matrix) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE product_type = %s '
'AND valid',
(self.product_type.id,))
return [x[0] for x in cursor.fetchall()]
@fields.depends('type', 'rm_bmz_type', 'reference_fraction',
2020-03-05 05:24:51 +01:00
'product_type', 'matrix', 'concentration_level',
'_parent_reference_fraction.label',
'_parent_concentration_level.description')
2017-10-08 02:23:22 +02:00
def on_change_with_label(self, name=None):
Date = Pool().get('ir.date')
if self.rm_bmz_type == 'exist':
return ''
label = ''
if self.type == 'rm':
label = 'RM'
if self.concentration_level:
label += (' (' +
self.concentration_level.description + ')')
elif self.type == 'bmz':
label = 'BMZ'
if self.rm_bmz_type == 'sla':
if self.reference_fraction:
label += (' ' +
self.reference_fraction.label)
label += ' ' + str(Date.today())
elif self.rm_bmz_type == 'noref':
label += ' ' + str(Date.today())
return label
class AddFractionRMBMZ(Wizard):
2017-10-08 02:23:22 +02:00
'Add Fraction RM/BMZ'
__name__ = 'lims.planification.add_fraction_rm_bmz'
start = StateView('lims.planification.add_fraction_rm_bmz.start',
'lims.lims_add_fraction_rm_bmz_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
defaults = {
'planification': Transaction().context['active_id'],
'concentration_level_invisible': True,
}
return defaults
def transition_add(self):
fraction = self.start.reference_fraction
if self.start.rm_bmz_type != 'exist':
fraction = self.create_control()
self.add_control(fraction)
self.add_planification_detail(fraction)
return 'end'
def create_control(self):
if self.start.rm_bmz_type == 'sla':
return self._create_control_sla()
if self.start.rm_bmz_type == 'noref':
return self._create_control_noref()
def _create_control_sla(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
2017-10-08 02:23:22 +02:00
config = Config(1)
if self.start.type == 'rm':
if not config.rm_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_rm_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.rm_fraction_type
elif self.start.type == 'bmz':
if not config.bmz_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_bmz_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.bmz_fraction_type
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
entry = Entry(workyear.default_entry_control.id)
2017-10-08 02:23:22 +02:00
original_fraction = self.start.reference_fraction
original_sample = Sample(original_fraction.sample.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(original_sample)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.copy([original_sample], default={
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'fractions': [],
})
# new fraction
fraction_default = {
'sample': new_sample.id,
'type': fraction_type.id,
'con_type': '',
'services': [],
}
if self.start.type == 'rm':
fraction_default['rm_type'] = 'sla'
fraction_default['rm_product_type'] = new_sample.product_type.id
fraction_default['rm_matrix'] = new_sample.matrix.id
fraction_default['rm_original_fraction'] = original_fraction.id
if self.start.type == 'bmz':
fraction_default['bmz_type'] = 'sla'
fraction_default['bmz_product_type'] = new_sample.product_type.id
fraction_default['bmz_matrix'] = new_sample.matrix.id
fraction_default['bmz_original_fraction'] = original_fraction.id
new_fraction, = Fraction.copy([original_fraction],
2017-10-08 02:23:22 +02:00
default=fraction_default)
# new services
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
services = Service.search([
2017-10-08 02:23:22 +02:00
('fraction', '=', original_fraction),
])
for service in services:
if not Analysis.is_typified(service.analysis,
2017-10-08 02:23:22 +02:00
new_sample.product_type, new_sample.matrix):
continue
method_id = service.method and service.method.id or None
device_id = service.device and service.device.id or None
if service.analysis.type == 'analysis':
original_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', original_fraction.id),
('analysis', '=', service.analysis.id),
('repetition', '=', 0),
], limit=1)
original_line = original_lines[0] if original_lines else None
if original_line:
method_id = original_line.method.id
if original_line.device:
device_id = original_line.device.id
new_service, = Service.copy([service], default={
2017-10-08 02:23:22 +02:00
'fraction': new_fraction.id,
'method': method_id,
'device': device_id,
})
# delete services/details not related to planification
to_delete = EntryDetailAnalysis.search([
('service', '=', new_service.id),
('analysis', 'not in', p_analysis_ids),
])
if to_delete:
with Transaction().set_user(0, set_context=True):
EntryDetailAnalysis.delete(to_delete)
if EntryDetailAnalysis.search_count([
('service', '=', new_service.id),
]) == 0:
with Transaction().set_user(0, set_context=True):
Service.delete([new_service])
2017-10-08 02:23:22 +02:00
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
if self.start.type == 'rm':
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'final_concentration': None,
'final_unit': None,
'detection_limit': None,
'quantification_limit': None,
}
if config.rm_start_uom:
defaults['initial_unit'] = config.rm_start_uom.id
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
# Generate repetition
if self.start.repetitions and self.start.repetitions > 0:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
self.generate_repetition(notebook_lines,
self.start.repetitions)
2017-10-08 02:23:22 +02:00
return new_fraction
2019-06-14 00:38:02 +02:00
def _get_obj_description(self, sample):
cursor = Transaction().connection.cursor()
ObjectiveDescription = Pool().get('lims.objective_description')
if not sample.product_type or not sample.matrix:
return None
cursor.execute('SELECT id '
'FROM "' + ObjectiveDescription._table + '" '
'WHERE product_type = %s '
'AND matrix = %s',
(sample.product_type.id, sample.matrix.id))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
def _create_control_noref(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
config = Config(1)
if self.start.type == 'rm':
if not config.rm_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_rm_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.rm_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_no_rm_default_configuration'))
2017-10-08 02:23:22 +02:00
elif self.start.type == 'bmz':
if not config.bmz_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_bmz_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.bmz_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_no_bmz_default_configuration'))
2017-10-08 02:23:22 +02:00
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
laboratory = self.start.planification.laboratory
entry = Entry(workyear.default_entry_control.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(self.start)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.create([{
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'product_type': self.start.product_type.id,
'matrix': self.start.matrix.id,
'zone': entry.party.entry_zone.id,
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'packages_quantity': 1,
'fractions': [],
}])
# new fraction
fraction_default = {
'sample': new_sample.id,
'type': fraction_type.id,
'storage_location': laboratory.related_location.id,
'packages_quantity': 1,
'package_type': fraction_type.default_package_type.id,
'fraction_state': fraction_type.default_fraction_state.id,
'services': [],
}
if fraction_type.max_storage_time:
fraction_default['storage_time'] = fraction_type.max_storage_time
elif laboratory.related_location.storage_time:
fraction_default['storage_time'] = (
laboratory.related_location.storage_time)
else:
fraction_default['storage_time'] = 3
if self.start.type == 'rm':
fraction_default['rm_type'] = 'noinitialrm'
fraction_default['rm_product_type'] = new_sample.product_type.id
fraction_default['rm_matrix'] = new_sample.matrix.id
if self.start.type == 'bmz':
fraction_default['bmz_type'] = 'noinitialbmz'
fraction_default['bmz_product_type'] = new_sample.product_type.id
fraction_default['bmz_matrix'] = new_sample.matrix.id
new_fraction, = Fraction.create([fraction_default])
2017-10-08 02:23:22 +02:00
# new services
services_default = []
for p_analysis in self.start.planification.analysis:
if not Analysis.is_typified(p_analysis,
2017-10-08 02:23:22 +02:00
new_sample.product_type, new_sample.matrix):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_not_typified',
analysis=p_analysis.rec_name,
product_type=new_sample.product_type.rec_name,
matrix=new_sample.matrix.rec_name,
))
2017-10-08 02:23:22 +02:00
laboratory_id = (laboratory.id if p_analysis.type != 'group'
else None)
method_id = None
if new_sample.typification_domain:
for t in new_sample.typification_domain:
if (t.analysis.id == p_analysis.id and
t.by_default is True):
2017-10-08 02:23:22 +02:00
method_id = t.method.id
device_id = None
if p_analysis.devices:
for d in p_analysis.devices:
if (d.laboratory.id == laboratory.id and
d.by_default is True):
2017-10-08 02:23:22 +02:00
device_id = d.device.id
services_default.append({
'fraction': new_fraction.id,
'analysis': p_analysis.id,
'laboratory': laboratory_id,
'method': method_id,
'device': device_id,
})
for service in services_default:
new_service, = Service.create([service])
2017-10-08 02:23:22 +02:00
# new analysis details (on service create)
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
if self.start.type == 'rm':
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'final_concentration': None,
'final_unit': None,
'detection_limit': None,
'quantification_limit': None,
}
if config.rm_start_uom:
defaults['initial_unit'] = config.rm_start_uom.id
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
# Generate repetition
if self.start.repetitions and self.start.repetitions > 0:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
self.generate_repetition(notebook_lines,
self.start.repetitions)
2017-10-08 02:23:22 +02:00
return new_fraction
def generate_repetition(self, notebook_lines, repetitions):
2017-10-08 02:23:22 +02:00
pool = Pool()
Analysis = pool.get('lims.analysis')
Notebook = pool.get('lims.notebook')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
analysis_to_repeat = {}
for notebook_line in notebook_lines:
if notebook_line.analysis.id not in p_analysis_ids:
continue
if notebook_line.analysis.id not in analysis_to_repeat:
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
elif (notebook_line.repetition >
analysis_to_repeat[notebook_line.analysis.id].repetition):
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
notebook = Notebook(notebook_lines[0].notebook.id)
2017-10-08 02:23:22 +02:00
to_create = []
2019-03-04 15:41:58 +01:00
for nline in analysis_to_repeat.values():
for i in range(1, repetitions + 1):
to_create.append({
'analysis_detail': nline.analysis_detail.id,
'service': nline.service.id,
'analysis': nline.analysis.id,
'analysis_origin': nline.analysis_origin,
'repetition': nline.repetition + i,
'laboratory': nline.laboratory.id,
'method': nline.method.id,
'device': nline.device.id if nline.device else None,
'initial_concentration': nline.initial_concentration,
'final_concentration': nline.final_concentration,
'initial_unit': (nline.initial_unit.id if
nline.initial_unit else None),
'final_unit': (nline.final_unit.id if
nline.final_unit else None),
'detection_limit': nline.detection_limit,
'quantification_limit': nline.quantification_limit,
'decimals': nline.decimals,
'report': nline.report,
'concentration_level': (nline.concentration_level.id if
nline.concentration_level else None),
'results_estimated_waiting': (
nline.results_estimated_waiting),
'department': (nline.department.id if
nline.department else None),
})
Notebook.write([notebook], {
2017-10-08 02:23:22 +02:00
'lines': [('create', to_create)],
})
def add_control(self, fraction):
Planification = Pool().get('lims.planification')
Planification.write([self.start.planification], {
2017-10-08 02:23:22 +02:00
'controls': [('add', [fraction.id])],
})
def add_planification_detail(self, fraction):
pool = Pool()
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
clause = [
('notebook.fraction', '=', fraction.id),
('analysis', 'in', p_analysis_ids),
('analysis.behavior', '!=', 'internal_relation'),
]
if self.start.rm_bmz_type == 'exist':
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
])
else:
clause.append(('planification', '=', None))
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if notebook_lines:
details_to_create = {}
for nl in notebook_lines:
f = nl.notebook.fraction.id
s = nl.service.analysis.id
if (f, s) not in details_to_create:
details_to_create[(f, s)] = []
details_to_create[(f, s)].append({
'notebook_line': nl.id,
'planned_service': s,
'is_control': True,
})
if details_to_create:
2019-03-04 15:41:58 +01:00
for k, v in details_to_create.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', self.start.planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
PlanificationDetail.create([{
2017-10-08 02:23:22 +02:00
'planification': self.start.planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
}])
class AddFractionBREStart(ModelView):
2017-10-08 02:23:22 +02:00
'Add Fraction BRE'
__name__ = 'lims.planification.add_fraction_bre.start'
planification = fields.Many2One('lims.planification', 'Planification')
type = fields.Selection([
('new', 'New BRE'),
('exist', 'Existing BRE'),
], 'BRE Type', sort=False, required=True)
bre_fraction = fields.Many2One('lims.fraction',
'BRE fraction', depends=['fraction_domain', 'type'],
states={
'readonly': Bool(Equal(Eval('type'), 'new')),
'required': Bool(Equal(Eval('type'), 'exist')),
}, domain=[('id', 'in', Eval('fraction_domain'))])
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
None, 'Fraction domain'), 'on_change_with_fraction_domain')
product_type = fields.Many2One('lims.product.type', 'Product type',
states={'required': Bool(Equal(Eval('type'), 'new'))},
domain=[('id', 'in', Eval('product_type_domain'))],
depends=['type', 'product_type_domain'])
product_type_domain = fields.Function(fields.Many2Many(
'lims.product.type', None, None, 'Product type domain'),
'on_change_with_product_type_domain')
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
states={'required': Bool(Equal(Eval('type'), 'new'))},
domain=[('id', 'in', Eval('matrix_domain'))],
depends=['type', 'matrix_domain'])
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
None, None, 'Matrix domain'),
'on_change_with_matrix_domain')
reagents = fields.One2Many('lims.fraction.reagent', None,
'Reagents', states={
'readonly': Bool(Equal(Eval('type'), 'exist')),
}, depends=['type'], required=True)
label = fields.Char('Label', depends=['type'], states={
'readonly': Eval('type') == 'exist'})
concentration_level = fields.Many2One('lims.concentration.level',
'Concentration level', states={
'invisible': Bool(Eval('concentration_level_invisible')),
}, depends=['concentration_level_invisible'])
concentration_level_invisible = fields.Boolean(
'Concentration level invisible')
2020-03-05 05:24:51 +01:00
@fields.depends('planification', 'type', '_parent_planification.analysis')
2017-10-08 02:23:22 +02:00
def on_change_with_fraction_domain(self, name=None):
pool = Pool()
Analysis = pool.get('lims.analysis')
Fraction = pool.get('lims.fraction')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
if self.type != 'exist':
return []
p_analysis_ids = []
for p_analysis in self.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
stored_fractions_ids = Fraction.get_stored_fractions()
2017-10-08 02:23:22 +02:00
deadline = datetime.now() - relativedelta(days=5)
clause = [
('notebook.fraction.special_type', '=', 'bre'),
('notebook.fraction.id', 'in', stored_fractions_ids),
('analysis', 'in', p_analysis_ids),
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
('notebook.fraction.sample.date2', '>=', deadline),
]
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
fractions = [nl.notebook.fraction.id for nl in notebook_lines]
return list(set(fractions))
@staticmethod
def default_product_type_domain():
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
cursor.execute('SELECT DISTINCT(product_type) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE valid')
return [x[0] for x in cursor.fetchall()]
def on_change_with_product_type_domain(self, name=None):
return self.default_product_type_domain()
@fields.depends('product_type')
def on_change_product_type(self):
matrix = None
if self.product_type:
matrixs = self.on_change_with_matrix_domain()
if len(matrixs) == 1:
matrix = matrixs[0]
self.matrix = matrix
@fields.depends('product_type')
def on_change_with_matrix_domain(self, name=None):
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
if not self.product_type:
return []
cursor.execute('SELECT DISTINCT(matrix) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE product_type = %s '
'AND valid',
(self.product_type.id,))
return [x[0] for x in cursor.fetchall()]
@fields.depends('type', 'product_type', 'matrix')
def on_change_with_label(self, name=None):
Date = Pool().get('ir.date')
if self.type == 'exist':
return ''
label = 'BRE'
label += ' ' + str(Date.today())
return label
class AddFractionBRE(Wizard):
2017-10-08 02:23:22 +02:00
'Add Fraction BRE'
__name__ = 'lims.planification.add_fraction_bre'
start = StateView('lims.planification.add_fraction_bre.start',
'lims.lims_add_fraction_bre_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
Config = Pool().get('lims.configuration')
config = Config(1)
defaults = {
'planification': Transaction().context['active_id'],
'concentration_level_invisible': True,
}
if (config.bre_fraction_type and
config.bre_fraction_type.control_charts):
defaults['concentration_level_invisible'] = False
return defaults
def transition_add(self):
fraction = self.start.bre_fraction
if self.start.type == 'new':
fraction = self.create_control()
self.add_control(fraction)
self.add_planification_detail(fraction)
return 'end'
def create_control(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
config = Config(1)
if not config.bre_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_bre_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.bre_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_bre_default_configuration'))
2017-10-08 02:23:22 +02:00
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
laboratory = self.start.planification.laboratory
entry = Entry(workyear.default_entry_control.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(self.start)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.create([{
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'product_type': self.start.product_type.id,
'matrix': self.start.matrix.id,
'zone': entry.party.entry_zone.id,
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'packages_quantity': 1,
'fractions': [],
}])
# new fraction
bre_reagents = [{
'product': r.product.id,
'lot': r.lot.id if r.lot else None,
} for r in self.start.reagents]
fraction_default = {
'sample': new_sample.id,
'type': fraction_type.id,
'storage_location': laboratory.related_location.id,
'packages_quantity': 1,
'package_type': fraction_type.default_package_type.id,
'fraction_state': fraction_type.default_fraction_state.id,
'services': [],
'bre_product_type': new_sample.product_type.id,
'bre_matrix': new_sample.matrix.id,
'bre_reagents': [('create', bre_reagents)],
}
if fraction_type.max_storage_time:
fraction_default['storage_time'] = fraction_type.max_storage_time
elif laboratory.related_location.storage_time:
fraction_default['storage_time'] = (
laboratory.related_location.storage_time)
else:
fraction_default['storage_time'] = 3
new_fraction, = Fraction.create([fraction_default])
2017-10-08 02:23:22 +02:00
# new services
services_default = []
for p_analysis in self.start.planification.analysis:
if not Analysis.is_typified(p_analysis,
2017-10-08 02:23:22 +02:00
new_sample.product_type, new_sample.matrix):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_not_typified',
analysis=p_analysis.rec_name,
product_type=new_sample.product_type.rec_name,
matrix=new_sample.matrix.rec_name,
))
2017-10-08 02:23:22 +02:00
laboratory_id = (laboratory.id if p_analysis.type != 'group'
else None)
method_id = None
if new_sample.typification_domain:
for t in new_sample.typification_domain:
if (t.analysis.id == p_analysis.id and
t.by_default is True):
2017-10-08 02:23:22 +02:00
method_id = t.method.id
device_id = None
if p_analysis.devices:
for d in p_analysis.devices:
if (d.laboratory.id == laboratory.id and
d.by_default is True):
2017-10-08 02:23:22 +02:00
device_id = d.device.id
services_default.append({
'fraction': new_fraction.id,
'analysis': p_analysis.id,
'laboratory': laboratory_id,
'method': method_id,
'device': device_id,
})
for service in services_default:
new_service, = Service.create([service])
2017-10-08 02:23:22 +02:00
# new analysis details (on service create)
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
return new_fraction
2019-06-14 00:38:02 +02:00
def _get_obj_description(self, sample):
cursor = Transaction().connection.cursor()
ObjectiveDescription = Pool().get('lims.objective_description')
if not sample.product_type or not sample.matrix:
return None
cursor.execute('SELECT id '
'FROM "' + ObjectiveDescription._table + '" '
'WHERE product_type = %s '
'AND matrix = %s',
(sample.product_type.id, sample.matrix.id))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
def add_control(self, fraction):
Planification = Pool().get('lims.planification')
Planification.write([self.start.planification], {
2017-10-08 02:23:22 +02:00
'controls': [('add', [fraction.id])],
})
def add_planification_detail(self, fraction):
pool = Pool()
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
clause = [
('notebook.fraction', '=', fraction.id),
('analysis', 'in', p_analysis_ids),
('analysis.behavior', '!=', 'internal_relation'),
]
if self.start.type == 'exist':
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
])
else:
clause.append(('planification', '=', None))
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if notebook_lines:
details_to_create = {}
for nl in notebook_lines:
f = nl.notebook.fraction.id
s = nl.service.analysis.id
if (f, s) not in details_to_create:
details_to_create[(f, s)] = []
details_to_create[(f, s)].append({
'notebook_line': nl.id,
'planned_service': s,
'is_control': True,
})
if details_to_create:
2019-03-04 15:41:58 +01:00
for k, v in details_to_create.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', self.start.planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
PlanificationDetail.create([{
2017-10-08 02:23:22 +02:00
'planification': self.start.planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
}])
class AddFractionMRTStart(ModelView):
2017-10-08 02:23:22 +02:00
'Add Fraction MRT'
__name__ = 'lims.planification.add_fraction_mrt.start'
planification = fields.Many2One('lims.planification', 'Planification')
type = fields.Selection([
('new', 'New MRT'),
('exist', 'Existing MRT'),
], 'MRT Type', sort=False, required=True)
mrt_fraction = fields.Many2One('lims.fraction',
'MRT fraction', depends=['fraction_domain', 'type'],
states={
'readonly': Bool(Equal(Eval('type'), 'new')),
'required': Bool(Equal(Eval('type'), 'exist')),
}, domain=[('id', 'in', Eval('fraction_domain'))])
fraction_domain = fields.Function(fields.One2Many('lims.fraction',
None, 'Fraction domain'), 'on_change_with_fraction_domain')
product_type = fields.Many2One('lims.product.type', 'Product type',
states={'required': Bool(Equal(Eval('type'), 'new'))},
domain=[('id', 'in', Eval('product_type_domain'))],
depends=['type', 'product_type_domain'])
product_type_domain = fields.Function(fields.Many2Many(
'lims.product.type', None, None, 'Product type domain'),
'on_change_with_product_type_domain')
matrix = fields.Many2One('lims.matrix', 'Matrix', required=True,
states={'required': Bool(Equal(Eval('type'), 'new'))},
domain=[('id', 'in', Eval('matrix_domain'))],
depends=['type', 'matrix_domain'])
matrix_domain = fields.Function(fields.Many2Many('lims.matrix',
None, None, 'Matrix domain'),
'on_change_with_matrix_domain')
repetitions = fields.Integer('Repetitions')
label = fields.Char('Label', depends=['type'], states={
'readonly': Eval('type') == 'exist'})
concentration_level = fields.Many2One('lims.concentration.level',
'Concentration level', states={
'invisible': Bool(Eval('concentration_level_invisible')),
}, depends=['concentration_level_invisible'])
concentration_level_invisible = fields.Boolean(
'Concentration level invisible')
2020-03-05 05:24:51 +01:00
@fields.depends('planification', 'type', '_parent_planification.analysis')
2017-10-08 02:23:22 +02:00
def on_change_with_fraction_domain(self, name=None):
pool = Pool()
Analysis = pool.get('lims.analysis')
Fraction = pool.get('lims.fraction')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
if self.type != 'exist':
return []
p_analysis_ids = []
for p_analysis in self.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
stored_fractions_ids = Fraction.get_stored_fractions()
2017-10-08 02:23:22 +02:00
deadline = datetime.now() - relativedelta(days=5)
clause = [
('notebook.fraction.special_type', '=', 'mrt'),
('notebook.fraction.id', 'in', stored_fractions_ids),
('analysis', 'in', p_analysis_ids),
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
('notebook.fraction.sample.date2', '>=', deadline),
]
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
fractions = [nl.notebook.fraction.id for nl in notebook_lines]
return list(set(fractions))
@staticmethod
def default_product_type_domain():
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
cursor.execute('SELECT DISTINCT(product_type) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE valid')
return [x[0] for x in cursor.fetchall()]
def on_change_with_product_type_domain(self, name=None):
return self.default_product_type_domain()
@fields.depends('product_type')
def on_change_product_type(self):
matrix = None
if self.product_type:
matrixs = self.on_change_with_matrix_domain()
if len(matrixs) == 1:
matrix = matrixs[0]
self.matrix = matrix
@fields.depends('product_type')
def on_change_with_matrix_domain(self, name=None):
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
if not self.product_type:
return []
cursor.execute('SELECT DISTINCT(matrix) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE product_type = %s '
'AND valid',
(self.product_type.id,))
return [x[0] for x in cursor.fetchall()]
@fields.depends('type', 'product_type', 'matrix')
def on_change_with_label(self, name=None):
Date = Pool().get('ir.date')
if self.type == 'exist':
return ''
label = 'MRT'
label += ' ' + str(Date.today())
return label
class AddFractionMRT(Wizard):
2017-10-08 02:23:22 +02:00
'Add Fraction MRT'
__name__ = 'lims.planification.add_fraction_mrt'
start = StateView('lims.planification.add_fraction_mrt.start',
'lims.lims_add_fraction_mrt_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
Config = Pool().get('lims.configuration')
config = Config(1)
defaults = {
'planification': Transaction().context['active_id'],
'concentration_level_invisible': True,
}
if (config.mrt_fraction_type and
config.mrt_fraction_type.control_charts):
defaults['concentration_level_invisible'] = False
return defaults
def transition_add(self):
fraction = self.start.mrt_fraction
if self.start.type == 'new':
fraction = self.create_control()
self.add_control(fraction)
self.add_planification_detail(fraction)
return 'end'
def create_control(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
config = Config(1)
if not config.mrt_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_mrt_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.mrt_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_mrt_default_configuration'))
2017-10-08 02:23:22 +02:00
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
laboratory = self.start.planification.laboratory
entry = Entry(workyear.default_entry_control.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(self.start)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.create([{
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'product_type': self.start.product_type.id,
'matrix': self.start.matrix.id,
'zone': entry.party.entry_zone.id,
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'packages_quantity': 1,
'fractions': [],
}])
# new fraction
fraction_default = {
'sample': new_sample.id,
'type': fraction_type.id,
'storage_location': laboratory.related_location.id,
'packages_quantity': 1,
'package_type': fraction_type.default_package_type.id,
'fraction_state': fraction_type.default_fraction_state.id,
'services': [],
'mrt_product_type': new_sample.product_type.id,
'mrt_matrix': new_sample.matrix.id,
}
if fraction_type.max_storage_time:
fraction_default['storage_time'] = fraction_type.max_storage_time
elif laboratory.related_location.storage_time:
fraction_default['storage_time'] = (
laboratory.related_location.storage_time)
else:
fraction_default['storage_time'] = 3
new_fraction, = Fraction.create([fraction_default])
2017-10-08 02:23:22 +02:00
# new services
services_default = []
for p_analysis in self.start.planification.analysis:
if not Analysis.is_typified(p_analysis,
2017-10-08 02:23:22 +02:00
new_sample.product_type, new_sample.matrix):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_not_typified',
analysis=p_analysis.rec_name,
product_type=new_sample.product_type.rec_name,
matrix=new_sample.matrix.rec_name,
))
2017-10-08 02:23:22 +02:00
laboratory_id = (laboratory.id if p_analysis.type != 'group'
else None)
method_id = None
if new_sample.typification_domain:
for t in new_sample.typification_domain:
if (t.analysis.id == p_analysis.id and
t.by_default is True):
2017-10-08 02:23:22 +02:00
method_id = t.method.id
device_id = None
if p_analysis.devices:
for d in p_analysis.devices:
if (d.laboratory.id == laboratory.id and
d.by_default is True):
2017-10-08 02:23:22 +02:00
device_id = d.device.id
services_default.append({
'fraction': new_fraction.id,
'analysis': p_analysis.id,
'laboratory': laboratory_id,
'method': method_id,
'device': device_id,
})
for service in services_default:
new_service, = Service.create([service])
2017-10-08 02:23:22 +02:00
# new analysis details (on service create)
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
# Generate repetition
if self.start.repetitions and self.start.repetitions > 0:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
self.generate_repetition(notebook_lines,
self.start.repetitions)
return new_fraction
2019-06-14 00:38:02 +02:00
def _get_obj_description(self, sample):
cursor = Transaction().connection.cursor()
ObjectiveDescription = Pool().get('lims.objective_description')
if not sample.product_type or not sample.matrix:
return None
cursor.execute('SELECT id '
'FROM "' + ObjectiveDescription._table + '" '
'WHERE product_type = %s '
'AND matrix = %s',
(sample.product_type.id, sample.matrix.id))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
def generate_repetition(self, notebook_lines, repetitions):
pool = Pool()
Analysis = pool.get('lims.analysis')
Notebook = pool.get('lims.notebook')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
analysis_to_repeat = {}
for notebook_line in notebook_lines:
if notebook_line.analysis.id not in p_analysis_ids:
continue
if notebook_line.analysis.id not in analysis_to_repeat:
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
elif (notebook_line.repetition >
analysis_to_repeat[notebook_line.analysis.id].repetition):
analysis_to_repeat[notebook_line.analysis.id] = notebook_line
notebook = Notebook(notebook_lines[0].notebook.id)
2017-10-08 02:23:22 +02:00
to_create = []
2019-03-04 15:41:58 +01:00
for nline in analysis_to_repeat.values():
2017-10-08 02:23:22 +02:00
for i in range(1, repetitions + 1):
to_create.append({
'analysis_detail': nline.analysis_detail.id,
'service': nline.service.id,
'analysis': nline.analysis.id,
'analysis_origin': nline.analysis_origin,
'repetition': nline.repetition + i,
'laboratory': nline.laboratory.id,
'method': nline.method.id,
'device': nline.device.id if nline.device else None,
'initial_concentration': nline.initial_concentration,
'final_concentration': nline.final_concentration,
'initial_unit': (nline.initial_unit.id if
nline.initial_unit else None),
'final_unit': (nline.final_unit.id if
nline.final_unit else None),
'detection_limit': nline.detection_limit,
'quantification_limit': nline.quantification_limit,
'decimals': nline.decimals,
'report': nline.report,
'concentration_level': (nline.concentration_level.id if
nline.concentration_level else None),
'results_estimated_waiting': (
nline.results_estimated_waiting),
'department': (nline.department.id if
nline.department else None),
2017-10-08 02:23:22 +02:00
})
Notebook.write([notebook], {
2017-10-08 02:23:22 +02:00
'lines': [('create', to_create)],
})
def add_control(self, fraction):
Planification = Pool().get('lims.planification')
Planification.write([self.start.planification], {
2017-10-08 02:23:22 +02:00
'controls': [('add', [fraction.id])],
})
def add_planification_detail(self, fraction):
pool = Pool()
Analysis = pool.get('lims.analysis')
NotebookLine = pool.get('lims.notebook.line')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
p_analysis_ids = []
for p_analysis in self.start.planification.analysis:
if p_analysis.type == 'analysis':
p_analysis_ids.append(p_analysis.id)
else:
p_analysis_ids.extend(
Analysis.get_included_analysis_analysis(p_analysis.id))
2017-10-08 02:23:22 +02:00
clause = [
('notebook.fraction', '=', fraction.id),
('analysis', 'in', p_analysis_ids),
('analysis.behavior', '!=', 'internal_relation'),
]
if self.start.type == 'exist':
clause.extend([
('result', 'in', (None, '')),
('end_date', '=', None),
('annulment_date', '=', None),
])
else:
clause.append(('planification', '=', None))
notebook_lines = NotebookLine.search(clause)
2017-10-08 02:23:22 +02:00
if notebook_lines:
details_to_create = {}
for nl in notebook_lines:
f = nl.notebook.fraction.id
s = nl.service.analysis.id
if (f, s) not in details_to_create:
details_to_create[(f, s)] = []
details_to_create[(f, s)].append({
'notebook_line': nl.id,
'planned_service': s,
'is_control': True,
})
if details_to_create:
2019-03-04 15:41:58 +01:00
for k, v in details_to_create.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', self.start.planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
PlanificationDetail.create([{
2017-10-08 02:23:22 +02:00
'planification': self.start.planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
}])
class RemoveControlStart(ModelView):
2017-10-08 02:23:22 +02:00
'Remove Control'
__name__ = 'lims.planification.remove_control.start'
controls = fields.Many2Many('lims.fraction', None, None, 'Controls',
required=True, domain=[('id', 'in', Eval('controls_domain'))],
depends=['controls_domain'])
controls_domain = fields.Many2Many('lims.fraction', None, None,
'Controls domain')
class RemoveControl(Wizard):
2017-10-08 02:23:22 +02:00
'Remove Control'
__name__ = 'lims.planification.remove_control'
start = StateView('lims.planification.remove_control.start',
'lims.lims_remove_control_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Remove', 'remove', 'tryton-ok', default=True),
])
remove = StateTransition()
def default_start(self, fields):
Planification = Pool().get('lims.planification')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
controls_domain = []
for c in planification.controls:
controls_domain.append(c.id)
return {
'controls_domain': controls_domain,
}
def transition_remove(self):
planification_id = Transaction().context['active_id']
control_ids = [c.id for c in self.start.controls]
self._unlink_controls(planification_id, control_ids)
return 'end'
def _unlink_controls(self, planification_id, control_ids):
pool = Pool()
PlanificationFraction = pool.get('lims.planification-fraction')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
controls = PlanificationFraction.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification_id),
('fraction', 'in', control_ids),
])
if controls:
PlanificationFraction.delete(controls)
2017-10-08 02:23:22 +02:00
controls_details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification_id),
('fraction', 'in', control_ids),
])
if controls_details:
PlanificationDetail.delete(controls_details)
2017-10-08 02:23:22 +02:00
class AddAnalysisStart(ModelView):
2017-10-08 02:23:22 +02:00
'Add Analysis'
__name__ = 'lims.planification.add_analysis.start'
laboratory = fields.Many2One('lims.laboratory', 'Laboratory')
date_from = fields.Date('Date from', required=True, readonly=True)
date_to = fields.Date('Date to', required=True, readonly=True)
analysis = fields.Many2Many('lims.analysis', None, None,
'Analysis/Sets/Groups', required=True,
domain=[('id', 'in', Eval('analysis_domain'))],
context={'date_from': Eval('date_from'), 'date_to': Eval('date_to')},
depends=['analysis_domain', 'date_from', 'date_to'])
analysis_domain = fields.Many2Many('lims.analysis', None, None,
'Analysis domain')
class AddAnalysis(Wizard):
2017-10-08 02:23:22 +02:00
'Add Analysis'
__name__ = 'lims.planification.add_analysis'
start = StateView('lims.planification.add_analysis.start',
'lims.lims_add_analysis_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
Planification = Pool().get('lims.planification')
planification = Planification(Transaction().context['active_id'])
analysis_domain = AddAnalysis._get_analysis_domain(
2017-10-08 02:23:22 +02:00
planification.laboratory, planification.date_from,
planification.date_to)
return {
'laboratory': planification.laboratory.id,
'analysis_domain': analysis_domain,
'date_from': planification.date_from,
'date_to': planification.date_to,
}
@staticmethod
def _get_analysis_domain(laboratory, date_from, date_to):
pool = Pool()
Planification = pool.get('lims.planification')
Analysis = pool.get('lims.analysis')
2017-10-08 02:23:22 +02:00
if not laboratory:
return []
asg_list = Planification._get_analysis_domain(laboratory)
2017-10-08 02:23:22 +02:00
new_context = {}
new_context['date_from'] = date_from
new_context['date_to'] = date_to
with Transaction().set_context(new_context):
pending_fractions = Analysis.analysis_pending_fractions(asg_list)
res = []
for analysis, pending in iter(pending_fractions.items()):
if pending > 0:
res.append(analysis)
return res
2017-10-08 02:23:22 +02:00
def transition_add(self):
Planification = Pool().get('lims.planification')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
Planification.write([planification], {
2017-10-08 02:23:22 +02:00
'analysis': [('remove', self.start.analysis)],
})
Planification.write([planification], {
2017-10-08 02:23:22 +02:00
'analysis': [('add', self.start.analysis)],
})
return 'end'
class SearchFractionsNext(ModelView):
2017-10-08 02:23:22 +02:00
'Search Fractions'
__name__ = 'lims.planification.search_fractions.next'
details = fields.Many2Many(
'lims.planification.search_fractions.detail',
None, None, 'Fractions to plan', depends=['details_domain'],
domain=[('id', 'in', Eval('details_domain'))], required=True)
details_domain = fields.One2Many(
'lims.planification.search_fractions.detail',
None, 'Fractions domain')
class SearchFractionsDetail(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Fraction to Plan'
__name__ = 'lims.planification.search_fractions.detail'
_table = 'lims_plan_search_fractions_detail'
fraction = fields.Many2One('lims.fraction', 'Fraction', readonly=True)
service_analysis = fields.Many2One('lims.analysis', 'Service',
readonly=True)
type = fields.Function(fields.Many2One('lims.fraction.type',
'Fraction type'), 'get_fraction_field',
searcher='search_fraction_field')
label = fields.Function(fields.Char('Label'), 'get_fraction_field',
searcher='search_fraction_field')
product_type = fields.Function(fields.Many2One('lims.product.type',
'Product type'), 'get_fraction_field',
searcher='search_fraction_field')
matrix = fields.Function(fields.Many2One('lims.matrix', 'Matrix'),
'get_fraction_field', searcher='search_fraction_field')
create_date2 = fields.Function(fields.DateTime('Create Date'),
'get_fraction_field', searcher='search_fraction_field')
2017-10-08 02:23:22 +02:00
urgent = fields.Function(fields.Boolean('Urgent'), 'get_service_field')
priority = fields.Function(fields.Integer('Priority'), 'get_service_field')
repetition = fields.Boolean('Repetition', readonly=True)
2017-10-08 02:23:22 +02:00
report_date = fields.Function(fields.Date('Date agreed for result'),
'get_service_field')
completion_percentage = fields.Function(fields.Float('Complete',
digits=(1, 4)), 'get_completion_percentage')
2017-10-08 02:23:22 +02:00
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(SearchFractionsDetail,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
@classmethod
def __setup__(cls):
super(SearchFractionsDetail, cls).__setup__()
2017-10-08 02:23:22 +02:00
cls._order.insert(0, ('fraction', 'ASC'))
cls._order.insert(1, ('service_analysis', 'ASC'))
@classmethod
def get_fraction_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name in ('label', 'create_date2'):
2017-10-08 02:23:22 +02:00
for d in details:
result[name][d.id] = getattr(d.fraction, name, None)
else:
for d in details:
field = getattr(d.fraction, name, None)
result[name][d.id] = field.id if field else None
return result
@classmethod
def search_fraction_field(cls, name, clause):
return [('fraction.' + name,) + tuple(clause[1:])]
@classmethod
def get_service_field(cls, details, names):
result = {}
for name in names:
result[name] = {}
if name == 'urgent':
for d in details:
result[name][d.id] = False
elif name == 'priority':
for d in details:
result[name][d.id] = 0
else:
for d in details:
result[name][d.id] = None
for d in details:
if d.fraction and d.service_analysis:
for service in d.fraction.services:
if service.analysis == d.service_analysis:
for name in names:
result[name][d.id] = getattr(service, name)
return result
@classmethod
def get_completion_percentage(cls, details, name):
result = {}
for d in details:
result[d.id] = getattr(d.fraction.sample, name, None)
return result
2017-10-08 02:23:22 +02:00
class SearchFractions(Wizard):
2017-10-08 02:23:22 +02:00
'Search Fractions'
__name__ = 'lims.planification.search_fractions'
start_state = 'search'
search = StateTransition()
next = StateView('lims.planification.search_fractions.next',
'lims.lims_search_fractions_next_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def transition_search(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
PlanificationDetail = pool.get('lims.planification.detail')
SearchFractionsDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.search_fractions.detail')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
service_detail = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
('is_control', '=', False),
('is_replanned', '=', False),
])
if service_detail:
PlanificationServiceDetail.delete(service_detail)
2017-10-08 02:23:22 +02:00
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
('details', '=', None),
])
if details:
PlanificationDetail.delete(details)
2017-10-08 02:23:22 +02:00
fractions_added = []
if not planification.analysis:
self.next.details = fractions_added
return 'next'
data = self._get_service_details(planification)
to_create = []
2019-03-04 15:41:58 +01:00
for k, v in data.items():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': k[0],
'service_analysis': k[1],
'repetition': v['repetition'],
2017-10-08 02:23:22 +02:00
})
fractions_added = SearchFractionsDetail.create(to_create)
2017-10-08 02:23:22 +02:00
self.next.details = fractions_added
return 'next'
def default_next(self, fields):
details = [d.id for d in self.next.details]
self.next.details = None
return {
'details': [],
'details_domain': details,
}
def transition_add(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
records_added = ['(%s,%s)' % (d.fraction.id, d.service_analysis.id)
for d in self.next.details]
records_ids_added = ', '.join(str(x)
for x in ['(0,0)'] + records_added)
extra_where = (
'AND (nb.fraction, srv.analysis) IN (' + records_ids_added + ') ')
data = self._get_service_details(planification, extra_where)
to_create = []
2019-03-04 15:41:58 +01:00
for k, v in data.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
to_create.append({
'planification': planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
})
if to_create:
PlanificationDetail.create(to_create)
2017-10-08 02:23:22 +02:00
return 'end'
def _get_service_details(self, planification, extra_where=''):
cursor = Transaction().connection.cursor()
pool = Pool()
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
NotebookLine = pool.get('lims.notebook.line')
Notebook = pool.get('lims.notebook')
Fraction = pool.get('lims.fraction')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
planification_details = PlanificationServiceDetail.search([
('planification.state', '=', 'preplanned'),
])
planned_lines = [pd.notebook_line.id for pd in planification_details
if pd.notebook_line]
planned_lines_ids = ', '.join(str(x) for x in [0] + planned_lines)
result = {}
nlines_added = []
for a in planification.analysis:
analysis_id = a.id
all_included_analysis = [analysis_id]
all_included_analysis.extend(
Analysis.get_included_analysis_analysis(analysis_id))
all_included_analysis_ids = ', '.join(str(x)
for x in all_included_analysis)
service_where = ('AND ad.analysis IN (' +
all_included_analysis_ids + ') ')
2017-10-08 02:23:22 +02:00
sql_select = (
'SELECT nl.id, nb.fraction, srv.analysis, nl.repetition != 0 ')
2017-10-08 02:23:22 +02:00
sql_from = (
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + Analysis._table + '" nla '
'ON nla.id = nl.analysis '
'INNER JOIN "' + Notebook._table + '" nb '
'ON nb.id = nl.notebook '
'INNER JOIN "' + Fraction._table + '" frc '
'ON frc.id = nb.fraction '
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
'ON ad.id = nl.analysis_detail '
'INNER JOIN "' + Service._table + '" srv '
'ON srv.id = nl.service ')
2017-10-08 02:23:22 +02:00
sql_where = (
'WHERE ad.plannable = TRUE '
'AND nl.start_date IS NULL '
'AND nl.annulled = FALSE '
'AND nl.laboratory = %s '
'AND nl.id NOT IN (' + planned_lines_ids + ') '
'AND nla.behavior != \'internal_relation\' '
'AND ad.confirmation_date::date >= %s::date '
'AND ad.confirmation_date::date <= %s::date ' +
service_where + extra_where)
2017-10-08 02:23:22 +02:00
sql_order = (
'ORDER BY nb.fraction ASC, srv.analysis ASC')
with Transaction().set_user(0):
cursor.execute(sql_select + sql_from + sql_where + sql_order,
(planification.laboratory.id, planification.date_from,
planification.date_to,))
notebook_lines = cursor.fetchall()
if not notebook_lines:
continue
if extra_where:
for nl in notebook_lines:
f_ = nl[1]
s_ = nl[2]
if (f_, s_) not in result:
result[(f_, s_)] = []
if nl[0] not in nlines_added:
nlines_added.append(nl[0])
result[(f_, s_)].append({
'notebook_line': nl[0],
'planned_service': a.id,
})
else:
for nl in notebook_lines:
f_ = nl[1]
s_ = nl[2]
result[(f_, s_)] = {
'repetition': nl[3],
}
2017-10-08 02:23:22 +02:00
return result
class SearchPlannedFractionsStart(ModelView):
2017-10-08 02:23:22 +02:00
'Search Planned Fractions'
__name__ = 'lims.planification.search_planned_fractions.start'
laboratory = fields.Many2One('lims.laboratory', 'Laboratory')
date_from = fields.Date('Date from', required=True)
date_to = fields.Date('Date to', required=True)
analysis = fields.Many2Many('lims.planification-analysis',
'planification', 'analysis', 'Analysis/Sets/Groups',
domain=[('id', 'in', Eval('analysis_domain'))],
depends=['analysis_domain'], required=True)
analysis_domain = fields.One2Many('lims.analysis', None,
'Analysis domain')
class SearchPlannedFractionsNext(ModelView):
2017-10-08 02:23:22 +02:00
'Search Planned Fractions'
__name__ = 'lims.planification.search_planned_fractions.next'
details = fields.Many2Many(
'lims.planification.search_fractions.detail',
2017-10-08 02:23:22 +02:00
None, None, 'Fractions to replan', depends=['details_domain'],
domain=[('id', 'in', Eval('details_domain'))], required=True)
details_domain = fields.One2Many(
'lims.planification.search_fractions.detail',
2017-10-08 02:23:22 +02:00
None, 'Fractions domain')
class SearchPlannedFractions(Wizard):
2017-10-08 02:23:22 +02:00
'Search Planned Fractions'
__name__ = 'lims.planification.search_planned_fractions'
start = StateView('lims.planification.search_planned_fractions.start',
'lims.lims_search_planned_fractions_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
2019-01-03 16:43:16 +01:00
Button('Search', 'search', 'tryton-forward', default=True),
2017-10-08 02:23:22 +02:00
])
search = StateTransition()
next = StateView('lims.planification.search_planned_fractions.next',
'lims.lims_search_planned_fractions_next_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Add', 'add', 'tryton-ok', default=True),
])
add = StateTransition()
def default_start(self, fields):
Planification = Pool().get('lims.planification')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
analysis = [a.id for a in planification.analysis]
return {
'laboratory': planification.laboratory.id,
'analysis': analysis,
'analysis_domain': analysis,
'date_from': planification.date_from,
'date_to': planification.date_to,
}
def transition_search(self):
pool = Pool()
SearchFractionsDetail = pool.get(
'lims.planification.search_fractions.detail')
2017-10-08 02:23:22 +02:00
data = self._get_service_details()
to_create = []
2019-03-04 15:41:58 +01:00
for k, v in data.items():
2017-10-08 02:23:22 +02:00
to_create.append({
'session_id': self._session_id,
'fraction': k[0],
'service_analysis': k[1],
'repetition': v['repetition'],
2017-10-08 02:23:22 +02:00
})
fractions_added = SearchFractionsDetail.create(to_create)
2017-10-08 02:23:22 +02:00
self.next.details = fractions_added
return 'next'
def default_next(self, fields):
details = [d.id for d in self.next.details]
self.next.details = None
return {
'details': [],
'details_domain': details,
}
def transition_add(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
records_added = ['(%s,%s)' % (d.fraction.id, d.service_analysis.id)
for d in self.next.details]
records_ids_added = ', '.join(str(x)
for x in ['(0,0)'] + records_added)
extra_where = (
'AND (nb.fraction, srv.analysis) IN (' + records_ids_added + ') ')
data = self._get_service_details(extra_where)
to_create = []
2019-03-04 15:41:58 +01:00
for k, v in data.items():
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
('fraction', '=', k[0]),
('service_analysis', '=', k[1]),
])
if details:
PlanificationDetail.write([details[0]], {
2017-10-08 02:23:22 +02:00
'details': [('create', v)],
})
else:
to_create.append({
'planification': planification.id,
'fraction': k[0],
'service_analysis': k[1],
'details': [('create', v)],
})
if to_create:
PlanificationDetail.create(to_create)
2017-10-08 02:23:22 +02:00
return 'end'
def _get_service_details(self, extra_where=''):
cursor = Transaction().connection.cursor()
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
Notebook = pool.get('lims.notebook')
Fraction = pool.get('lims.fraction')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
result = {}
nlines_added = []
for a in self.start.analysis:
analysis_id = a.id
all_included_analysis = [analysis_id]
all_included_analysis.extend(
Analysis.get_included_analysis_analysis(analysis_id))
all_included_analysis_ids = ', '.join(str(x)
for x in all_included_analysis)
service_where = ('AND ad.analysis IN (' +
all_included_analysis_ids + ') ')
2017-10-08 02:23:22 +02:00
excluded_fractions = self.get_control_fractions_excluded(
self.start.laboratory.id, service_where)
2017-10-08 02:23:22 +02:00
excluded_fractions_ids = ', '.join(str(x)
for x in [0] + excluded_fractions)
sql_select = (
'SELECT nl.id, nb.fraction, srv.analysis, nl.repetition != 0 ')
2017-10-08 02:23:22 +02:00
sql_from = (
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + Analysis._table + '" nla '
'ON nla.id = nl.analysis '
'INNER JOIN "' + Notebook._table + '" nb '
'ON nb.id = nl.notebook '
'INNER JOIN "' + Fraction._table + '" frc '
'ON frc.id = nb.fraction '
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
'ON ad.id = nl.analysis_detail '
'INNER JOIN "' + Service._table + '" srv '
'ON srv.id = nl.service ')
2017-10-08 02:23:22 +02:00
sql_where = (
'WHERE ad.plannable = TRUE '
'AND nl.start_date IS NOT NULL '
'AND nl.end_date IS NULL '
'AND nl.laboratory = %s '
'AND nla.behavior != \'internal_relation\' '
'AND nb.fraction NOT IN (' + excluded_fractions_ids + ') '
'AND ad.confirmation_date::date >= %s::date '
'AND ad.confirmation_date::date <= %s::date ' +
service_where + extra_where)
2017-10-08 02:23:22 +02:00
sql_order = (
'ORDER BY nb.fraction ASC, srv.analysis ASC')
with Transaction().set_user(0):
cursor.execute(sql_select + sql_from + sql_where + sql_order,
(self.start.laboratory.id, self.start.date_from,
self.start.date_to,))
notebook_lines = cursor.fetchall()
if notebook_lines:
if extra_where:
for nl in notebook_lines:
f_ = nl[1]
s_ = nl[2]
if (f_, s_) not in result:
result[(f_, s_)] = []
if nl[0] not in nlines_added:
nlines_added.append(nl[0])
result[(f_, s_)].append({
'notebook_line': nl[0],
'planned_service': a.id,
'is_replanned': True,
})
else:
for nl in notebook_lines:
f_ = nl[1]
s_ = nl[2]
result[(f_, s_)] = {
'repetition': nl[3],
}
2017-10-08 02:23:22 +02:00
return result
def get_control_fractions_excluded(self, laboratory, search_clause):
cursor = Transaction().connection.cursor()
2017-10-08 02:23:22 +02:00
pool = Pool()
NotebookLine = pool.get('lims.notebook.line')
Notebook = pool.get('lims.notebook')
Fraction = pool.get('lims.fraction')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
Config = pool.get('lims.configuration')
2017-10-08 02:23:22 +02:00
config = Config(1)
special_types = []
if config.rm_fraction_type:
special_types.append(config.rm_fraction_type.id)
if config.bmz_fraction_type:
special_types.append(config.bmz_fraction_type.id)
if config.bre_fraction_type:
special_types.append(config.bre_fraction_type.id)
if config.mrt_fraction_type:
special_types.append(config.mrt_fraction_type.id)
special_types_ids = ', '.join(str(x) for x in [0] + special_types)
cursor.execute('SELECT nl.analysis, nl.notebook '
'FROM "' + NotebookLine._table + '" nl '
'INNER JOIN "' + Notebook._table + '" nb '
'ON nb.id = nl.notebook '
'INNER JOIN "' + Fraction._table + '" frc '
'ON frc.id = nb.fraction '
'INNER JOIN "' + EntryDetailAnalysis._table + '" ad '
'ON ad.id = nl.analysis_detail '
'WHERE frc.type IN (' + special_types_ids + ') '
'AND nl.start_date IS NOT NULL '
'AND nl.end_date IS NULL '
'AND nl.laboratory = %s ' +
search_clause, (laboratory,))
notebook_lines = cursor.fetchall()
if not notebook_lines:
return []
2017-10-08 02:23:22 +02:00
excluded_fractions = []
analysis = []
notebooks_id = []
for nbl in notebook_lines:
analysis.append(nbl[0])
notebooks_id.append(nbl[1])
analysis = set(analysis)
notebooks = Notebook.search([
('id', 'in', list(set(notebooks_id))),
])
for nb in notebooks:
cursor.execute('SELECT analysis '
'FROM "' + NotebookLine._table + '" '
'WHERE notebook = %s', (nb.id,))
nbl_analysis_ids = set(cursor.fetchall())
if not analysis.issubset(nbl_analysis_ids):
excluded_fractions.append(nb.fraction.id)
2017-10-08 02:23:22 +02:00
return list(set(excluded_fractions))
class CreateFractionControlStart(ModelView):
2017-10-08 02:23:22 +02:00
'Create Fraction Control'
__name__ = 'lims.planification.create_fraction_con.start'
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
required=True)
type = fields.Selection([
('coi', 'COI'),
('mrc', 'MRC'),
('sla', 'SLA'),
], 'Control type', sort=False, required=True)
product_type = fields.Many2One('lims.product.type', 'Product type',
required=True,
domain=[('id', 'in', Eval('product_type_domain'))],
depends=['product_type_domain'])
product_type_domain = fields.Function(fields.Many2Many(
'lims.product.type', None, None, 'Product type domain'),
'on_change_with_product_type_domain')
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')
analysis = fields.Many2Many('lims.planification-analysis',
'planification', 'analysis', 'Analysis/Sets/Groups',
domain=[('id', 'in', Eval('analysis_domain'))],
depends=['analysis_domain'])
analysis_domain = fields.Function(fields.One2Many('lims.analysis',
None, 'Analysis domain'), 'on_change_with_analysis_domain')
label = fields.Char('Label')
concentration_level = fields.Many2One('lims.concentration.level',
'Concentration level', states={
'invisible': Bool(Eval('concentration_level_invisible')),
}, depends=['concentration_level_invisible'])
concentration_level_invisible = fields.Boolean(
'Concentration level invisible')
sample_created = fields.Many2One('lims.sample', 'Sample created')
@fields.depends('type')
def on_change_with_concentration_level_invisible(self, name=None):
Config = Pool().get('lims.configuration')
config = Config(1)
if self.type == 'coi':
if (config.coi_fraction_type and
config.coi_fraction_type.control_charts):
return False
elif self.type == 'mrc':
if (config.mrc_fraction_type and
config.mrc_fraction_type.control_charts):
return False
elif self.type == 'sla':
if (config.sla_fraction_type and
config.sla_fraction_type.control_charts):
return False
return True
@fields.depends('laboratory', 'product_type', 'matrix')
def on_change_with_analysis_domain(self, name=None):
cursor = Transaction().connection.cursor()
pool = Pool()
AnalysisLaboratory = pool.get('lims.analysis-laboratory')
Analysis = pool.get('lims.analysis')
Typification = pool.get('lims.typification')
CalculatedTypification = pool.get('lims.typification.calculated')
2017-10-08 02:23:22 +02:00
if not self.laboratory or not self.product_type or not self.matrix:
return []
cursor.execute('SELECT DISTINCT(analysis) '
'FROM "' + AnalysisLaboratory._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE laboratory = %s', (self.laboratory.id,))
analysis_sets_list = [a[0] for a in cursor.fetchall()]
if not analysis_sets_list:
return []
lab_analysis_ids = ', '.join(str(a) for a in
analysis_sets_list)
groups_list = []
groups = Analysis.search([
2017-10-08 02:23:22 +02:00
('type', '=', 'group'),
])
if groups:
for group in groups:
available = True
ia = Analysis.get_included_analysis_analysis(
2017-10-08 02:23:22 +02:00
group.id)
if not ia:
continue
included_ids = ', '.join(str(a) for a in ia)
cursor.execute('SELECT id '
'FROM "' + Analysis._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE id IN (' + included_ids + ') '
'AND id NOT IN (' + lab_analysis_ids +
')')
2017-10-08 02:23:22 +02:00
if cursor.fetchone():
available = False
if available:
groups_list.append(group.id)
analysis_domain = analysis_sets_list + groups_list
analysis_domain_ids = ', '.join(str(a) for a in analysis_domain)
cursor.execute('SELECT DISTINCT(typ.analysis) '
'FROM ('
'SELECT t.analysis FROM "' + Typification._table + '" t '
2017-10-08 02:23:22 +02:00
'WHERE t.product_type = %s AND t.matrix = %s AND t.valid '
'UNION '
'SELECT ct.analysis FROM "' + CalculatedTypification._table +
'" ct '
2017-10-08 02:23:22 +02:00
'WHERE ct.product_type = %s AND ct.matrix = %s'
') AS typ '
'WHERE typ.analysis IN (' + analysis_domain_ids + ')',
(self.product_type.id, self.matrix.id,
self.product_type.id, self.matrix.id))
typified_analysis = [a[0] for a in cursor.fetchall()]
return typified_analysis
@staticmethod
def default_product_type_domain():
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
cursor.execute('SELECT DISTINCT(product_type) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE valid')
return [x[0] for x in cursor.fetchall()]
def on_change_with_product_type_domain(self, name=None):
return self.default_product_type_domain()
@fields.depends('product_type')
def on_change_product_type(self):
matrix = None
if self.product_type:
matrixs = self.on_change_with_matrix_domain()
if len(matrixs) == 1:
matrix = matrixs[0]
self.matrix = matrix
@fields.depends('product_type')
def on_change_with_matrix_domain(self, name=None):
cursor = Transaction().connection.cursor()
Typification = Pool().get('lims.typification')
2017-10-08 02:23:22 +02:00
if not self.product_type:
return []
cursor.execute('SELECT DISTINCT(matrix) '
'FROM "' + Typification._table + '" '
2017-10-08 02:23:22 +02:00
'WHERE product_type = %s '
'AND valid',
(self.product_type.id,))
return [x[0] for x in cursor.fetchall()]
2020-03-05 05:24:51 +01:00
@fields.depends('type', 'product_type', 'matrix',
'_parent_product_type.code', '_parent_matrix.code')
2017-10-08 02:23:22 +02:00
def on_change_with_label(self, name=None):
label = ''
if self.type == 'coi':
label += 'COI'
elif self.type == 'mrc':
label += 'MRC'
elif self.type == 'sla':
label += 'SLA'
if self.product_type:
label += ' ' + self.product_type.code
if self.matrix:
label += ' ' + self.matrix.code
return label
class CreateFractionControl(Wizard):
2017-10-08 02:23:22 +02:00
'Create Fraction Control'
__name__ = 'lims.planification.create_fraction_con'
start = StateView('lims.planification.create_fraction_con.start',
'lims.lims_create_fraction_con_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Create', 'create_', 'tryton-ok', default=True),
])
create_ = StateTransition()
open_ = StateAction('lims.act_lims_sample_list')
def default_start(self, fields):
defaults = {
'laboratory': Transaction().context.get('laboratory', None),
'concentration_level_invisible': True,
}
return defaults
def transition_create_(self):
control = self.create_control()
self.start.sample_created = control.sample.id
return 'open_'
def create_control(self):
pool = Pool()
Config = pool.get('lims.configuration')
LabWorkYear = pool.get('lims.lab.workyear')
Entry = pool.get('lims.entry')
Sample = pool.get('lims.sample')
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
NotebookLine = pool.get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
config = Config(1)
if self.start.type == 'coi':
if not config.coi_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_coi_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.coi_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_no_coi_default_configuration'))
2017-10-08 02:23:22 +02:00
elif self.start.type == 'mrc':
if not config.mrc_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_mrc_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.mrc_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_no_mrc_default_configuration'))
2017-10-08 02:23:22 +02:00
elif self.start.type == 'sla':
if not config.sla_fraction_type:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_sla_fraction_type'))
2017-10-08 02:23:22 +02:00
fraction_type = config.sla_fraction_type
if (not fraction_type.default_package_type or
not fraction_type.default_fraction_state):
2019-07-23 23:27:33 +02:00
raise UserError(gettext(
'lims.msg_no_sla_default_configuration'))
2017-10-08 02:23:22 +02:00
if (fraction_type.control_charts and not
self.start.concentration_level):
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_concentration_level'))
2017-10-08 02:23:22 +02:00
workyear_id = LabWorkYear.find()
workyear = LabWorkYear(workyear_id)
2017-10-08 02:23:22 +02:00
if not workyear.default_entry_control:
2019-07-23 23:27:33 +02:00
raise UserError(gettext('lims.msg_no_entry_control'))
2017-10-08 02:23:22 +02:00
laboratory = self.start.laboratory
entry = Entry(workyear.default_entry_control.id)
2019-06-14 00:38:02 +02:00
obj_description = self._get_obj_description(self.start)
2017-10-08 02:23:22 +02:00
# new sample
new_sample, = Sample.create([{
2017-10-08 02:23:22 +02:00
'entry': entry.id,
'date': datetime.now(),
'product_type': self.start.product_type.id,
'matrix': self.start.matrix.id,
'zone': entry.party.entry_zone.id,
'label': self.start.label,
2019-06-14 00:38:02 +02:00
'obj_description': obj_description,
2017-10-08 02:23:22 +02:00
'packages_quantity': 1,
'fractions': [],
}])
# new fraction
fraction_default = {
'sample': new_sample.id,
'type': fraction_type.id,
'storage_location': laboratory.related_location.id,
'packages_quantity': 1,
'package_type': fraction_type.default_package_type.id,
'fraction_state': fraction_type.default_fraction_state.id,
'services': [],
'con_type': self.start.type,
}
if fraction_type.max_storage_time:
fraction_default['storage_time'] = fraction_type.max_storage_time
elif laboratory.related_location.storage_time:
fraction_default['storage_time'] = (
laboratory.related_location.storage_time)
else:
fraction_default['storage_time'] = 3
new_fraction, = Fraction.create([fraction_default])
2017-10-08 02:23:22 +02:00
# new services
services_default = []
for p_analysis in self.start.analysis:
laboratory_id = (laboratory.id if p_analysis.type != 'group'
else None)
method_id = None
if new_sample.typification_domain:
for t in new_sample.typification_domain:
if (t.analysis.id == p_analysis.id and
t.by_default is True):
2017-10-08 02:23:22 +02:00
method_id = t.method.id
device_id = None
if p_analysis.devices:
for d in p_analysis.devices:
if (d.laboratory.id == laboratory.id and
d.by_default is True):
2017-10-08 02:23:22 +02:00
device_id = d.device.id
services_default.append({
'fraction': new_fraction.id,
'analysis': p_analysis.id,
'laboratory': laboratory_id,
'method': method_id,
'device': device_id,
})
for service in services_default:
new_service, = Service.create([service])
2017-10-08 02:23:22 +02:00
# new analysis details (on service create)
# confirm fraction: new notebook and stock move
Fraction.confirm([new_fraction])
2017-10-08 02:23:22 +02:00
# Edit notebook lines
if fraction_type.control_charts:
notebook_lines = NotebookLine.search([
2017-10-08 02:23:22 +02:00
('notebook.fraction', '=', new_fraction.id),
])
if notebook_lines:
defaults = {
'concentration_level': self.start.concentration_level.id,
}
NotebookLine.write(notebook_lines, defaults)
2017-10-08 02:23:22 +02:00
return new_fraction
2019-06-14 00:38:02 +02:00
def _get_obj_description(self, sample):
cursor = Transaction().connection.cursor()
ObjectiveDescription = Pool().get('lims.objective_description')
if not sample.product_type or not sample.matrix:
return None
cursor.execute('SELECT id '
'FROM "' + ObjectiveDescription._table + '" '
'WHERE product_type = %s '
'AND matrix = %s',
(sample.product_type.id, sample.matrix.id))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
def do_open_(self, action):
action['pyson_domain'] = PYSONEncoder().encode([
('id', '=', self.start.sample_created.id),
])
return action, {}
class ReleaseFractionStart(ModelView):
2017-10-08 02:23:22 +02:00
'Release Fraction'
__name__ = 'lims.planification.release_fraction.start'
laboratory = fields.Many2One('lims.laboratory', 'Laboratory',
required=True)
date_from = fields.Date('Date from', required=True)
date_to = fields.Date('Date to', required=True)
class ReleaseFractionEmpty(ModelView):
2017-10-08 02:23:22 +02:00
'Release Fraction'
__name__ = 'lims.planification.release_fraction.empty'
class ReleaseFractionResult(ModelView):
2017-10-08 02:23:22 +02:00
'Release Fraction'
__name__ = 'lims.planification.release_fraction.result'
fractions = fields.Many2Many('lims.planification.detail', None, None,
'Fractions', required=True,
domain=[('id', 'in', Eval('fractions_domain'))],
depends=['fractions_domain'])
fractions_domain = fields.One2Many('lims.planification.detail', None,
'Fractions domain')
class ReleaseFraction(Wizard):
2017-10-08 02:23:22 +02:00
'Release Fraction'
__name__ = 'lims.planification.release_fraction'
start = StateView('lims.planification.release_fraction.start',
'lims.lims_planification_release_fraction_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
2019-01-03 16:43:16 +01:00
Button('Search', 'search', 'tryton-forward', default=True),
2017-10-08 02:23:22 +02:00
])
search = StateTransition()
empty = StateView('lims.planification.release_fraction.empty',
'lims.lims_planification_release_fraction_empty_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
2019-01-03 16:43:16 +01:00
Button('Search again', 'start', 'tryton-forward', default=True),
2017-10-08 02:23:22 +02:00
])
result = StateView('lims.planification.release_fraction.result',
'lims.lims_planification_release_fraction_result_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Release', 'release', 'tryton-ok', default=True),
])
release = StateTransition()
def default_start(self, fields):
res = {}
for field in ('date_from', 'date_to'):
if (hasattr(self.start, field) and getattr(self.start, field)):
res[field] = getattr(self.start, field)
if (hasattr(self.start, 'laboratory') and
getattr(self.start, 'laboratory')):
res['laboratory'] = getattr(self.start, 'laboratory').id
else:
res['laboratory'] = Transaction().context.get('laboratory', None)
return res
def transition_search(self):
PlanificationDetail = Pool().get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('planification.laboratory', '=', self.start.laboratory.id),
('planification.start_date', '>=', self.start.date_from),
('planification.start_date', '<=', self.start.date_to),
('planification.state', '!=', 'draft'),
])
if details:
fractions = []
for detail in details:
available = True
for service_detail in detail.details:
if service_detail.is_control:
available = False
break
nline = service_detail.notebook_line
if not nline:
available = False
break
if (nline.result or nline.converted_result or
nline.literal_result or nline.end_date or
nline.annulment_date):
available = False
break
if available:
fractions.append(detail)
if fractions:
self.result.fractions = fractions
return 'result'
return 'empty'
def default_result(self, fields):
fractions = [f.id for f in self.result.fractions]
self.result.fractions = None
return {
'fractions_domain': fractions,
}
def transition_release(self):
self._re_update_laboratory_notebook()
self._re_update_analysis_detail()
self._unlink_fractions()
return 'end'
def _re_update_laboratory_notebook(self):
NotebookLine = Pool().get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
for detail in self.result.fractions:
for service_detail in detail.details:
if (not service_detail.is_control and
service_detail.notebook_line):
notebook_line = NotebookLine(
2017-10-08 02:23:22 +02:00
service_detail.notebook_line.id)
notebook_line.start_date = None
notebook_line.laboratory_professionals = []
notebook_line.planification = None
notebook_line.controls = []
notebook_line.save()
def _re_update_analysis_detail(self):
EntryDetailAnalysis = Pool().get('lims.entry.detail.analysis')
2017-10-08 02:23:22 +02:00
analysis_detail_ids = []
for detail in self.result.fractions:
for service_detail in detail.details:
if (not service_detail.is_control and
service_detail.notebook_line and
service_detail.notebook_line.analysis_detail):
2017-10-08 02:23:22 +02:00
analysis_detail_ids.append(
service_detail.notebook_line.analysis_detail.id)
analysis_details = EntryDetailAnalysis.search([
2017-10-08 02:23:22 +02:00
('id', 'in', analysis_detail_ids),
])
if analysis_details:
EntryDetailAnalysis.write(analysis_details, {
2017-10-08 02:23:22 +02:00
'state': 'unplanned',
})
def _unlink_fractions(self):
pool = Pool()
PlanificationServiceDetail = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
PlanificationDetail = pool.get('lims.planification.detail')
2017-10-08 02:23:22 +02:00
details_ids = []
service_details_ids = []
for detail in self.result.fractions:
for service_detail in detail.details:
if not service_detail.is_control:
service_details_ids.append(service_detail.id)
details_ids.append(service_detail.detail.id)
service_detail = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('id', 'in', service_details_ids),
])
if service_detail:
PlanificationServiceDetail.delete(service_detail)
2017-10-08 02:23:22 +02:00
details = PlanificationDetail.search([
2017-10-08 02:23:22 +02:00
('id', 'in', details_ids),
('details', '=', None),
])
if details:
PlanificationDetail.delete(details)
2017-10-08 02:23:22 +02:00
class QualificationSituations(ModelView):
2017-10-08 02:23:22 +02:00
'Technicians Qualification'
__name__ = 'lims.planification.qualification.situations'
situations = fields.Many2Many('lims.planification.qualification.situation',
None, None, 'Situations')
total = fields.Integer('Total')
index = fields.Integer('Index')
class QualificationSituation(ModelSQL, ModelView):
2017-10-08 02:23:22 +02:00
'Qualification Situation'
__name__ = 'lims.planification.qualification.situation'
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
professional = fields.Many2One('lims.laboratory.professional',
'Professional', readonly=True)
situation = fields.Integer('Situation', readonly=True)
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(QualificationSituation,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
class QualificationAction(ModelSQL):
2017-10-08 02:23:22 +02:00
'Qualification Action'
__name__ = 'lims.planification.qualification.action'
method = fields.Many2One('lims.lab.method', 'Method')
professional = fields.Many2One('lims.laboratory.professional',
'Professional')
action = fields.Integer('Action')
session_id = fields.Integer('Session ID')
@classmethod
def __register__(cls, module_name):
super(QualificationAction,
2017-10-08 02:23:22 +02:00
cls).__register__(module_name)
cursor = Transaction().connection.cursor()
cursor.execute('DELETE FROM "' + cls._table + '"')
class QualificationSituation2(ModelView):
2017-10-08 02:23:22 +02:00
'Technicians Qualification'
__name__ = 'lims.planification.qualification.situation.2'
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
professional = fields.Many2One('lims.laboratory.professional',
'Professional', readonly=True)
class QualificationSituation3(ModelView):
2017-10-08 02:23:22 +02:00
'Technicians Qualification'
__name__ = 'lims.planification.qualification.situation.3'
method = fields.Many2One('lims.lab.method', 'Method', readonly=True)
professional = fields.Many2One('lims.laboratory.professional',
'Professional', readonly=True)
class QualificationSituation4(ModelView):
2017-10-08 02:23:22 +02:00
'Technicians Qualification'
__name__ = 'lims.planification.qualification.situation.4'
methods = fields.Text('Methods', readonly=True)
class TechniciansQualification(Wizard):
2017-10-08 02:23:22 +02:00
'Technicians Qualification'
__name__ = 'lims.planification.technicians_qualification'
situations = StateView('lims.planification.qualification.situations',
'lims.lims_qualification_situations_view_form', [])
2017-10-08 02:23:22 +02:00
start = StateTransition()
next_ = StateTransition()
confirm = StateTransition()
sit2 = StateView('lims.planification.qualification.situation.2',
'lims.lims_qualification_situation_2_view_form', [
2017-10-08 02:23:22 +02:00
Button('Qualify', 'sit2_op1', 'tryton-ok', default=True),
Button('New Training', 'sit2_op2', 'tryton-ok'),
Button('Cancel', 'end', 'tryton-cancel'),
])
sit2_op1 = StateTransition()
sit2_op2 = StateTransition()
sit3 = StateView('lims.planification.qualification.situation.3',
'lims.lims_qualification_situation_3_view_form', [
2017-10-08 02:23:22 +02:00
Button('Requalify', 'sit3_op1', 'tryton-ok', default=True),
Button('Cancel', 'end', 'tryton-cancel'),
])
sit3_op1 = StateTransition()
sit4 = StateView('lims.planification.qualification.situation.4',
'lims.lims_qualification_situation_4_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
])
def transition_start(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
LabProfessionalMethod = pool.get('lims.lab.professional.method')
QualificationSituation = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.situation')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
planification_details = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
])
situations = {}
for detail in planification_details:
method = (detail.notebook_line.method if detail.notebook_line
else None)
if method:
for technician in detail.staff_responsible:
key = (technician.id, method.id)
if key in situations:
continue
situations[key] = 0
qualifications = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', technician.id),
('method', '=', method.id),
('type', '=', 'preparation'),
])
if not qualifications:
situations[key] = 1
else:
if qualifications[0].state == 'training':
situations[key] = 2
elif (qualifications[0].state in
('qualified', 'requalified')):
situations[key] = 3
if situations:
self.situations.situations = QualificationSituation.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': k[0],
'method': k[1],
'situation': v,
2019-03-04 15:41:58 +01:00
} for k, v in situations.items()])
2017-10-08 02:23:22 +02:00
self.situations.total = len(self.situations.situations)
self.situations.index = 0
return 'next_'
return self._confirm()
def transition_next_(self):
data = self.situations.situations[self.situations.index]
if data.situation == 1:
return self.situation_1(data)
elif data.situation == 2:
return self.situation_2(data)
elif data.situation == 3:
return self.situation_3(data)
return self._continue()
def situation_1(self, data):
# The technician has not the method. Write a new training directly.
QualificationAction = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
QualificationAction.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': data.professional.id,
'method': data.method.id,
'action': 1,
}])
return self._continue()
def situation_2(self, data):
# The technician has received at least one training. Ask if qualify
# or write a new training.
self.sit2.professional = data.professional.id
self.sit2.method = data.method.id
return 'sit2'
def default_sit2(self, fields):
professional = self.sit2.professional.id
method = self.sit2.method.id
self.sit2.professional = None
self.sit2.method = None
return {
'professional': professional,
'method': method,
}
def transition_sit2_op1(self):
# Qualify the technician
QualificationAction = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
QualificationAction.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': self.sit2.professional.id,
'method': self.sit2.method.id,
'action': 2,
}])
return self._continue()
def transition_sit2_op2(self):
# Write a new training.
QualificationAction = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
QualificationAction.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': self.sit2.professional.id,
'method': self.sit2.method.id,
'action': 3,
}])
return self._continue()
def situation_3(self, data):
# The technician is qualified. Check the last execution date of the
# method; if it's expired, ask if requalify or cancel the process.
# If it is valid, write a new execution
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
QualificationAction = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
deadline = Date.today() - relativedelta(
months=data.method.requalification_months)
professional_method, = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', data.professional),
('method', '=', data.method),
('type', '=', 'preparation'),
])
last_execution = date.min
for requalification in professional_method.requalification_history:
if (requalification.last_execution_date and
last_execution < requalification.last_execution_date):
last_execution = requalification.last_execution_date
if last_execution < deadline:
self.sit3.professional = data.professional.id
self.sit3.method = data.method.id
return 'sit3'
else:
QualificationAction.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': data.professional.id,
'method': data.method.id,
'action': 5,
}])
return self._continue()
def default_sit3(self, fields):
professional = self.sit3.professional.id
method = self.sit3.method.id
self.sit3.professional = None
self.sit3.method = None
return {
'professional': professional,
'method': method,
}
def transition_sit3_op1(self):
# Requalify the technician
QualificationAction = Pool().get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
QualificationAction.create([{
2017-10-08 02:23:22 +02:00
'session_id': self._session_id,
'professional': self.sit3.professional.id,
'method': self.sit3.method.id,
'action': 4,
}])
return self._continue()
def situation_4(self, data):
# The method has no qualified technician
self.sit4.methods = data['methods']
return 'sit4'
def default_sit4(self, fields):
methods = self.sit4.methods
self.sit4.methods = None
return {
'methods': methods,
}
def _continue(self):
self.situations.index += 1
if self.situations.index < self.situations.total:
return 'next_'
return self._confirm()
def _confirm(self):
pool = Pool()
Planification = pool.get('lims.planification')
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
LabProfessionalMethod = pool.get('lims.lab.professional.method')
QualificationAction = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
planification_details = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification.id),
])
methods = []
for detail in planification_details:
method = (detail.notebook_line.method if detail.notebook_line
else None)
if method:
qualified = False
for technician in detail.staff_responsible:
qualifications = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', technician.id),
('method', '=', method.id),
('type', '=', 'preparation'),
])
if (qualifications and qualifications[0].state in
('qualified', 'requalified')):
if not method.supervised_requalification:
qualified = True
break
actions = QualificationAction.search([
2017-10-08 02:23:22 +02:00
('session_id', '=', self._session_id),
('professional', '=', technician.id),
('method', '=', method.id),
('action', '=', 4),
])
if not actions:
qualified = True
break
if not qualified:
methods.append('[%s] %s' % (method.code, method.name))
if methods:
return self.situation_4({'methods': '\n'.join(list(set(methods)))})
actions = QualificationAction.search([
2017-10-08 02:23:22 +02:00
('session_id', '=', self._session_id),
])
if actions:
controls = self._get_controls()
start_date = planification.start_date
for data in actions:
if data.action == 1:
self.action_1(data, controls, start_date)
elif data.action == 2:
self.action_2(data, controls, start_date)
elif data.action == 3:
self.action_3(data, controls, start_date)
elif data.action == 4:
self.action_4(data, controls, start_date)
elif data.action == 5:
self.action_5(data, controls, start_date)
return 'confirm'
def action_1(self, data, controls, start_date):
# The technician has not the method. Write a new training directly.
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
supervisors = self._get_supervisors(data)
requalification_history = [{
'type': 'training',
'date': Date.today(),
'last_execution_date': start_date,
'supervisors': [('create', supervisors)],
'controls': [('create', controls)],
}]
professional_method, = LabProfessionalMethod.create([{
2017-10-08 02:23:22 +02:00
'professional': data.professional.id,
'method': data.method.id,
'state': 'training',
'type': 'preparation',
'requalification_history': [('create', requalification_history)],
}])
def action_2(self, data, controls, start_date):
# Qualify the technician
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
professional_method, = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', data.professional),
('method', '=', data.method),
('type', '=', 'preparation'),
])
supervisors = self._get_supervisors(data)
requalification_history = [{
'type': 'qualification',
'date': Date.today(),
'last_execution_date': start_date,
'supervisors': [('create', supervisors)],
'controls': [('create', controls)],
}]
LabProfessionalMethod.write([professional_method], {
2017-10-08 02:23:22 +02:00
'state': 'qualified',
'requalification_history': [('create', requalification_history)],
})
def action_3(self, data, controls, start_date):
# Write a new training
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
professional_method, = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', data.professional),
('method', '=', data.method),
('type', '=', 'preparation'),
])
supervisors = self._get_supervisors(data)
requalification_history = [{
'type': 'training',
'date': Date.today(),
'last_execution_date': start_date,
'supervisors': [('create', supervisors)],
'controls': [('create', controls)],
}]
LabProfessionalMethod.write([professional_method], {
2017-10-08 02:23:22 +02:00
'requalification_history': [('create', requalification_history)],
})
def action_4(self, data, controls, start_date):
# Requalify the technician
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
professional_method, = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', data.professional),
('method', '=', data.method),
('type', '=', 'preparation'),
])
supervisors = self._get_supervisors(data)
requalification_history = [{
'type': 'requalification',
'date': Date.today(),
'last_execution_date': start_date,
'supervisors': [('create', supervisors)],
'controls': [('create', controls)],
}]
LabProfessionalMethod.write([professional_method], {
2017-10-08 02:23:22 +02:00
'state': 'requalified',
'requalification_history': [('create', requalification_history)],
})
def action_5(self, data, controls, start_date):
# Write a new execution
pool = Pool()
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
Date = pool.get('ir.date')
professional_method, = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', data.professional),
('method', '=', data.method),
('type', '=', 'preparation'),
])
supervisors = self._get_supervisors(data)
requalification_history = [{
'type': ('qualification'
if professional_method.state == 'qualified'
else 'requalification'),
'date': Date.today(),
'last_execution_date': start_date,
'supervisors': [('create', supervisors)],
'controls': [('create', controls)],
}]
LabProfessionalMethod.write([professional_method], {
2017-10-08 02:23:22 +02:00
'requalification_history': [('create', requalification_history)],
})
def transition_confirm(self):
pool = Pool()
Planification = pool.get('lims.planification')
2017-10-08 02:23:22 +02:00
Config = pool.get('lims.configuration')
process_background = Config(1).planification_process_background
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
planification.state = 'confirmed'
if process_background:
planification.waiting_process = True
planification.save()
if not process_background:
Planification.do_confirm([planification])
2017-10-08 02:23:22 +02:00
return 'end'
def _get_supervisors(self, data):
pool = Pool()
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
LabProfessionalMethod = pool.get('lims.lab.professional.method')
QualificationAction = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.qualification.action')
planification_id = Transaction().context['active_id']
supervisors = []
planification_details = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification_id),
('notebook_line.method', '=', data.method.id),
])
for detail in planification_details:
for technician in detail.staff_responsible:
if technician.id == data.professional.id:
continue
qualifications = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', technician.id),
('method', '=', data.method.id),
('type', '=', 'preparation'),
])
if (qualifications and qualifications[0].state in
('qualified', 'requalified')):
if not data.method.supervised_requalification:
supervisors.append(technician.id)
else:
actions = QualificationAction.search([
2017-10-08 02:23:22 +02:00
('session_id', '=', self._session_id),
('professional', '=', technician.id),
('method', '=', data.method.id),
('action', '=', 4),
])
if not actions:
supervisors.append(technician.id)
return [{'supervisor': t_id} for t_id in list(set(supervisors))]
def _get_controls(self):
PlanificationFraction = Pool().get('lims.planification-fraction')
2017-10-08 02:23:22 +02:00
planification_id = Transaction().context['active_id']
controls = []
planification_controls = PlanificationFraction.search([
2017-10-08 02:23:22 +02:00
('planification', '=', planification_id),
('fraction.type.requalify', '=', True),
])
for p_control in planification_controls:
controls.append(p_control.fraction.id)
return [{'control': f_id} for f_id in list(set(controls))]
class ReplaceTechnicianStart(ModelView):
2017-10-08 02:23:22 +02:00
'Replace Technician'
__name__ = 'lims.planification.replace_technician.start'
planification = fields.Many2One('lims.planification', 'Planification')
technician_replaced = fields.Many2One('lims.laboratory.professional',
'Technician replaced', required=True,
domain=[('id', 'in', Eval('replaced_domain'))],
depends=['replaced_domain'])
replaced_domain = fields.One2Many('lims.laboratory.professional',
None, 'Replaced domain')
technician_substitute = fields.Many2One('lims.laboratory.professional',
'Technician substitute', required=True,
domain=[('id', 'in', Eval('substitute_domain'))],
depends=['substitute_domain'])
substitute_domain = fields.Function(fields.One2Many(
'lims.laboratory.professional', None, 'Substitute domain'),
'on_change_with_substitute_domain')
2020-03-05 05:24:51 +01:00
@fields.depends('technician_replaced', 'planification',
'_parent_planification.laboratory')
2017-10-08 02:23:22 +02:00
def on_change_with_substitute_domain(self, name=None):
pool = Pool()
UserLaboratory = pool.get('lims.user-laboratory')
LaboratoryProfessional = pool.get('lims.laboratory.professional')
PlanificationServiceDetail = pool.get(
2017-10-08 02:23:22 +02:00
'lims.planification.service_detail')
LabProfessionalMethod = pool.get('lims.lab.professional.method')
2017-10-08 02:23:22 +02:00
if not self.technician_replaced:
return []
substitute_domain = []
users = UserLaboratory.search([
2017-10-08 02:23:22 +02:00
('laboratory', '=', self.planification.laboratory.id),
])
if users:
professionals = LaboratoryProfessional.search([
2017-10-08 02:23:22 +02:00
('party.lims_user', 'in', [u.user.id for u in users]),
])
if professionals:
substitute_domain = [p.id for p in professionals]
service_details = PlanificationServiceDetail.search([
2017-10-08 02:23:22 +02:00
('planification', '=', self.planification.id),
('notebook_line', '!=', None),
('staff_responsible', '=', self.technician_replaced.id),
])
methods_ids = []
for service_detail in service_details:
method = service_detail.notebook_line.method
if method:
methods_ids.append(method.id)
methods_ids = list(set(methods_ids))
for technician_id in substitute_domain:
for method_id in methods_ids:
qualifications = LabProfessionalMethod.search([
2017-10-08 02:23:22 +02:00
('professional', '=', technician_id),
('method', '=', method_id),
('type', '=', 'preparation'),
])
if not (qualifications and qualifications[0].state in
('qualified', 'requalified')):
substitute_domain.remove(technician_id)
break
return substitute_domain
class ReplaceTechnician(Wizard):
2017-10-08 02:23:22 +02:00
'Replace Technician'
__name__ = 'lims.planification.replace_technician'
start_state = 'check'
check = StateTransition()
start = StateView('lims.planification.replace_technician.start',
'lims.lims_replace_technician_start_view_form', [
2017-10-08 02:23:22 +02:00
Button('Cancel', 'end', 'tryton-cancel'),
Button('Replace', 'replace', 'tryton-ok', default=True),
])
replace = StateTransition()
def transition_check(self):
pool = Pool()
Planification = pool.get('lims.planification')
2017-10-08 02:23:22 +02:00
NotebookLine = pool.get('lims.notebook.line')
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
if planification.state != 'confirmed':
return 'end'
notebook_lines = NotebookLine.search([
('planification', '=', planification.id),
('end_date', '!=', None),
])
if notebook_lines:
return 'end'
return 'start'
def default_start(self, fields):
Planification = Pool().get('lims.planification')
2017-10-08 02:23:22 +02:00
planification = Planification(Transaction().context['active_id'])
2017-10-08 02:23:22 +02:00
return {
'planification': planification.id,
'replaced_domain': [t.laboratory_professional.id
for t in planification.technicians],
'substitute_domain': [],
}
def transition_replace(self):
planification_id = Transaction().context['active_id']
technician_replaced_id = self.start.technician_replaced.id
technician_substitute_id = self.start.technician_substitute.id
self.update_planification_detail(planification_id,
technician_replaced_id, technician_substitute_id)
self.update_planification_technician(planification_id,
technician_replaced_id, technician_substitute_id)
self.update_laboratory_notebook(planification_id,
technician_replaced_id, technician_substitute_id)
return 'end'
def update_planification_detail(self, planification_id,
technician_replaced_id, technician_substitute_id):
PlanificationServiceDetailProfessional = Pool().get(
'lims.planification.service_detail-laboratory.professional')
details_professional = PlanificationServiceDetailProfessional.search([
('detail.detail.planification', '=', planification_id),
('professional', '=', technician_replaced_id),
])
PlanificationServiceDetailProfessional.write(details_professional, {
'professional': technician_substitute_id,
})
def update_planification_technician(self, planification_id,
technician_replaced_id, technician_substitute_id):
PlanificationTechnician = Pool().get('lims.planification.technician')
planification_professional = PlanificationTechnician.search([
('planification', '=', planification_id),
('laboratory_professional', '=', technician_replaced_id),
])
PlanificationTechnician.delete(planification_professional)
planification_professional = PlanificationTechnician.search([
('planification', '=', planification_id),
('laboratory_professional', '=', technician_substitute_id),
])
if not planification_professional:
PlanificationTechnician.create([{
'planification': planification_id,
'laboratory_professional': technician_substitute_id,
}])
def update_laboratory_notebook(self, planification_id,
technician_replaced_id, technician_substitute_id):
NotebookLineProfessional = Pool().get(
'lims.notebook.line-laboratory.professional')
notebook_line_professional = NotebookLineProfessional.search([
('notebook_line.planification', '=', planification_id),
('professional', '=', technician_replaced_id),
])
NotebookLineProfessional.write(notebook_line_professional, {
'professional': technician_substitute_id,
})
class LoadServices(Wizard):
'Load Services'
__name__ = 'lims.load_services'
2017-10-08 02:23:22 +02:00
start_state = 'check'
check = StateTransition()
load = StateTransition()
2017-10-08 02:23:22 +02:00
def transition_check(self):
Fraction = Pool().get('lims.fraction')
2017-10-08 02:23:22 +02:00
fraction = Fraction(Transaction().context['active_id'])
if (not fraction or not fraction.cie_fraction_type or
not fraction.cie_original_fraction):
return 'end'
return 'load'
2017-10-08 02:23:22 +02:00
def transition_load(self):
pool = Pool()
Fraction = pool.get('lims.fraction')
Service = pool.get('lims.service')
Analysis = pool.get('lims.analysis')
new_fraction = Fraction(Transaction().context['active_id'])
original_fraction = new_fraction.cie_original_fraction
# new services
services = Service.search([
('fraction', '=', original_fraction),
])
for service in services:
if not Analysis.is_typified(service.analysis,
new_fraction.product_type, new_fraction.matrix):
continue
new_service, = Service.copy([service], default={
'fraction': new_fraction.id,
})
return 'end'
class PlanificationSequenceReport(Report):
'Sequence'
__name__ = 'lims.planification.sequence.report'
@classmethod
def get_context(cls, records, data):
report_context = super(PlanificationSequenceReport,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
objects = {}
for planification in records:
if planification.state != 'confirmed':
continue
date = str(planification.start_date)
if date not in objects:
objects[date] = {
'date': planification.start_date,
'methods': {},
}
for detail in planification.details:
fraction = detail.fraction
for service_detail in detail.details:
if (service_detail.notebook_line.analysis.behavior ==
'internal_relation'):
continue
notebook_line = service_detail.notebook_line
method_id = notebook_line.method.id
if method_id not in objects[date]['methods']:
objects[date]['methods'][method_id] = {
'method': notebook_line.method.code,
'lines': {},
}
number = fraction.get_formated_number('sn-sy-fn')
number = (number + '-' + str(notebook_line.repetition))
number_parts = number.split('-')
order = (number_parts[1] + '-' + number_parts[0] + '-' +
number_parts[2] + '-' + number_parts[3])
product_type = fraction.product_type.code
matrix = fraction.matrix.code
fraction_type = fraction.type.code
comments = fraction.comments
analysis_origin = notebook_line.analysis_origin
priority = notebook_line.priority
urgent = notebook_line.urgent
report_date = (notebook_line.report_date or
notebook_line.results_estimated_date)
trace_report = fraction.sample.trace_report
sample_client_description = (
fraction.sample.sample_client_description)
key = (number, product_type, matrix, fraction_type,
analysis_origin, priority, trace_report)
if key not in objects[date]['methods'][method_id]['lines']:
objects[date]['methods'][method_id]['lines'][key] = {
'order': order,
'number': number,
'product_type': product_type,
'matrix': matrix,
'fraction_type': fraction_type,
'analysis_origin': analysis_origin,
'priority': priority,
'urgent': urgent,
'report_date': report_date,
'trace_report': trace_report,
'comments': comments,
'sample_client_description': (
sample_client_description),
}
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2, lines in objects[k1]['methods'].items():
sorted_lines = sorted(list(lines['lines'].values()),
key=lambda x: x['order'])
objects[k1]['methods'][k2]['lines'] = sorted_lines
report_context['objects'] = objects
return report_context
class PlanificationWorksheetAnalysisReport(Report):
'Worksheet by Analysis'
__name__ = 'lims.planification.worksheet_analysis.report'
@classmethod
def get_context(cls, records, data):
report_context = super(PlanificationWorksheetAnalysisReport,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
objects = {}
for planification in records:
if planification.state != 'confirmed':
continue
date = str(planification.start_date)
if date not in objects:
objects[date] = {
'date': planification.start_date,
'professionals': {},
}
for detail in planification.details:
fraction = detail.fraction
for service_detail in detail.details:
if not service_detail.notebook_line:
continue
if (service_detail.notebook_line.analysis.behavior ==
'internal_relation'):
continue
p_key = ()
p_names = ''
for professional in service_detail.staff_responsible:
p_key += (professional.id,)
if p_names:
p_names += ', '
p_names += professional.rec_name
if not p_key:
continue
p_key = tuple(sorted(p_key))
if (p_key not in objects[date]['professionals']):
objects[date]['professionals'][p_key] = {
'professional': p_names,
'analysis': {},
'total': 0,
}
notebook_line = service_detail.notebook_line
key = (notebook_line.analysis.id, notebook_line.method.id)
if (key not in
objects[date]['professionals'][p_key]['analysis']):
objects[date]['professionals'][p_key]['analysis'][
key] = {
'order': notebook_line.analysis.order or 9999,
'analysis': notebook_line.analysis.rec_name,
'method': notebook_line.method.rec_name,
'lines': {},
}
number = fraction.get_formated_number('pt-m-sy-sn-fn')
number = (number + '-' + str(notebook_line.repetition))
concentration_level = (
notebook_line.concentration_level.description if
notebook_line.concentration_level else '')
if (number in objects[date]['professionals'][p_key][
'analysis'][key]['lines']):
continue
number_parts = number.split('-')
order = (number_parts[3] + '-' + number_parts[2] + '-' +
number_parts[4] + '-' + number_parts[5])
comments = '%s - %s - %s' % (planification.comments or '',
notebook_line.service.comments or '',
notebook_line.service.fraction.comments or '')
record = {
'order': order,
'number': number,
'label': fraction.label,
'sample_client_description': (
fraction.sample.sample_client_description),
'party': fraction.party.code,
'storage_location': fraction.storage_location.code,
'fraction_type': fraction.type.code,
'concentration_level': concentration_level,
'device': (notebook_line.device.code if
notebook_line.device else None),
'urgent': 'SI' if notebook_line.urgent else '',
'comments': comments,
'planification_code': planification.code,
'sample_obj_description': (
fraction.sample.obj_description.description
if fraction.sample.obj_description else
fraction.sample.obj_description_manual
if fraction.sample.obj_description_manual else
''),
}
objects[date]['professionals'][p_key]['analysis'][
key]['lines'][number] = record
objects[date]['professionals'][p_key]['total'] += 1
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2 in objects[k1]['professionals'].keys():
sorted_analysis = sorted(list(objects[k1]['professionals'][k2][
'analysis'].items()), key=lambda x: x[1]['order'])
objects[k1]['professionals'][k2]['analysis'] = []
for item in sorted_analysis:
2019-03-04 15:41:58 +01:00
sorted_lines = sorted(list(item[1]['lines'].items()),
key=lambda x: x[1]['order'])
item[1]['lines'] = [l[1] for l in sorted_lines]
objects[k1]['professionals'][k2]['analysis'].append(
item[1])
report_context['records'] = objects
return report_context
class PlanificationWorksheetMethodReport(Report):
'Worksheet by Method'
__name__ = 'lims.planification.worksheet_method.report'
@classmethod
def get_context(cls, records, data):
report_context = super(PlanificationWorksheetMethodReport,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
objects = {}
for planification in records:
if planification.state != 'confirmed':
continue
date = str(planification.start_date)
if date not in objects:
objects[date] = {
'date': planification.start_date,
'professionals': {},
}
for detail in planification.details:
fraction = detail.fraction
for service_detail in detail.details:
if not service_detail.notebook_line:
continue
if (service_detail.notebook_line.analysis.behavior ==
'internal_relation'):
continue
p_key = ()
p_names = ''
for professional in service_detail.staff_responsible:
p_key += (professional.id,)
if p_names:
p_names += ', '
p_names += professional.rec_name
if not p_key:
continue
p_key = tuple(sorted(p_key))
if (p_key not in objects[date]['professionals']):
objects[date]['professionals'][p_key] = {
'professional': p_names,
'total': 0,
'lines': {},
}
notebook_line = service_detail.notebook_line
number = fraction.get_formated_number('pt-m-sy-sn-fn')
number = (number + '-' + str(notebook_line.repetition))
concentration_level = (
notebook_line.concentration_level.description if
notebook_line.concentration_level else '')
if (number not in objects[date]['professionals'][p_key][
'lines']):
number_parts = number.split('-')
order = (number_parts[3] + '-' + number_parts[2] +
'-' + number_parts[4] + '-' + number_parts[5])
comments_planif = '. '.join(cls.get_planning_legend(
fraction, planification))
if comments_planif:
comments = '%s - %s - %s - %s' % (
planification.comments or '',
notebook_line.service.comments or '',
notebook_line.service.fraction.comments or '',
comments_planif or '')
else:
comments = '%s - %s - %s' % (
planification.comments or
'', notebook_line.service.comments or '',
notebook_line.service.fraction.comments or '')
if fraction.packages_quantity != 0.0:
pack_quant = str(fraction.packages_quantity)
else:
pack_quant = ''
record = {
'order': order,
'number': number,
'label': fraction.label,
'sample_client_description': (
fraction.sample.sample_client_description),
'party': fraction.party.code,
'storage_location': fraction.storage_location.code,
'fraction_type': fraction.type.code,
'concentration_level': concentration_level,
'device': (notebook_line.device.code if
notebook_line.device else None),
'urgent': 'SI' if notebook_line.urgent else '',
'comments': comments,
'planification_code': planification.code,
'package_type':
pack_quant + ' ' +
fraction.package_type.description,
'methods': {},
'sample_obj_description': (
fraction.sample.obj_description.description
if fraction.sample.obj_description else
fraction.sample.obj_description_manual
if fraction.sample.obj_description_manual else
''),
}
objects[date]['professionals'][p_key]['lines'][
number] = record
objects[date]['professionals'][p_key]['total'] += 1
if (notebook_line.method.id not in
objects[date]['professionals'][p_key]['lines'][
number]['methods']):
objects[date]['professionals'][p_key]['lines'][
number]['methods'][notebook_line.method.id] = (
notebook_line.method.rec_name)
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2 in objects[k1]['professionals'].keys():
objects[k1]['professionals'][k2]['methods'] = {}
2019-07-23 23:27:33 +02:00
fractions = list(
objects[k1]['professionals'][k2]['lines'].values())
for fraction in fractions:
m_key = ()
m_names = []
2019-03-04 15:41:58 +01:00
for m_id, m_name in fraction['methods'].items():
m_key += (m_id,)
m_names.append(m_name)
m_key = tuple(sorted(m_key))
if (m_key not in objects[k1]['professionals'][k2][
'methods']):
objects[k1]['professionals'][k2]['methods'][m_key] = {
'methods': m_names,
'lines': [],
}
objects[k1]['professionals'][k2]['methods'][m_key][
'lines'].append(fraction)
del objects[k1]['professionals'][k2]['lines']
for m_key in objects[k1]['professionals'][k2][
2019-03-04 15:41:58 +01:00
'methods'].keys():
sorted_lines = sorted(objects[k1]['professionals'][k2][
'methods'][m_key]['lines'], key=lambda x: x['order'])
objects[k1]['professionals'][k2]['methods'][m_key][
'lines'] = sorted_lines
report_context['records'] = objects
return report_context
@classmethod
def get_planning_legend(cls, fraction, planification):
cursor = Transaction().connection.cursor()
pool = Pool()
Analysis = pool.get('lims.analysis')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
PlanificationDetail = pool.get('lims.planification.detail')
NotebookLine = pool.get('lims.notebook.line')
cursor.execute('SELECT DISTINCT(a.planning_legend) '
'FROM "' + PlanificationDetail._table + '" pd '
'INNER JOIN "' + PlanificationServiceDetail._table +
'" psd '
'ON pd.id = psd.detail '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON nl.id = psd.notebook_line '
'INNER JOIN "' + Analysis._table + '" a '
'ON a.id = nl.analysis '
'WHERE pd.planification = %s '
'AND pd.fraction = %s '
'AND a.planning_legend IS NOT NULL ',
(planification.id, fraction.id))
planned_ids = [s[0] for s in cursor.fetchall()]
return planned_ids
class PlanificationWorksheetReport(Report):
'Worksheet'
__name__ = 'lims.planification.worksheet.report'
@classmethod
def get_context(cls, records, data):
report_context = super(PlanificationWorksheetReport,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
objects = {}
for planification in records:
if planification.state != 'confirmed':
continue
date = str(planification.start_date)
if date not in objects:
objects[date] = {
'date': planification.start_date,
'professionals': {},
}
for detail in planification.details:
fraction = detail.fraction
for service_detail in detail.details:
if not service_detail.notebook_line:
continue
if (service_detail.notebook_line.analysis.behavior ==
'internal_relation'):
continue
p_key = ()
p_names = ''
for professional in service_detail.staff_responsible:
p_key += (professional.id,)
if p_names:
p_names += ', '
p_names += professional.rec_name
if not p_key:
continue
p_key = tuple(sorted(p_key))
if (p_key not in objects[date]['professionals']):
objects[date]['professionals'][p_key] = {
'professional': p_names,
'analysis': {},
'total': 0,
}
notebook_line = service_detail.notebook_line
key = service_detail.planned_service.id
if (key not in
objects[date]['professionals'][p_key]['analysis']):
objects[date]['professionals'][p_key]['analysis'][
key] = {
'analysis': (
service_detail.planned_service.rec_name),
'lines': {},
}
number = fraction.get_formated_number('pt-m-sy-sn-fn')
number = (number + '-' + str(notebook_line.repetition))
concentration_level = (
notebook_line.concentration_level.description if
notebook_line.concentration_level else '')
if (number not in objects[date]['professionals'][p_key][
'analysis'][key]['lines']):
number_parts = number.split('-')
order = (number_parts[3] + '-' + number_parts[2] +
'-' + number_parts[4] + '-' + number_parts[5])
comments_planif = '. '.join(cls.get_planning_legend(
fraction, planification))
if comments_planif:
comments = '%s - %s - %s - %s' % (
planification.comments or '',
notebook_line.service.comments or '',
notebook_line.service.fraction.comments or '',
comments_planif or '')
else:
comments = '%s - %s - %s' % (
planification.comments or
'', notebook_line.service.comments or '',
notebook_line.service.fraction.comments or '')
if fraction.packages_quantity != 0.0:
pack_quant = str(fraction.packages_quantity)
else:
pack_quant = ''
record = {
'order': order,
'number': number,
'label': fraction.label,
'sample_client_description': (
fraction.sample.sample_client_description if
fraction.sample.sample_client_description else
''),
'party': fraction.party.code,
'storage_location': fraction.storage_location.code,
'fraction_type': fraction.type.code,
'concentration_level': concentration_level,
'device': (notebook_line.device.code if
notebook_line.device else None),
'urgent': 'SI' if notebook_line.urgent else '',
'comments': comments,
'planification_code': planification.code,
'package_type':
pack_quant + ' ' +
fraction.package_type.description,
'methods': {},
'sample_obj_description': (
fraction.sample.obj_description.description
if fraction.sample.obj_description else
fraction.sample.obj_description_manual
if fraction.sample.obj_description_manual else
''),
}
objects[date]['professionals'][p_key]['analysis'][key][
'lines'][number] = record
objects[date]['professionals'][p_key]['total'] += 1
if (notebook_line.method.id not in
objects[date]['professionals'][p_key]['analysis'][
key]['lines'][number]['methods']):
objects[date]['professionals'][p_key]['analysis'][
key]['lines'][number]['methods'][
notebook_line.method.id] = (
notebook_line.method.rec_name)
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2 in objects[k1]['professionals'].keys():
for k3 in objects[k1]['professionals'][k2][
2019-03-04 15:41:58 +01:00
'analysis'].keys():
objects[k1]['professionals'][k2]['analysis'][k3][
'methods'] = {}
2019-07-23 23:27:33 +02:00
fractions = list(
objects[k1]['professionals'][k2]['analysis'][
k3]['lines'].values())
for fraction in fractions:
m_key = ()
m_names = []
2019-03-04 15:41:58 +01:00
for m_id, m_name in fraction['methods'].items():
m_key += (m_id,)
m_names.append(m_name)
m_key = tuple(sorted(m_key))
if (m_key not in objects[k1]['professionals'][k2][
'analysis'][k3]['methods']):
objects[k1]['professionals'][k2]['analysis'][k3][
'methods'][m_key] = {
'methods': m_names,
'lines': [],
}
objects[k1]['professionals'][k2]['analysis'][k3][
'methods'][m_key]['lines'].append(fraction)
del objects[k1]['professionals'][k2]['analysis'][k3][
'lines']
for m_key in objects[k1]['professionals'][k2][
2019-03-04 15:41:58 +01:00
'analysis'][k3]['methods'].keys():
sorted_lines = sorted(objects[k1]['professionals'][k2][
'analysis'][k3]['methods'][m_key]['lines'],
key=lambda x: x['order'])
objects[k1]['professionals'][k2]['analysis'][k3][
'methods'][m_key]['lines'] = sorted_lines
report_context['records'] = objects
return report_context
@classmethod
def get_planning_legend(cls, fraction, planification):
cursor = Transaction().connection.cursor()
pool = Pool()
Analysis = pool.get('lims.analysis')
PlanificationServiceDetail = pool.get(
'lims.planification.service_detail')
PlanificationDetail = pool.get('lims.planification.detail')
NotebookLine = pool.get('lims.notebook.line')
cursor.execute('SELECT DISTINCT(a.planning_legend) '
'FROM "' + PlanificationDetail._table + '" pd '
'INNER JOIN "' + PlanificationServiceDetail._table +
'" psd '
'ON pd.id = psd.detail '
'INNER JOIN "' + NotebookLine._table + '" nl '
'ON nl.id = psd.notebook_line '
'INNER JOIN "' + Analysis._table + '" a '
'ON a.id = nl.analysis '
'WHERE pd.planification = %s '
'AND pd.fraction = %s '
'AND a.planning_legend IS NOT NULL ',
(planification.id, fraction.id))
planned_ids = [s[0] for s in cursor.fetchall()]
return planned_ids
class PrintPendingServicesUnplannedReportStart(ModelView):
'Print Pending Services Unplanned Report Start'
__name__ = 'lims.pending_services_unplanned.start'
start_date = fields.Date('Start Date', required=True)
end_date = fields.Date('End Date', required=True)
party = fields.Many2One('party.party', 'Party')
include_method = fields.Boolean('Include method')
class PrintPendingServicesUnplannedReport(Wizard):
'Print Pending Services Unplanned Report'
__name__ = 'lims.pending_services_unplanned'
start = StateView('lims.pending_services_unplanned.start',
'lims.print_pending_services_unplanned_report_start'
'_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-print', default=True),
Button('Save', 'save', 'tryton-save'),
])
print_ = StateAction(
'lims.report_pending_services_unplanned')
save = StateAction(
'lims.report_pending_services_unplanned_spreadsheet')
def do_print_(self, action):
data = {
'start_date': self.start.start_date,
'end_date': self.start.end_date or None,
'party': self.start.party and self.start.party.id or None,
'include_method': self.start.include_method,
}
return action, data
def do_save(self, action):
data = {
'start_date': self.start.start_date,
'end_date': self.start.end_date or None,
'party': self.start.party and self.start.party.id or None,
'include_method': self.start.include_method,
}
return action, data
class PendingServicesUnplannedReport(Report):
'Pending Services Unplanned'
__name__ = 'lims.pending_services_unplanned.report'
@classmethod
def get_context(cls, records, data):
pool = Pool()
Laboratory = pool.get('lims.laboratory')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
report_context = super(PendingServicesUnplannedReport,
cls).get_context(records, data)
2017-10-08 02:23:22 +02:00
report_context['company'] = report_context['user'].company
report_context['start_date'] = (data['start_date']
if data['start_date'] else '')
report_context['end_date'] = (data['end_date']
if data['end_date'] else '')
report_context['include_method'] = data['include_method']
if report_context['user'].laboratory:
labs = [report_context['user'].laboratory.id]
else:
labs = [l.id for l in Laboratory.search([])]
clause = [
('plannable', '=', True),
('state', '=', 'unplanned'),
('analysis.behavior', '!=', 'internal_relation'),
('service.fraction.confirmed', '=', True),
]
if data['start_date']:
clause.append(
('service.confirmation_date', '>=', data['start_date']))
if data['end_date']:
clause.append(
('service.confirmation_date', '<=', data['end_date']))
if data['party']:
clause.append(('party', '=', data['party']))
objects = {}
with Transaction().set_user(0):
details = EntryDetailAnalysis.search(clause)
for detail in details:
# Laboratory
laboratory_id = detail.laboratory.id
if laboratory_id not in labs:
continue
if laboratory_id not in objects:
objects[laboratory_id] = {
'laboratory': detail.laboratory.rec_name,
'services': {},
'total': 0,
}
# Service
analysis_id = detail.service.analysis.id
if analysis_id not in objects[laboratory_id]['services']:
objects[laboratory_id]['services'][analysis_id] = {
'service': detail.service.analysis.rec_name,
'parties': {},
'total': 0,
}
# Party
party_id = detail.party.id
if (party_id not in objects[laboratory_id]['services'][
analysis_id]['parties']):
objects[laboratory_id]['services'][analysis_id]['parties'][
party_id] = {
'party': detail.party.code,
'lines': {},
'total': 0,
}
# Key
if data['include_method']:
key = (detail.fraction.id, detail.method.id)
else:
key = detail.fraction.id
if (key in objects[laboratory_id]['services'][analysis_id][
'parties'][party_id]['lines']):
continue
number = detail.service.fraction.get_formated_number(
'pt-m-sn-sy-fn')
number = (number + '-' + str(detail.service.sample.label))
number_parts = number.split('-')
order = (number_parts[3] + '-' + number_parts[2] + '-' +
number_parts[4])
results_estimated_date = None
results_estimated_waiting = cls._get_estimated_waiting(detail.id)
if results_estimated_waiting:
results_estimated_date = (detail.service.confirmation_date +
relativedelta(days=results_estimated_waiting))
record = {
'order': order,
'number': number,
'date': detail.service.sample.date2,
'sample_client_description': (
detail.service.sample.sample_client_description),
'current_location': (
detail.service.fraction.current_location.code
if detail.service.fraction.current_location else ''),
'fraction_type': detail.service.fraction.type.code,
'priority': detail.service.priority,
'urgent': 'Yes' if detail.service.urgent else 'No',
'method': detail.method.code if data['include_method'] else '',
'comments': ('%s - %s' % (
detail.service.fraction.comments or '',
detail.service.sample.comments or '')),
'report_date': detail.service.report_date,
'confirmation_date': (detail.service.confirmation_date
if detail.service.confirmation_date else ''),
'results_estimated_date': results_estimated_date or '',
}
objects[laboratory_id]['services'][analysis_id]['parties'][
party_id]['lines'][key] = record
objects[laboratory_id]['services'][analysis_id]['parties'][
party_id]['total'] += 1
objects[laboratory_id]['services'][analysis_id][
'total'] += 1
objects[laboratory_id]['total'] += 1
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2 in objects[k1]['services'].keys():
for k3, lines in objects[k1]['services'][k2][
2019-03-04 15:41:58 +01:00
'parties'].items():
sorted_lines = sorted(lines['lines'].values(),
key=lambda x: x['order'])
objects[k1]['services'][k2]['parties'][k3]['lines'] = (
sorted_lines)
report_context['objects'] = objects
return report_context
@classmethod
def _get_estimated_waiting(cls, detail_id):
cursor = Transaction().connection.cursor()
NotebookLine = Pool().get('lims.notebook.line')
2017-10-08 02:23:22 +02:00
cursor.execute('SELECT MIN(results_estimated_waiting) '
'FROM "' + NotebookLine._table + '" '
'WHERE analysis_detail = %s '
'AND results_estimated_waiting IS NOT NULL',
(detail_id,))
res = cursor.fetchone()
return res and res[0] or None
2017-10-08 02:23:22 +02:00
class PendingServicesUnplannedSpreadsheet(Report):
'Pending Services Unplanned'
__name__ = 'lims.pending_services_unplanned.spreadsheet'
@classmethod
def get_context(cls, records, data):
2017-10-08 02:23:22 +02:00
pool = Pool()
Laboratory = pool.get('lims.laboratory')
EntryDetailAnalysis = pool.get('lims.entry.detail.analysis')
report_context = super(PendingServicesUnplannedSpreadsheet,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
report_context['start_date'] = (data['start_date']
if data['start_date'] else '')
report_context['end_date'] = (data['end_date']
if data['end_date'] else '')
report_context['include_method'] = data['include_method']
2017-10-08 02:23:22 +02:00
if report_context['user'].laboratory:
labs = [report_context['user'].laboratory.id]
else:
labs = [l.id for l in Laboratory.search([])]
clause = [
('plannable', '=', True),
('state', '=', 'unplanned'),
('analysis.behavior', '!=', 'internal_relation'),
('service.fraction.confirmed', '=', True),
]
if data['start_date']:
clause.append(
('service.confirmation_date', '>=', data['start_date']))
if data['end_date']:
clause.append(
('service.confirmation_date', '<=', data['end_date']))
if data['party']:
clause.append(('party', '=', data['party']))
objects = {}
with Transaction().set_user(0):
details = EntryDetailAnalysis.search(clause)
for detail in details:
# Laboratory
laboratory_id = detail.laboratory.id
if laboratory_id not in labs:
continue
# Key
if data['include_method']:
key = (detail.fraction.id, detail.method.id)
else:
key = detail.fraction.id
if key in objects:
continue
2017-10-08 02:23:22 +02:00
number = detail.service.fraction.get_formated_number(
'pt-m-sn-sy-fn')
number = (number + '-' + str(detail.service.sample.label))
number_parts = number.split('-')
order = (number_parts[3] + '-' + number_parts[2] + '-' +
number_parts[4])
results_estimated_date = None
results_estimated_waiting = cls._get_estimated_waiting(detail.id)
if results_estimated_waiting:
results_estimated_date = (detail.service.confirmation_date +
relativedelta(days=results_estimated_waiting))
notice = None
report_date = detail.service.report_date
today = get_print_date().date()
if report_date:
if report_date < today:
notice = 'Timed out'
else:
d = (report_date - today).days
if d >= 0 and d < 3:
notice = 'To expire'
elif results_estimated_date:
if results_estimated_date < today:
notice = 'Timed out'
else:
d = (results_estimated_date - today).days
if d >= 0 and d < 3:
notice = 'To expire'
record = {
'laboratory': detail.laboratory.rec_name,
'service': detail.service.analysis.rec_name,
'party': detail.party.code,
'order': order,
'number': number,
'date': detail.service.sample.date2,
'sample_client_description': (
detail.service.sample.sample_client_description),
'current_location': (
detail.service.fraction.current_location.code
if detail.service.fraction.current_location else ''),
'fraction_type': detail.service.fraction.type.code,
'priority': detail.service.priority,
'urgent': 'Yes' if detail.service.urgent else 'No',
'method': detail.method.code if data['include_method'] else '',
'comments': ('%s - %s' % (
detail.service.fraction.comments or '',
detail.service.sample.comments or '')),
'report_date': detail.service.report_date,
'confirmation_date': (detail.service.confirmation_date
if detail.service.confirmation_date else ''),
'results_estimated_date': results_estimated_date or '',
'results_estimated_waiting': results_estimated_waiting or '',
'notice': notice or '',
}
objects[key] = record
objects = sorted(objects.values(), key=lambda x: (
x['laboratory'], x['service'], x['party'], x['order']))
2017-10-08 02:23:22 +02:00
report_context['objects'] = objects
return report_context
2017-10-08 02:23:22 +02:00
@classmethod
def _get_estimated_waiting(cls, detail_id):
cursor = Transaction().connection.cursor()
NotebookLine = Pool().get('lims.notebook.line')
cursor.execute('SELECT MIN(results_estimated_waiting) '
'FROM "' + NotebookLine._table + '" '
'WHERE analysis_detail = %s '
'AND results_estimated_waiting IS NOT NULL',
(detail_id,))
res = cursor.fetchone()
return res and res[0] or None
class PrintBlindSampleReportStart(ModelView):
'Blind Samples Report'
__name__ = 'lims.print_blind_sample_report.start'
date_from = fields.Date('Date from', required=True)
date_to = fields.Date('Date to', required=True)
class PrintBlindSampleReport(Wizard):
'Blind Samples Report'
__name__ = 'lims.print_blind_sample_report'
start = StateView('lims.print_blind_sample_report.start',
'lims.lims_print_blind_sample_report_start_view_form', [
Button('Cancel', 'end', 'tryton-cancel'),
Button('Print', 'print_', 'tryton-print', default=True),
2017-10-08 02:23:22 +02:00
])
print_ = StateAction('lims.report_blind_sample')
def do_print_(self, action):
BlindSample = Pool().get('lims.blind_sample')
blind_samples = BlindSample.search_count([
('date', '>=', self.start.date_from),
('date', '<=', self.start.date_to),
('line.result', '!=', None),
])
if blind_samples > 0:
data = {
'date_from': self.start.date_from,
'date_to': self.start.date_to,
}
return action, data
def transition_print_(self):
2017-10-08 02:23:22 +02:00
return 'end'
class BlindSampleReport(Report):
'Blind Samples Report'
__name__ = 'lims.blind_sample_report'
@classmethod
def get_context(cls, records, data):
report_context = super(BlindSampleReport, cls).get_context(records,
data)
BlindSample = Pool().get('lims.blind_sample')
report_context['company'] = report_context['user'].company
objects = []
blind_samples = BlindSample.search([
('date', '>=', data['date_from']),
('date', '<=', data['date_to']),
('line.result', '!=', None),
], order=[('date', 'ASC')])
for bs in blind_samples:
if bs.line.converted_result:
result = float(bs.line.converted_result)
unit = bs.line.final_unit
concentration = bs.line.final_concentration
result = round(result, bs.line.decimals)
elif bs.line.result:
result = float(bs.line.result)
unit = bs.line.initial_unit
concentration = bs.line.initial_concentration
result = round(result, bs.line.decimals)
else:
result = None
unit = None
concentration = ''
record = {
'date': bs.date,
'fraction': bs.fraction.rec_name,
'report': (bs.line.results_report.number if
bs.line.results_report else ''),
'repetition': bs.line.repetition,
'analysis_origin': bs.line.analysis_origin,
'analysis': bs.analysis.rec_name,
'result': result,
'unit': unit.symbol if unit else '',
'concentration': concentration,
'original_fraction': '',
'original_report': '',
'original_result': '',
'original_unit': '',
'original_concentration': '',
'error': '',
'max_sd': '',
'two_max_sd': '',
'accepted': '',
}
if bs.original_fraction:
record['original_fraction'] = bs.original_fraction.rec_name
if bs.original_line:
if bs.original_line.results_report:
record['original_report'] = (
bs.original_line.results_report.number)
if bs.original_line.converted_result:
original_result = float(
bs.original_line.converted_result)
original_unit = bs.original_line.final_unit
original_concentration = (
bs.original_line.final_concentration)
elif bs.original_line.result:
original_result = float(bs.original_line.result)
original_unit = bs.original_line.initial_unit
original_concentration = (
bs.original_line.initial_concentration)
else:
original_result = None
if original_result:
original_result = round(original_result,
bs.original_line.decimals)
record['original_result'] = original_result
record['original_unit'] = (original_unit.symbol
if original_unit else '')
record['original_concentration'] = (
original_concentration)
if unit == original_unit:
average = (result + original_result) / 2
difference = result - original_result
record['error'] = round(difference * 100 / average,
2)
try:
maximum_concentration = float(
unit.maximum_concentration)
rsd_horwitz = float(unit.rsd_horwitz)
except (TypeError, ValueError):
maximum_concentration = 0
rsd_horwitz = 0
if result <= maximum_concentration:
record['max_sd'] = round(average * rsd_horwitz,
2)
record['two_max_sd'] = round(record['max_sd'] *
2, 2)
if abs(difference) <= record['two_max_sd']:
record['accepted'] = 'a'
else:
record['accepted'] = 'r'
else:
if bs.min_value and bs.max_value:
min_value = float(bs.min_value)
max_value = float(bs.max_value)
if min_value <= result and result <= max_value:
record['error'] = 'OK'
elif result < min_value:
record['error'] = round(result - min_value, 2)
elif result > max_value:
record['error'] = round(result - max_value, 2)
objects.append(record)
report_context['objects'] = objects
report_context['date_from'] = data['date_from']
report_context['date_to'] = data['date_to']
return report_context
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
2019-03-04 15:41:58 +01:00
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
class PlanificationSequenceAnalysisReport(Report):
'Sequence Analysis'
__name__ = 'lims.planification.sequence.analysis.report'
@classmethod
def get_context(cls, records, data):
report_context = super(PlanificationSequenceAnalysisReport,
cls).get_context(records, data)
report_context['company'] = report_context['user'].company
objects = {}
for planification in records:
if planification.state != 'confirmed':
continue
date = str(planification.start_date)
if date not in objects:
objects[date] = {
'date': planification.start_date,
'methods': {},
}
for detail in planification.details:
fraction = detail.fraction
for service_detail in detail.details:
if (service_detail.notebook_line.analysis.behavior ==
'internal_relation'):
continue
notebook_line = service_detail.notebook_line
method_id = notebook_line.method.id
if method_id not in objects[date]['methods']:
objects[date]['methods'][method_id] = {
'method': notebook_line.method.code,
'lines': {},
}
number = fraction.get_formated_number('sn-sy-fn')
number = (number + '-' + str(notebook_line.repetition))
number_parts = number.split('-')
order = (number_parts[1] + '-' + number_parts[0] + '-' +
number_parts[2] + '-' + number_parts[3])
product_type = fraction.product_type.code
matrix = fraction.matrix.code
fraction_type = fraction.type.code
analysis = notebook_line.analysis.rec_name
initial_unit = (notebook_line.initial_unit.symbol
if notebook_line.initial_unit else '')
priority = notebook_line.priority
urgent = notebook_line.urgent
report_date = (notebook_line.report_date or
notebook_line.results_estimated_date)
trace_report = fraction.sample.trace_report
sample_client_description = (
fraction.sample.sample_client_description)
key = (number, product_type, matrix, fraction_type,
analysis, priority, trace_report)
if key not in objects[date]['methods'][method_id]['lines']:
objects[date]['methods'][method_id]['lines'][key] = {
'order': order,
'number': number,
'product_type': product_type,
'matrix': matrix,
'fraction_type': fraction_type,
'analysis': analysis,
'priority': priority,
'urgent': urgent,
'report_date': report_date,
'trace_report': trace_report,
'sample_client_description': (
sample_client_description),
'initial_unit': initial_unit,
}
2019-03-04 15:41:58 +01:00
for k1 in objects.keys():
for k2, lines in objects[k1]['methods'].items():
sorted_lines = sorted(list(lines['lines'].values()),
key=lambda x: x['order'])
objects[k1]['methods'][k2]['lines'] = sorted_lines
report_context['objects'] = objects
return report_context