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 # Prepare result data with values result_data = { 'test_id': test.id, 'parameter_id': param.id, } # Set value based on parameter type try: 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_data['value_numeric'] = value else: result_data['value_numeric'] = 50.0 elif param.value_type == 'text': # Handle different text parameters appropriately param_lower = param.name.lower() if 'cultivo' in param_lower: result_data['value_text'] = "No se observa crecimiento bacteriano" elif 'observacion' in param_lower: result_data['value_text'] = "Sin observaciones particulares" elif 'color' in param_lower: result_data['value_text'] = "Amarillo claro" elif 'aspecto' in param_lower: result_data['value_text'] = "Transparente" elif 'olor' in param_lower: result_data['value_text'] = "Sui generis" else: result_data['value_text'] = "Normal" elif param.value_type == 'boolean': result_data['value_boolean'] = False elif param.value_type == 'selection': if param.selection_values: options = param.selection_values.split(',') # For pregnancy tests, randomly assign positive/negative if 'beta-hcg' in param.name.lower() or 'embarazo' in param.name.lower(): # 30% chance of positive for pregnant patients, 5% for others is_positive = random.random() < (0.3 if hasattr(test, 'patient_id') and test.patient_id.is_pregnant else 0.05) result_data['value_selection'] = 'Positivo' if is_positive else 'Negativo' else: result_data['value_selection'] = options[0].strip() else: # No selection values defined, use default if 'embarazo' in param.name.lower(): result_data['value_selection'] = 'Negativo' else: result_data['value_selection'] = 'Normal' except Exception as e: print(f" - Error preparing value for parameter {param.name}: {str(e)}") # Set a default value to avoid validation errors if param.value_type == 'numeric': result_data['value_numeric'] = 50.0 elif param.value_type == 'text': result_data['value_text'] = "Pendiente" elif param.value_type == 'boolean': result_data['value_boolean'] = False elif param.value_type == 'selection': result_data['value_selection'] = "Normal" # Create result with values result = env['lims.result'].create(result_data) 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 con contexto especial result.with_context(skip_value_validation=True).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 # Get all available analysis products all_analyses = env['product.template'].search([('is_analysis', '=', True)]) # Find or create pregnancy test pregnancy_test = env['product.template'].search([('name', '=', 'Prueba de Embarazo'), ('is_analysis', '=', True)], limit=1) if not pregnancy_test: # Create pregnancy test if it doesn't exist pregnancy_test = env['product.template'].create({ 'name': 'Prueba de Embarazo', 'is_analysis': True, 'analysis_type': 'immunology', 'list_price': 15.00, 'standard_price': 8.00, 'type': 'service', 'categ_id': env.ref('lims_management.lims_category_immunology').id if env.ref('lims_management.lims_category_immunology', False) else env['product.category'].search([], limit=1).id, }) print("Created Pregnancy Test product") # Create parameter for pregnancy test preg_param = env['lims.analysis.parameter'].search([('name', '=', 'Beta-hCG')], limit=1) if not preg_param: preg_param = env['lims.analysis.parameter'].create({ 'name': 'Beta-hCG', 'code': 'BHCG', 'value_type': 'selection', 'selection_values': 'Positivo,Negativo,Indeterminado', 'description': 'Hormona gonadotropina coriónica humana beta' }) # Link parameter to pregnancy test env['product.template.parameter'].create({ 'product_tmpl_id': pregnancy_test.id, 'parameter_id': preg_param.id, 'sequence': 10, 'required': True }) # Separate analyses for different purposes routine_analyses = [a for a in all_analyses if a.name not in ['Prueba de Embarazo']] # Get all patients all_patients = env['res.partner'].search([('is_patient', '=', True)], order='id') # Get available doctors doctors = env['res.partner'].search([('is_doctor', '=', True)]) print(f"\n=== Starting creation of lab orders for {len(all_patients)} patients ===") print(f"Available analyses: {len(all_analyses)}") print(f"Available doctors: {len(doctors)}") orders_created = 0 failed_orders = [] for idx, patient in enumerate(all_patients): print(f"\n--- Processing patient {idx+1}/{len(all_patients)}: {patient.name} ---") # Randomly assign a doctor doctor = random.choice(doctors) if doctors else False # Create 2 orders per patient for order_num in range(1, 3): try: order_lines = [] # For pregnant patients, include pregnancy test in one of the orders if patient.is_pregnant and order_num == 1: order_lines.append((0, 0, { 'product_id': pregnancy_test.product_variant_id.id, 'product_uom_qty': 1 })) print(f" - Added pregnancy test for pregnant patient") # Select random analyses (minimum 2 per order) num_analyses = random.randint(2, 4) selected_analyses = random.sample(routine_analyses, min(num_analyses, len(routine_analyses))) for analysis in selected_analyses: order_lines.append((0, 0, { 'product_id': analysis.product_variant_id.id, 'product_uom_qty': 1 })) # Create the order order = env['sale.order'].create({ 'partner_id': patient.id, 'doctor_id': doctor.id if doctor else False, 'is_lab_request': True, 'order_line': order_lines }) print(f" Order {order_num}: Created {order.name} with {len(order_lines)} analyses") # Confirm the order order.action_confirm() print(f" Order {order_num}: Confirmed. Generated samples: {len(order.generated_sample_ids)}") # Process tests process_order_tests(env, order) orders_created += 1 except Exception as e: error_msg = f"Patient: {patient.name}, Order {order_num}, Error: {str(e)}" failed_orders.append(error_msg) print(f" ERROR creating order {order_num} for {patient.name}: {str(e)}") import traceback traceback.print_exc() # Final summary print("\n=== SUMMARY ===") print(f"Total orders created: {orders_created}") print(f"Failed orders: {len(failed_orders)}") if failed_orders: print("\n=== FAILED ORDERS ===") for idx, error in enumerate(failed_orders, 1): print(f"{idx}. {error}") if __name__ == '__main__': db_name = 'lims_demo' registry = odoo.registry(db_name) with registry.cursor() as cr: create_lab_requests(cr) cr.commit()