clinical_laboratory/test/create_demo_data.py
Luis Ernesto Portillo Zaldivar 02237c6d8c fix(#71): Corregir errores en dashboards y scripts de inicialización
- Cambiar 'tree' por 'list' en view_mode de todas las acciones de dashboard
- Corregir sintaxis de filtros de fecha usando context_today() y relativedelta
- Eliminar campo booleano is_out_of_range como medida en gráfico
- Corregir referencia a sample.state en lugar de sample.sample_state
- Reemplazar sample.test_ids por búsqueda de tests asociados
- Eliminar consulta SQL directa a columna logo inexistente
- Corregir método invalidate_cache() por _invalidate_cache()
- Agregar sección de notificaciones en CLAUDE.md

Los dashboards ahora funcionan correctamente sin errores de JavaScript.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-18 12:11:01 -06:00

416 lines
18 KiB
Python

# -*- coding: utf-8 -*-
"""
Script para crear datos de demostración completos para el módulo LIMS.
Incluye órdenes de laboratorio, muestras, pruebas y resultados.
"""
import odoo
from datetime import datetime, timedelta
import random
import logging
_logger = logging.getLogger(__name__)
def create_demo_lab_data(cr):
"""Crea datos completos de demostración para laboratorio"""
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
print("\n=== INICIANDO CREACIÓN DE DATOS DE DEMOSTRACIÓN ===")
# Verificar que los parámetros y rangos se cargaron correctamente
param_count = env['lims.analysis.parameter'].search_count([])
range_count = env['lims.parameter.range'].search_count([])
print(f"Parámetros encontrados: {param_count}")
print(f"Rangos de referencia encontrados: {range_count}")
if param_count == 0 or range_count == 0:
print("⚠️ No se encontraron parámetros o rangos. Asegúrese de que los datos XML se cargaron.")
return
# Obtener pacientes de demostración
patients = []
patient_refs = [
'lims_management.demo_patient_1',
'lims_management.demo_patient_2',
'lims_management.demo_patient_3',
'lims_management.demo_patient_4'
]
for ref in patient_refs:
patient = env.ref(ref, raise_if_not_found=False)
if patient:
patients.append(patient)
if not patients:
print("⚠️ No se encontraron pacientes de demostración")
return
print(f"Pacientes encontrados: {len(patients)}")
# Obtener doctores
doctors = []
doctor_refs = ['lims_management.demo_doctor_1', 'lims_management.demo_doctor_2']
for ref in doctor_refs:
doctor = env.ref(ref, raise_if_not_found=False)
if doctor:
doctors.append(doctor)
if not doctors:
# Crear un doctor de demo si no existe
doctors = [env['res.partner'].create({
'name': 'Dr. Demo',
'is_doctor': True
})]
# Obtener análisis disponibles
analyses = []
analysis_refs = [
'lims_management.analysis_hemograma',
'lims_management.analysis_perfil_lipidico',
'lims_management.analysis_glucosa',
'lims_management.analysis_quimica_sanguinea',
'lims_management.analysis_urianalisis',
'lims_management.analysis_serologia',
'lims_management.analysis_urocultivo',
'lims_management.analysis_tp',
'lims_management.analysis_prueba_embarazo'
]
for ref in analysis_refs:
analysis = env.ref(ref, raise_if_not_found=False)
if analysis:
analyses.append(analysis)
print(f"Análisis encontrados: {len(analyses)}")
if not analyses:
print("⚠️ No se encontraron análisis de demostración")
return
# Crear órdenes de laboratorio
orders_created = []
# Orden 1: Chequeo general para paciente adulto masculino
if len(patients) > 0 and len(analyses) >= 4:
order1 = env['sale.order'].create({
'partner_id': patients[0].id,
'doctor_id': doctors[0].id if doctors else False,
'is_lab_request': True,
# 'lab_request_priority': 'normal', # Campo no existe aún
'note': 'Chequeo general anual - Control de salud preventivo',
'order_line': [
(0, 0, {
'product_id': analyses[0].product_variant_id.id, # Hemograma
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[1].product_variant_id.id, # Perfil Lipídico
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[2].product_variant_id.id, # Glucosa
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[3].product_variant_id.id, # Química Sanguínea
'product_uom_qty': 1
})
]
})
order1.action_confirm()
orders_created.append(order1)
print(f"✓ Orden {order1.name} creada para {order1.partner_id.name}")
# Orden 2: Control prenatal para paciente embarazada
if len(patients) > 2 and len(analyses) >= 5:
# Usar María González (índice 2) que es femenina
patients[2].is_pregnant = True
order2 = env['sale.order'].create({
'partner_id': patients[2].id,
'doctor_id': doctors[-1].id if doctors else False,
'is_lab_request': True,
# 'lab_request_priority': 'high', # Campo no existe aún
'note': 'Control prenatal - 20 semanas de gestación',
'order_line': [
(0, 0, {
'product_id': analyses[0].product_variant_id.id, # Hemograma
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[2].product_variant_id.id, # Glucosa
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[4].product_variant_id.id if len(analyses) > 4 else analyses[0].product_variant_id.id, # Urianálisis
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[5].product_variant_id.id if len(analyses) > 5 else analyses[1].product_variant_id.id, # Serología
'product_uom_qty': 1
})
]
})
order2.action_confirm()
orders_created.append(order2)
print(f"✓ Orden {order2.name} creada para {order2.partner_id.name} (embarazada)")
# Orden 3: Urgencia - Sospecha de infección
if len(patients) > 1 and len(analyses) >= 3:
order3 = env['sale.order'].create({
'partner_id': patients[1].id,
'doctor_id': doctors[0].id if doctors else False,
'is_lab_request': True,
# 'lab_request_priority': 'urgent', # Campo no existe aún
'note': 'Urgencia - Fiebre de 39°C, dolor lumbar, sospecha de infección urinaria',
'order_line': [
(0, 0, {
'product_id': analyses[4].product_variant_id.id if len(analyses) > 4 else analyses[0].product_variant_id.id, # Urianálisis
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[6].product_variant_id.id if len(analyses) > 6 else analyses[1].product_variant_id.id, # Urocultivo
'product_uom_qty': 1
}),
(0, 0, {
'product_id': analyses[0].product_variant_id.id, # Hemograma (para ver leucocitos)
'product_uom_qty': 1
})
]
})
order3.action_confirm()
orders_created.append(order3)
print(f"✓ Orden urgente {order3.name} creada para {order3.partner_id.name}")
# Orden 4: Control pediátrico
if len(patients) > 3:
order4 = env['sale.order'].create({
'partner_id': patients[3].id,
'doctor_id': doctors[-1].id if doctors else False,
'is_lab_request': True,
# 'lab_request_priority': 'normal', # Campo no existe aún
'note': 'Control pediátrico - Evaluación de anemia, niña con palidez',
'order_line': [
(0, 0, {
'product_id': analyses[0].product_variant_id.id, # Hemograma completo
'product_uom_qty': 1
})
]
})
order4.action_confirm()
orders_created.append(order4)
print(f"✓ Orden pediátrica {order4.name} creada para {order4.partner_id.name}")
print(f"\n📋 Total de órdenes creadas: {len(orders_created)}")
# Procesar muestras y generar resultados
for idx, order in enumerate(orders_created):
print(f"\n--- Procesando orden {idx + 1}/{len(orders_created)}: {order.name} ---")
# Generar muestras si no existen
if not order.generated_sample_ids:
order.action_generate_samples()
print(f" ✓ Muestras generadas: {len(order.generated_sample_ids)}")
# Procesar cada muestra
for sample in order.generated_sample_ids:
# Marcar como recolectada
if sample.state == 'pending_collection':
sample.action_collect()
print(f" ✓ Muestra {sample.name} recolectada")
# Procesar pruebas de esta muestra
tests = env['lims.test'].search([('sample_id', '=', sample.id)])
for test in tests:
print(f" - Procesando prueba: {test.product_id.name}")
# Iniciar proceso si está en borrador
if test.state == 'draft':
test.action_start_process()
# La generación automática de resultados ya debería haberse ejecutado
if test.result_ids:
print(f" ✓ Resultados generados automáticamente: {len(test.result_ids)}")
# Simular ingreso de valores en los resultados
simulate_test_results(env, test)
# Marcar como resultados ingresados
if test.state == 'in_process':
test.action_enter_results()
print(f" ✓ Resultados ingresados")
# Validar las primeras 2 órdenes completas y algunas pruebas de la tercera
should_validate = (idx < 2) or (idx == 2 and test == tests[0])
if should_validate and test.state == 'result_entered':
test.action_validate()
print(f" ✓ Prueba validada")
else:
print(f" ⚠️ No se generaron resultados automáticamente")
# Resumen final
print("\n" + "="*60)
print("RESUMEN DE DATOS CREADOS")
print("="*60)
# Contar registros creados
total_samples = env['stock.lot'].search_count([('is_lab_sample', '=', True)])
total_tests = env['lims.test'].search_count([])
total_results = env['lims.result'].search_count([])
tests_by_state = {}
for state in ['draft', 'in_process', 'result_entered', 'validated', 'cancelled']:
count = env['lims.test'].search_count([('state', '=', state)])
if count > 0:
tests_by_state[state] = count
print(f"\n📊 Estadísticas:")
print(f" - Órdenes de laboratorio: {len(orders_created)}")
print(f" - Muestras totales: {total_samples}")
print(f" - Pruebas totales: {total_tests}")
print(f" - Resultados totales: {total_results}")
print(f"\n📈 Pruebas por estado:")
for state, count in tests_by_state.items():
print(f" - {state}: {count}")
# Verificar algunos resultados fuera de rango
out_of_range = env['lims.result'].search_count([('is_out_of_range', '=', True)])
critical = env['lims.result'].search_count([('is_critical', '=', True)])
if out_of_range or critical:
print(f"\n⚠️ Valores anormales:")
print(f" - Fuera de rango: {out_of_range}")
print(f" - Críticos: {critical}")
print("\n✅ Datos de demostración creados exitosamente")
def simulate_test_results(env, test):
"""Simular el ingreso de resultados realistas para una prueba"""
for result in test.result_ids:
param = result.parameter_id
if param.value_type == 'numeric':
# Generar valor numérico considerando el rango normal
if result.applicable_range_id:
range_obj = result.applicable_range_id
# Probabilidades: 75% normal, 20% anormal, 5% crítico
rand = random.random()
if rand < 0.75: # Valor normal
# Generar valor dentro del rango normal
value = random.uniform(range_obj.normal_min, range_obj.normal_max)
elif rand < 0.95: # Valor anormal pero no crítico
# Decidir si va por arriba o por abajo
if random.random() < 0.5 and range_obj.normal_min > 0:
# Por debajo del normal
value = random.uniform(range_obj.normal_min * 0.7, range_obj.normal_min * 0.95)
else:
# Por encima del normal
value = random.uniform(range_obj.normal_max * 1.05, range_obj.normal_max * 1.3)
else: # Valor crítico (5%)
if range_obj.critical_min and random.random() < 0.5:
# Crítico bajo
value = random.uniform(range_obj.critical_min * 0.5, range_obj.critical_min * 0.9)
elif range_obj.critical_max:
# Crítico alto
value = random.uniform(range_obj.critical_max * 1.1, range_obj.critical_max * 1.5)
else:
# Si no hay valores críticos definidos, usar un valor muy anormal
value = range_obj.normal_max * 2.0
# Redondear según el tipo de parámetro
if param.code in ['HGB', 'CREA', 'GLU', 'CHOL', 'HDL', 'LDL', 'TRIG']:
result.value_numeric = round(value, 1)
elif param.code in ['U-PH', 'U-DENS']:
result.value_numeric = round(value, 3)
else:
result.value_numeric = round(value, 2)
# Agregar notas para valores anormales en algunos casos
if result.is_out_of_range and random.random() < 0.3:
if param.code == 'GLU' and result.value_numeric > 126:
result.notes = "Hiperglucemia - Sugerir control de diabetes"
elif param.code == 'WBC' and result.value_numeric > 11:
result.notes = "Leucocitosis - Posible proceso infeccioso"
elif param.code == 'HGB' and result.value_numeric < range_obj.normal_min:
result.notes = "Anemia - Evaluar causa"
else:
# Sin rango definido, usar valores típicos
result.value_numeric = round(random.uniform(10, 100), 2)
elif param.value_type == 'selection':
# Seleccionar una opción con pesos realistas
if param.selection_values:
options = [opt.strip() for opt in param.selection_values.split(',')]
# Para cultivos, 70% negativo, 30% positivo
if param.code in ['CULT', 'HIV', 'HBsAg', 'HCV', 'VDRL']:
if 'Negativo' in options or 'No Reactivo' in options:
negative_option = 'Negativo' if 'Negativo' in options else 'No Reactivo'
positive_option = 'Positivo' if 'Positivo' in options else 'Reactivo'
result.value_selection = negative_option if random.random() < 0.7 else positive_option
else:
result.value_selection = random.choice(options)
# Para orina, distribución más realista
elif param.code == 'U-COLOR':
weights = [0.1, 0.6, 0.2, 0.05, 0.02, 0.02, 0.01] # Amarillo más común
result.value_selection = random.choices(options, weights=weights[:len(options)])[0]
elif param.code == 'U-ASP':
weights = [0.7, 0.2, 0.08, 0.02] # Transparente más común
result.value_selection = random.choices(options, weights=weights[:len(options)])[0]
else:
# Primera opción más probable (generalmente es la normal)
weights = [0.7] + [0.3/(len(options)-1)]*(len(options)-1)
result.value_selection = random.choices(options, weights=weights)[0]
elif param.value_type == 'boolean':
# Para pruebas de embarazo, considerar el género del paciente
if param.code == 'HCG' and test.patient_id.gender == 'female' and test.patient_id.is_pregnant:
result.value_boolean = True
else:
# 85% probabilidad de False (negativo) para la mayoría de pruebas
result.value_boolean = random.random() > 0.85
elif param.value_type == 'text':
# Generar texto según el parámetro
if param.code == 'MICRO':
# Solo si el cultivo es positivo
culture_result = test.result_ids.filtered(
lambda r: r.parameter_id.code == 'CULT'
)
if culture_result and culture_result.value_selection == 'Positivo':
organisms = ['E. coli', 'Klebsiella pneumoniae', 'Proteus mirabilis',
'Enterococcus faecalis', 'Staphylococcus aureus',
'Pseudomonas aeruginosa', 'Streptococcus agalactiae']
result.value_text = random.choice(organisms)
else:
result.value_text = "No se aisló microorganismo"
elif param.code == 'UFC':
# Solo si hay microorganismo
micro_result = test.result_ids.filtered(
lambda r: r.parameter_id.code == 'MICRO'
)
if micro_result and micro_result.value_text and micro_result.value_text != "No se aisló microorganismo":
counts = ['>100,000', '>50,000', '>10,000', '<10,000']
weights = [0.5, 0.3, 0.15, 0.05]
result.value_text = random.choices(counts, weights=weights)[0] + " UFC/mL"
if __name__ == '__main__':
db_name = 'lims_demo'
registry = odoo.registry(db_name)
with registry.cursor() as cr:
create_demo_lab_data(cr)
cr.commit()