# -*- 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()