clinical_laboratory/test/create_lab_requests.py

277 lines
14 KiB
Python

import odoo
import json
import random
from datetime import datetime
# Diccionario de notas médicas para parámetros críticos
CRITICAL_NOTES = {
'glucosa': {
'high': 'Valor elevado de glucosa. Posible prediabetes o diabetes. Se recomienda repetir la prueba en ayunas y consultar con endocrinología.',
'low': 'Hipoglucemia detectada. Riesgo de síntomas neuroglucogénicos. Evaluar causas: medicamentos, insuficiencia hepática o endocrinopatías.'
},
'hemoglobina': {
'high': 'Policitemia. Evaluar posibles causas: deshidratación, tabaquismo, cardiopatía o policitemia vera.',
'low': 'Anemia severa. Investigar origen: deficiencia de hierro, pérdida sanguínea, hemólisis o enfermedad crónica.'
},
'hematocrito': {
'high': 'Hemoconcentración. Correlacionar con hemoglobina. Descartar deshidratación o policitemia.',
'low': 'Valor compatible con anemia. Evaluar junto con hemoglobina e índices eritrocitarios.'
},
'leucocitos': {
'high': 'Leucocitosis marcada. Descartar proceso infeccioso, inflamatorio o hematológico.',
'low': 'Leucopenia severa. Riesgo de infecciones. Evaluar causas: viral, medicamentosa o hematológica.'
},
'plaquetas': {
'high': 'Trombocitosis. Riesgo trombótico. Descartar causa primaria vs reactiva.',
'low': 'Trombocitopenia severa. Riesgo de sangrado. Evaluar PTI, hiperesplenismo o supresión medular.'
},
'neutrofilos': {
'high': 'Neutrofilia. Sugiere infección bacteriana o proceso inflamatorio agudo.',
'low': 'Neutropenia. Alto riesgo de infección bacteriana. Evaluar urgentemente.'
},
'linfocitos': {
'high': 'Linfocitosis. Considerar infección viral o proceso linfoproliferativo.',
'low': 'Linfopenia. Evaluar inmunodeficiencia o efecto de corticoides.'
},
'colesterol total': {
'high': 'Hipercolesterolemia. Riesgo cardiovascular elevado. Iniciar medidas dietéticas y evaluar tratamiento con estatinas.',
'low': 'Hipocolesterolemia. Evaluar malnutrición, hipertiroidismo o enfermedad hepática.'
},
'trigliceridos': {
'high': 'Hipertrigliceridemia severa. Riesgo de pancreatitis aguda. Considerar tratamiento farmacológico urgente.',
'low': 'Valor bajo, generalmente sin significado patológico.'
},
'hdl': {
'high': 'HDL elevado, factor protector cardiovascular.',
'low': 'HDL bajo. Factor de riesgo cardiovascular. Recomendar ejercicio y cambios en estilo de vida.'
},
'ldl': {
'high': 'LDL elevado. Alto riesgo aterogénico. Evaluar inicio de estatinas según riesgo global.',
'low': 'LDL bajo, generalmente favorable.'
},
'glucosa en sangre': {
'high': 'Hiperglucemia. Si en ayunas >126 mg/dL sugiere diabetes. Confirmar con segunda muestra.',
'low': 'Hipoglucemia. Evaluar síntomas y causas. Riesgo neurológico si <50 mg/dL.'
}
}
def get_critical_note(param_name, value, normal_min=None, normal_max=None):
"""Obtiene la nota apropiada para un resultado crítico"""
param_lower = param_name.lower()
# Buscar el parámetro en el diccionario
for key in CRITICAL_NOTES:
if key in param_lower:
if normal_max and value > normal_max:
return CRITICAL_NOTES[key].get('high', f'Valor crítico alto para {param_name}. Requiere evaluación médica inmediata.')
elif normal_min and value < normal_min:
return CRITICAL_NOTES[key].get('low', f'Valor crítico bajo para {param_name}. Requiere evaluación médica inmediata.')
# Nota genérica si no se encuentra el parámetro
if normal_max and value > normal_max:
return f'Valor significativamente elevado. Rango normal: {normal_min}-{normal_max}. Se recomienda evaluación médica.'
elif normal_min and value < normal_min:
return f'Valor significativamente bajo. Rango normal: {normal_min}-{normal_max}. Se recomienda evaluación médica.'
return 'Valor fuera de rango normal. Requiere interpretación clínica.'
def process_order_tests(env, order):
"""Process all tests for a given order: regenerate results, fill values, and validate"""
print(f"\nProcessing tests for order {order.name}...")
# First, update sample states to allow processing
samples = order.generated_sample_ids.sudo()
for sample in samples:
if sample.state == 'pending_collection':
sample.action_collect()
print(f" - Sample {sample.name} collected")
if sample.state == 'collected':
sample.action_receive()
print(f" - Sample {sample.name} received")
if sample.state == 'received':
sample.action_start_analysis()
print(f" - Sample {sample.name} analysis started")
# Find all tests associated with this order
tests = env['lims.test'].search([('sale_order_id', '=', order.id)])
print(f"Found {len(tests)} tests for order {order.name}")
# Ensure we have the right permissions by using sudo()
tests = tests.sudo()
for test in tests:
try:
print(f"\nProcessing test {test.name} - {test.product_id.name}")
# First, mark the test as in_process if it's in draft state
if test.state == 'draft':
test.write({'state': 'in_process'})
# Manually create results if they don't exist
if not test.result_ids:
# Get analysis parameters from product template
product_tmpl = test.product_id.product_tmpl_id
for param_link in product_tmpl.parameter_ids:
param = param_link.parameter_id
result = env['lims.result'].create({
'test_id': test.id,
'parameter_id': param.id,
})
# Immediately set a value to avoid validation errors
if param.value_type == 'numeric':
# Generar valor que a veces esté fuera de rango
if random.random() < 0.3: # 30% de valores críticos
# Obtener rangos normales del parámetro
normal_min = param_link.normal_min if hasattr(param_link, 'normal_min') and param_link.normal_min else 10
normal_max = param_link.normal_max if hasattr(param_link, 'normal_max') and param_link.normal_max else 100
# Decidir si será alto o bajo
if random.random() < 0.5:
# Valor alto
value = round(random.uniform(normal_max * 1.2, normal_max * 1.5), 2)
else:
# Valor bajo
value = round(random.uniform(normal_min * 0.5, normal_min * 0.8), 2)
result.value_numeric = value
else:
result.value_numeric = 50.0
elif param.value_type == 'text':
result.value_text = "Normal"
elif param.value_type == 'boolean':
result.value_boolean = False
elif param.value_type == 'selection' and param.selection_options:
options = param.selection_options.split(',')
result.value_selection = options[0].strip()
print(f" - Created {len(product_tmpl.parameter_ids)} result fields")
# Evaluar resultados críticos y agregar notas
for result in test.result_ids:
# Leer el registro para actualizar campos computados
result.read(['is_critical'])
# Si el resultado es crítico, agregar nota
if result.is_critical and result.parameter_id.value_type == 'numeric':
value = result.value_numeric
param_name = result.parameter_id.name
# Obtener rangos del rango aplicable si existe
normal_min = normal_max = None
if result.applicable_range_id:
normal_min = result.applicable_range_id.normal_min
normal_max = result.applicable_range_id.normal_max
# Obtener la nota apropiada
note = get_critical_note(param_name, value, normal_min, normal_max)
result.write({'notes': note})
print(f" - Agregada nota crítica para {param_name}: valor {value}")
print(f" - Results ready with values and critical notes")
# Update test state directly to bypass permission checks
if test.state == 'in_process':
# Mark as results entered
test.write({
'state': 'result_entered'
})
print(f" - Results entered")
# Then validate the test
if test.state == 'result_entered':
test.write({
'state': 'validated',
'validator_id': env.user.id,
'validation_date': datetime.now()
})
print(f" - Test validated successfully")
except Exception as e:
print(f" - Error processing test {test.name}: {str(e)}")
import traceback
traceback.print_exc()
print(f" - Test state: {test.state}")
print(f" - Product template: {test.product_id.product_tmpl_id.name}")
print(f" - Parameters: {len(test.product_id.product_tmpl_id.parameter_ids)}")
print(f"\nCompleted processing tests for order {order.name}")
def create_lab_requests(cr):
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
# Delete unwanted demo sale orders
unwanted_orders = env['sale.order'].search([('name', 'in', ['S00001', 'S00002', 'S00003', 'S00004', 'S00005', 'S00006', 'S00007', 'S00008', 'S00009', 'S00010', 'S00011', 'S00012', 'S00013', 'S00014', 'S00015', 'S00016', 'S00017', 'S00018', 'S00019', 'S00020', 'S00021', 'S00022'])])
for order in unwanted_orders:
try:
order.action_cancel()
except Exception:
pass
try:
unwanted_orders.unlink()
except Exception:
pass
try:
# Get patients and doctors - using search instead of ref to be more robust
patient1 = env['res.partner'].search([('patient_identifier', '=', 'P-A87B01'), ('is_patient', '=', True)], limit=1)
patient2 = env['res.partner'].search([('patient_identifier', '=', 'P-C45D02'), ('is_patient', '=', True)], limit=1)
doctor1 = env['res.partner'].search([('doctor_license', '=', 'L-98765'), ('is_doctor', '=', True)], limit=1)
if not patient1:
print("Warning: Patient 1 not found, skipping lab requests creation")
return
# Get analysis products - using search instead of ref
hemograma = env['product.template'].search([('name', '=', 'Hemograma Completo'), ('is_analysis', '=', True)], limit=1)
perfil_lipidico = env['product.template'].search([('name', '=', 'Perfil Lipídico'), ('is_analysis', '=', True)], limit=1)
glucosa = env['product.template'].search([('name', '=', 'Glucosa en Sangre'), ('is_analysis', '=', True)], limit=1)
urocultivo = env['product.template'].search([('name', '=', 'Urocultivo'), ('is_analysis', '=', True)], limit=1)
# Create Lab Request 1 - Multiple analyses with same sample type
if patient1 and hemograma and perfil_lipidico:
order1 = env['sale.order'].create({
'partner_id': patient1.id,
'doctor_id': doctor1.id if doctor1 else False,
'is_lab_request': True,
'order_line': [
(0, 0, {'product_id': hemograma.product_variant_id.id, 'product_uom_qty': 1}),
(0, 0, {'product_id': perfil_lipidico.product_variant_id.id, 'product_uom_qty': 1})
]
})
print(f"Created Lab Order 1: {order1.name}")
# Confirm the order to test automatic sample generation
order1.action_confirm()
print(f"Confirmed Lab Order 1. Generated samples: {len(order1.generated_sample_ids)}")
# Process tests for order1
process_order_tests(env, order1)
# Create Lab Request 2 - Different sample types
if patient2 and glucosa and urocultivo:
order2 = env['sale.order'].create({
'partner_id': patient2.id,
'is_lab_request': True,
'order_line': [
(0, 0, {'product_id': glucosa.product_variant_id.id, 'product_uom_qty': 1}),
(0, 0, {'product_id': urocultivo.product_variant_id.id, 'product_uom_qty': 1})
]
})
print(f"Created Lab Order 2: {order2.name}")
# Confirm to test automatic sample generation with different types
order2.action_confirm()
print(f"Confirmed Lab Order 2. Generated samples: {len(order2.generated_sample_ids)}")
# Process tests for order2
process_order_tests(env, order2)
except Exception as e:
print(f"Error creating lab requests: {str(e)}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
create_lab_requests(cr)
cr.commit()