
- Creados 4 archivos de test completos con cobertura total - test_analysis_parameter.py: Tests del modelo de parámetros - test_parameter_range.py: Tests de rangos de referencia - test_result_parameter_integration.py: Tests de integración - test_auto_result_generation.py: Tests de generación automática - Creado script simplificado test_parameters_simple.py para ejecución rápida - Corregido valor por defecto de age_max a 150 en parameter_range.py - Documentación completa en README.md
214 lines
7.2 KiB
Python
214 lines
7.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo import models, fields, api
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class LimsParameterRange(models.Model):
|
|
_name = 'lims.parameter.range'
|
|
_description = 'Rangos de Referencia por Parámetro'
|
|
_order = 'parameter_id, gender desc, age_min'
|
|
_rec_name = 'name'
|
|
|
|
parameter_id = fields.Many2one(
|
|
'lims.analysis.parameter',
|
|
string='Parámetro',
|
|
required=True,
|
|
ondelete='cascade',
|
|
help='Parámetro al que aplica este rango de referencia'
|
|
)
|
|
|
|
name = fields.Char(
|
|
string='Descripción',
|
|
compute='_compute_name',
|
|
store=True,
|
|
help='Descripción automática del rango'
|
|
)
|
|
|
|
# Condiciones
|
|
gender = fields.Selection([
|
|
('male', 'Masculino'),
|
|
('female', 'Femenino'),
|
|
('both', 'Ambos')
|
|
],
|
|
string='Género',
|
|
default='both',
|
|
required=True,
|
|
help='Género al que aplica este rango'
|
|
)
|
|
|
|
age_min = fields.Integer(
|
|
string='Edad Mínima',
|
|
default=0,
|
|
help='Edad mínima en años (inclusive)'
|
|
)
|
|
|
|
age_max = fields.Integer(
|
|
string='Edad Máxima',
|
|
default=150,
|
|
help='Edad máxima en años (inclusive)'
|
|
)
|
|
|
|
pregnant = fields.Boolean(
|
|
string='Embarazada',
|
|
default=False,
|
|
help='Marcar si este rango es específico para mujeres embarazadas'
|
|
)
|
|
|
|
# Valores de referencia
|
|
normal_min = fields.Float(
|
|
string='Valor Normal Mínimo',
|
|
help='Límite inferior del rango normal'
|
|
)
|
|
|
|
normal_max = fields.Float(
|
|
string='Valor Normal Máximo',
|
|
help='Límite superior del rango normal'
|
|
)
|
|
|
|
critical_min = fields.Float(
|
|
string='Valor Crítico Mínimo',
|
|
help='Por debajo de este valor es crítico'
|
|
)
|
|
|
|
critical_max = fields.Float(
|
|
string='Valor Crítico Máximo',
|
|
help='Por encima de este valor es crítico'
|
|
)
|
|
|
|
# Información adicional
|
|
interpretation = fields.Text(
|
|
string='Interpretación',
|
|
help='Guía de interpretación clínica para este rango'
|
|
)
|
|
|
|
# Campos relacionados para facilitar búsquedas
|
|
parameter_name = fields.Char(
|
|
related='parameter_id.name',
|
|
string='Nombre del Parámetro',
|
|
store=True,
|
|
readonly=True
|
|
)
|
|
|
|
parameter_code = fields.Char(
|
|
related='parameter_id.code',
|
|
string='Código del Parámetro',
|
|
store=True,
|
|
readonly=True
|
|
)
|
|
|
|
parameter_unit = fields.Char(
|
|
related='parameter_id.unit',
|
|
string='Unidad',
|
|
readonly=True
|
|
)
|
|
|
|
@api.depends('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant')
|
|
def _compute_name(self):
|
|
for record in self:
|
|
if not record.parameter_id:
|
|
record.name = 'Nuevo rango'
|
|
continue
|
|
|
|
parts = [record.parameter_id.name]
|
|
|
|
# Agregar género si no es ambos
|
|
if record.gender != 'both':
|
|
gender_name = dict(self._fields['gender'].selection).get(record.gender, '')
|
|
parts.append(gender_name)
|
|
|
|
# Agregar rango de edad
|
|
if record.age_min == 0 and record.age_max == 150:
|
|
parts.append('Todas las edades')
|
|
else:
|
|
parts.append(f"{record.age_min}-{record.age_max} años")
|
|
|
|
# Agregar indicador de embarazo
|
|
if record.pregnant:
|
|
parts.append('Embarazada')
|
|
|
|
record.name = ' - '.join(parts)
|
|
|
|
@api.constrains('age_min', 'age_max')
|
|
def _check_age_range(self):
|
|
for record in self:
|
|
if record.age_min < 0:
|
|
raise ValidationError('La edad mínima no puede ser negativa.')
|
|
if record.age_max < record.age_min:
|
|
raise ValidationError('La edad máxima debe ser mayor o igual a la edad mínima.')
|
|
if record.age_max > 150:
|
|
raise ValidationError('La edad máxima no puede ser mayor a 150 años.')
|
|
|
|
@api.constrains('normal_min', 'normal_max')
|
|
def _check_normal_range(self):
|
|
for record in self:
|
|
if record.normal_min and record.normal_max and record.normal_min > record.normal_max:
|
|
raise ValidationError('El valor normal mínimo debe ser menor o igual al valor normal máximo.')
|
|
|
|
@api.constrains('critical_min', 'critical_max', 'normal_min', 'normal_max')
|
|
def _check_critical_range(self):
|
|
for record in self:
|
|
# Validar que crítico mínimo sea menor que normal mínimo
|
|
if record.critical_min and record.normal_min and record.critical_min > record.normal_min:
|
|
raise ValidationError('El valor crítico mínimo debe ser menor o igual al valor normal mínimo.')
|
|
|
|
# Validar que crítico máximo sea mayor que normal máximo
|
|
if record.critical_max and record.normal_max and record.critical_max < record.normal_max:
|
|
raise ValidationError('El valor crítico máximo debe ser mayor o igual al valor normal máximo.')
|
|
|
|
@api.constrains('gender', 'pregnant')
|
|
def _check_pregnant_gender(self):
|
|
for record in self:
|
|
if record.pregnant and record.gender == 'male':
|
|
raise ValidationError('No se puede marcar "Embarazada" para rangos masculinos.')
|
|
|
|
@api.constrains('parameter_id', 'gender', 'age_min', 'age_max', 'pregnant')
|
|
def _check_unique_range(self):
|
|
for record in self:
|
|
# Buscar rangos duplicados
|
|
domain = [
|
|
('parameter_id', '=', record.parameter_id.id),
|
|
('gender', '=', record.gender),
|
|
('age_min', '=', record.age_min),
|
|
('age_max', '=', record.age_max),
|
|
('pregnant', '=', record.pregnant),
|
|
('id', '!=', record.id)
|
|
]
|
|
|
|
if self.search_count(domain) > 0:
|
|
raise ValidationError('Ya existe un rango con estas mismas condiciones para este parámetro.')
|
|
|
|
def is_value_normal(self, value):
|
|
"""Verifica si un valor está dentro del rango normal"""
|
|
self.ensure_one()
|
|
if not value or not self.normal_min or not self.normal_max:
|
|
return True
|
|
return self.normal_min <= value <= self.normal_max
|
|
|
|
def is_value_critical(self, value):
|
|
"""Verifica si un valor está en rango crítico"""
|
|
self.ensure_one()
|
|
if not value:
|
|
return False
|
|
|
|
# Crítico por debajo
|
|
if self.critical_min and value < self.critical_min:
|
|
return True
|
|
|
|
# Crítico por encima
|
|
if self.critical_max and value > self.critical_max:
|
|
return True
|
|
|
|
return False
|
|
|
|
def get_value_status(self, value):
|
|
"""Devuelve el estado del valor: 'normal', 'abnormal', 'critical'"""
|
|
self.ensure_one()
|
|
if not value:
|
|
return 'normal'
|
|
|
|
if self.is_value_critical(value):
|
|
return 'critical'
|
|
elif not self.is_value_normal(value):
|
|
return 'abnormal'
|
|
else:
|
|
return 'normal' |