feat: Implementar autocompletado de notas para resultados críticos en lims.result
This commit is contained in:
parent
53eada8432
commit
64f44b9d2c
|
@ -28,7 +28,8 @@
|
||||||
"Bash(git cherry-pick:*)",
|
"Bash(git cherry-pick:*)",
|
||||||
"Bash(del comment_issue_15.txt)",
|
"Bash(del comment_issue_15.txt)",
|
||||||
"Bash(cat:*)",
|
"Bash(cat:*)",
|
||||||
"Bash(powershell.exe:*)"
|
"Bash(powershell.exe:*)",
|
||||||
|
"Bash(gh pr create:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,6 +365,101 @@ class LimsResult(models.Model):
|
||||||
if len(matches) == 1:
|
if len(matches) == 1:
|
||||||
self.value_selection = matches[0]
|
self.value_selection = matches[0]
|
||||||
|
|
||||||
|
@api.onchange('value_numeric', 'is_critical')
|
||||||
|
def _onchange_critical_value(self):
|
||||||
|
"""Autocompleta las notas cuando el valor es crítico."""
|
||||||
|
if self.is_critical and self.parameter_value_type == 'numeric' and self.value_numeric:
|
||||||
|
# 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.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Solo autocompletar si no hay notas previas o están vacías
|
||||||
|
if not self.notes or self.notes.strip() == '':
|
||||||
|
note = self._get_critical_note(CRITICAL_NOTES)
|
||||||
|
if note:
|
||||||
|
self.notes = note
|
||||||
|
|
||||||
|
def _get_critical_note(self, critical_notes_dict):
|
||||||
|
"""Obtiene la nota apropiada para un resultado crítico."""
|
||||||
|
if not self.parameter_id or not self.parameter_name:
|
||||||
|
return False
|
||||||
|
|
||||||
|
param_lower = self.parameter_name.lower()
|
||||||
|
|
||||||
|
# Buscar el parámetro en el diccionario
|
||||||
|
for key in critical_notes_dict:
|
||||||
|
if key in param_lower:
|
||||||
|
# Obtener rangos del rango aplicable si existe
|
||||||
|
normal_min = normal_max = None
|
||||||
|
if self.applicable_range_id:
|
||||||
|
normal_min = self.applicable_range_id.normal_min
|
||||||
|
normal_max = self.applicable_range_id.normal_max
|
||||||
|
|
||||||
|
if normal_max and self.value_numeric > normal_max:
|
||||||
|
return critical_notes_dict[key].get('high', f'Valor crítico alto para {self.parameter_name}. Requiere evaluación médica inmediata.')
|
||||||
|
elif normal_min and self.value_numeric < normal_min:
|
||||||
|
return critical_notes_dict[key].get('low', f'Valor crítico bajo para {self.parameter_name}. Requiere evaluación médica inmediata.')
|
||||||
|
|
||||||
|
# Nota genérica si no se encuentra el parámetro
|
||||||
|
if self.applicable_range_id:
|
||||||
|
normal_min = self.applicable_range_id.normal_min
|
||||||
|
normal_max = self.applicable_range_id.normal_max
|
||||||
|
|
||||||
|
if normal_max and self.value_numeric > normal_max:
|
||||||
|
return f'Valor significativamente elevado. Rango normal: {normal_min}-{normal_max}. Se recomienda evaluación médica.'
|
||||||
|
elif normal_min and self.value_numeric < 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 _validate_and_autocomplete_selection(self, value):
|
def _validate_and_autocomplete_selection(self, value):
|
||||||
"""Valida y autocompleta el valor de selección.
|
"""Valida y autocompleta el valor de selección.
|
||||||
|
|
||||||
|
|
85
test/test_critical_notes_autocomplete.py
Normal file
85
test/test_critical_notes_autocomplete.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import odoo
|
||||||
|
import json
|
||||||
|
|
||||||
|
def test_critical_notes_autocomplete(cr):
|
||||||
|
"""Prueba el autocompletado de notas críticas en resultados de laboratorio"""
|
||||||
|
env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {})
|
||||||
|
|
||||||
|
print("\n=== PRUEBA DE AUTOCOMPLETADO DE NOTAS CRÍTICAS ===\n")
|
||||||
|
|
||||||
|
# Buscar algunas pruebas con resultados
|
||||||
|
tests = env['lims.test'].search([('state', 'in', ['result_entered', 'validated'])], limit=5)
|
||||||
|
|
||||||
|
if not tests:
|
||||||
|
print("No se encontraron pruebas con resultados para probar.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for test in tests:
|
||||||
|
print(f"\nPrueba: {test.name} - {test.product_id.name}")
|
||||||
|
print(f"Paciente: {test.patient_id.name}")
|
||||||
|
|
||||||
|
for result in test.result_ids:
|
||||||
|
if result.parameter_value_type == 'numeric':
|
||||||
|
print(f"\n Parámetro: {result.parameter_name}")
|
||||||
|
print(f" Valor: {result.value_numeric} {result.parameter_unit or ''}")
|
||||||
|
print(f" ¿Es crítico?: {'SÍ' if result.is_critical else 'NO'}")
|
||||||
|
|
||||||
|
if result.is_critical:
|
||||||
|
# Limpiar las notas para probar el autocompletado
|
||||||
|
result.notes = ''
|
||||||
|
|
||||||
|
# Simular cambio en el valor para activar el onchange
|
||||||
|
with env.cr.savepoint():
|
||||||
|
# Trigger the onchange by updating the value
|
||||||
|
result.with_context(force_onchange=True)._onchange_critical_value()
|
||||||
|
|
||||||
|
print(f" Nota autocompletada: {result.notes}")
|
||||||
|
|
||||||
|
# No guardar los cambios, solo mostrar
|
||||||
|
env.cr.rollback()
|
||||||
|
|
||||||
|
# Probar con valores específicos
|
||||||
|
print("\n\n=== PRUEBA CON VALORES ESPECÍFICOS ===\n")
|
||||||
|
|
||||||
|
# Buscar parámetros específicos
|
||||||
|
test_params = [
|
||||||
|
('Glucosa', 200.0, 'high'),
|
||||||
|
('Glucosa', 50.0, 'low'),
|
||||||
|
('Hemoglobina', 20.0, 'high'),
|
||||||
|
('Hemoglobina', 7.0, 'low'),
|
||||||
|
('Plaquetas', 600000, 'high'),
|
||||||
|
('Plaquetas', 50000, 'low')
|
||||||
|
]
|
||||||
|
|
||||||
|
for param_name, test_value, expected_type in test_params:
|
||||||
|
# Buscar un resultado con este parámetro
|
||||||
|
result = env['lims.result'].search([
|
||||||
|
('parameter_name', 'ilike', param_name),
|
||||||
|
('parameter_value_type', '=', 'numeric')
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
print(f"\nProbando {param_name} con valor {test_value} (esperado: {expected_type})")
|
||||||
|
|
||||||
|
with env.cr.savepoint():
|
||||||
|
# Establecer el valor de prueba
|
||||||
|
result.value_numeric = test_value
|
||||||
|
result.notes = ''
|
||||||
|
|
||||||
|
# Forzar recálculo de is_critical
|
||||||
|
result._compute_is_out_of_range()
|
||||||
|
|
||||||
|
# Trigger el onchange
|
||||||
|
result._onchange_critical_value()
|
||||||
|
|
||||||
|
print(f" ¿Es crítico?: {'SÍ' if result.is_critical else 'NO'}")
|
||||||
|
print(f" Nota generada: {result.notes[:100]}...")
|
||||||
|
|
||||||
|
# No guardar
|
||||||
|
env.cr.rollback()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
db_name = 'lims_demo'
|
||||||
|
registry = odoo.registry(db_name)
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
test_critical_notes_autocomplete(cr)
|
Loading…
Reference in New Issue
Block a user