diff --git a/mobile.py b/mobile.py index 196e60e9..0eb8c9ca 100644 --- a/mobile.py +++ b/mobile.py @@ -116,7 +116,7 @@ @app.route("/version", methods=["GET"]) def getVersion(): - return {"status": "success", "data": "v3.26-beta"}, status.HTTP_200_OK + return {"status": "success", "data": "v3.27-beta"}, status.HTTP_200_OK if __name__ == "__main__": diff --git a/models/prescription.py b/models/prescription.py index 1e4554a2..8fa477b5 100644 --- a/models/prescription.py +++ b/models/prescription.py @@ -314,6 +314,10 @@ def getPatients( idPatient=[], intervals=[], prescriber=None, + diff=None, + global_score_min=None, + global_score_max=None, + pending_interventions=None, ): q = ( db.session.query( @@ -492,6 +496,48 @@ def getPatients( ) ) + if diff != None: + if bool(int(diff)): + q = q.filter(Prescription.features["diff"].astext.cast(Integer) > 0) + else: + q = q.filter(Prescription.features["diff"].astext.cast(Integer) == 0) + + if pending_interventions != None: + if bool(int(pending_interventions)): + q = q.filter( + Prescription.features["interventions"].astext.cast(Integer) > 0 + ) + else: + q = q.filter( + Prescription.features["interventions"].astext.cast(Integer) == 0 + ) + + if global_score_min != None: + q = q.filter( + ( + Prescription.features["prescriptionScore"].astext.cast(Integer) + + Prescription.features["av"].astext.cast(Integer) + + Prescription.features["am"].astext.cast(Integer) + + Prescription.features["alertExams"].astext.cast(Integer) + + Prescription.features["alerts"].astext.cast(Integer) + + Prescription.features["diff"].astext.cast(Integer) + ) + >= global_score_min + ) + + if global_score_max != None: + q = q.filter( + ( + Prescription.features["prescriptionScore"].astext.cast(Integer) + + Prescription.features["av"].astext.cast(Integer) + + Prescription.features["am"].astext.cast(Integer) + + Prescription.features["alertExams"].astext.cast(Integer) + + Prescription.features["alerts"].astext.cast(Integer) + + Prescription.features["diff"].astext.cast(Integer) + ) + <= global_score_max + ) + if prescriber != None: q = q.filter(Prescription.prescriber.ilike(f"%{prescriber}%")) diff --git a/routes/prescription.py b/routes/prescription.py index d641cb43..959c1406 100644 --- a/routes/prescription.py +++ b/routes/prescription.py @@ -71,6 +71,10 @@ def getPrescriptions(): patientStatus = request.args.get("patientStatus", None) patientReviewType = request.args.get("patientReviewType", None) prescriber = request.args.get("prescriber", None) + diff = request.args.get("diff", None) + pending_interventions = request.args.get("pendingInterventions", None) + global_score_min = request.args.get("globalScoreMin", None) + global_score_max = request.args.get("globalScoreMax", None) patients = Patient.getPatients( idSegment=idSegment, @@ -96,6 +100,10 @@ def getPrescriptions(): idPatient=idPatient, intervals=intervals, prescriber=prescriber, + diff=diff, + global_score_min=global_score_min, + global_score_max=global_score_max, + pending_interventions=pending_interventions, ) results = [] @@ -121,6 +129,7 @@ def getPrescriptions(): "alertExams", "interventions", "complication", + "alertLevel", ] features = {"processed": True} @@ -750,6 +759,7 @@ def setPrescriptionStatus(): ) evaluation_time = data.get("evaluationTime", None) alerts = data.get("alerts", []) + fast_check = data.get("fastCheck", False) try: result = prescription_service.check_prescription( @@ -758,6 +768,7 @@ def setPrescriptionStatus(): user=user, evaluation_time=evaluation_time, alerts=alerts, + fast_check=fast_check, ) except ValidationError as e: return { diff --git a/services/prescription_service.py b/services/prescription_service.py index 6a2f3d54..e414a921 100644 --- a/services/prescription_service.py +++ b/services/prescription_service.py @@ -88,7 +88,13 @@ def search(search_key): def check_prescription( - idPrescription, p_status, user, evaluation_time, alerts, service_user=False + idPrescription, + p_status, + user, + evaluation_time, + alerts, + service_user=False, + fast_check=False, ): roles = user.config["roles"] if user.config and "roles" in user.config else [] if not permission_service.is_pharma(user): @@ -102,14 +108,18 @@ def check_prescription( if p is None: raise ValidationError( "Prescrição inexistente", - "errors.invalidRegister", + "errors.businessRules", status.HTTP_400_BAD_REQUEST, ) if p.status == p_status: raise ValidationError( - "Não houve alteração de situação", - "errors.invalidRegister", + ( + "Não houve alteração da situação: Prescrição já está checada" + if p_status == "s" + else "Não houve alteração da situação: A checagem já foi desfeita" + ), + "errors.businessRules", status.HTTP_400_BAD_REQUEST, ) @@ -147,6 +157,7 @@ def check_prescription( "evaluationTime": evaluation_time or 0, "alerts": alerts, "serviceUser": service_user, + "fastCheck": fast_check, } results = [] diff --git a/services/support_service.py b/services/support_service.py index b6b4846b..e1cbee02 100644 --- a/services/support_service.py +++ b/services/support_service.py @@ -48,9 +48,11 @@ def create_ticket(user, from_url, filelist, category, description, title): options={"specification": {}}, ) + attachments = [] + if filelist: for f in filelist: - client( + att = client( model="ir.attachment", action="create", payload=[ @@ -65,6 +67,8 @@ def create_ticket(user, from_url, filelist, category, description, title): options={}, ) + attachments.append(att) + ticket = client( model="helpdesk.ticket", action="search_read", @@ -74,11 +78,31 @@ def create_ticket(user, from_url, filelist, category, description, title): "id", "access_token", "ticket_ref", + "partner_id", ], "limit": 50, }, ) + if len(ticket) > 0 and ticket[0].get("partner_id", None) != None: + # add message + client( + model="mail.message", + action="create", + payload=[ + { + "message_type": "email", + "author_id": ticket[0]["partner_id"][0], + "body": description, + "model": "helpdesk.ticket", + "res_id": result[0]["id"], + "subtype_id": 1, + "attachment_ids": attachments, + } + ], + options={}, + ) + return ticket diff --git a/tests/test_alerts.py b/tests/test_alerts.py index 7170cfed..0c91b0e1 100644 --- a/tests/test_alerts.py +++ b/tests/test_alerts.py @@ -3,6 +3,7 @@ from conftest import * from models.prescription import PrescriptionDrug, DrugAttributes, Drug, Frequency +from models.enums import DrugAlertTypeEnum, DrugAlertLevelEnum from services import alert_service MockRow = namedtuple( @@ -27,6 +28,8 @@ def _get_mock_row( lactating: str = None, interval: str = None, freq_obj: Frequency = None, + use_weight: bool = False, + expire_date: datetime = None, ): d = Drug() d.id = 1 @@ -54,6 +57,7 @@ def _get_mock_row( da.pregnant = pregnant da.lactating = lactating da.fasting = True + da.useWeight = use_weight return MockRow( pd, @@ -113,6 +117,96 @@ def test_dosemaxplus(): assert stats.get("maxDosePlus", 0) == 2 +@pytest.mark.parametrize( + "doses, frequencies, max_dose, weight, use_weight, expire_dates, expected_alert", + [ + ( + [10, 10], # doses + [2, 3], # frequencies + 40, # max_dose + 80, # weight + False, # use_weight + [datetime.now(), datetime.now()], # expire_dates + True, # expected_alert + ), + ( + [5, 10], # doses + [2, 1], # frequencies + 40, # max_dose + 80, # weight + False, # use_weight + [datetime.now(), datetime.now()], # expire_dates + False, # expected_alert + ), + ( + [0.3, 0.4], # doses + [2, 3], # frequencies + 0.5, # max_dose + 80, # weight + True, # use_weight + [datetime.now(), datetime.now()], # expire_dates + True, # expected_alert + ), + ( + [0.1, 0.2], # doses + [2, 1], # frequencies + 0.5, # max_dose + 80, # weight + True, # use_weight + [datetime.now(), datetime.now()], # expire_dates + False, # expected_alert + ), + ], +) +def test_max_dose_total_additional( + doses, frequencies, max_dose, weight, use_weight, expire_dates, expected_alert +): + drugs = [ + _get_mock_row( + id_prescription_drug=i + 1, + dose=dose, + frequency=freq, + max_dose=max_dose, + use_weight=use_weight, + expire_date=exp_date, + ) + for i, (dose, freq, exp_date) in enumerate( + zip(doses, frequencies, expire_dates) + ) + ] + exams = {"age": 50, "weight": weight} + + alerts = alert_service.find_alerts( + drug_list=drugs, + exams=exams, + dialisys=None, + pregnant=None, + lactating=None, + schedules_fasting=None, + ) + max_dose_total_alerts = [ + alert + for alerts_list in alerts.get("alerts", {}).values() + for alert in alerts_list + if alert.get("type") == DrugAlertTypeEnum.MAX_DOSE_PLUS.value + ] + max_dose_total_alert_count = alerts.get("stats", {}).get( + DrugAlertTypeEnum.MAX_DOSE_PLUS.value, 0 + ) + + if expected_alert: + assert len(max_dose_total_alerts) > 0 + assert ( + max_dose_total_alerts[0].get("type") + == DrugAlertTypeEnum.MAX_DOSE_PLUS.value + ) + assert max_dose_total_alerts[0].get("level") == DrugAlertLevelEnum.HIGH.value + assert max_dose_total_alert_count > 0 + else: + assert len(max_dose_total_alerts) == 0 + assert max_dose_total_alert_count == 0 + + def test_dosemax(): """Alertas dosemaxplus: Testa presença do alerta tipo dosemax""" drugs = [] @@ -144,6 +238,55 @@ def test_dosemax(): assert stats.get("maxDose", 0) == 1 +@pytest.mark.parametrize( + "dose, frequency, max_dose, weight, use_weight, expected_alert", + [ + (20, 1, 10, 80, False, True), # Dose exceeds max_dose + (5, 1, 10, 80, False, False), # Dose within max_dose + (1, 1, 0.05, 80, True, True), # Dose/kg exceeds max_dose/kg + (0.02, 1, 0.05, 80, True, False), # Dose/kg within max_dose/kg + (20, 2, 30, 80, False, True), # Daily dose (20*2) exceeds max_dose + (10, 2, 30, 80, False, False), # Daily dose (10*2) within max_dose + ], +) +def test_max_dose_additional( + dose, frequency, max_dose, weight, use_weight, expected_alert +): + drugs = [ + _get_mock_row( + id_prescription_drug=61, + dose=dose, + frequency=frequency, + max_dose=max_dose, + use_weight=use_weight, + ) + ] + exams = {"age": 50, "weight": weight} + + alerts = alert_service.find_alerts( + drug_list=drugs, + exams=exams, + dialisys=None, + pregnant=None, + lactating=None, + schedules_fasting=None, + ) + + max_dose_alerts = alerts.get("alerts", {}).get("61", []) + max_dose_alert_count = alerts.get("stats", {}).get( + DrugAlertTypeEnum.MAX_DOSE.value, 0 + ) + + if expected_alert: + assert len(max_dose_alerts) == 1 + assert max_dose_alerts[0].get("type") == DrugAlertTypeEnum.MAX_DOSE.value + assert max_dose_alerts[0].get("level") == DrugAlertLevelEnum.HIGH.value + assert max_dose_alert_count == 1 + else: + assert len(max_dose_alerts) == 0 + assert max_dose_alert_count == 0 + + def test_kidney_dialysis(): """Kidney alerts: Test for the presence of an alert type equal to 'kidney' with dialysis""" @@ -289,6 +432,50 @@ def test_kidney_swrtz1(): assert stats.get("kidney", 0) == 1 +@pytest.mark.parametrize( + "kidney_threshold, ckd_value, age, dialysis, expected_alert", + [ + (60, 50, 18, None, True), # Adult, CKD below threshold + (60, 70, 18, None, False), # Adult, CKD above threshold + (60, 70, 18, "c", True), # Continuous dialysis + (60, 70, 18, "x", True), # Extended dialysis + (60, 70, 18, "v", True), # Intermittent dialysis + (60, 70, 18, "p", True), # Peritoneal dialysis + ], +) +def test_kidney_alert_multiple( + kidney_threshold, ckd_value, age, dialysis, expected_alert +): + + drugs = [ + _get_mock_row(id_prescription_drug=1, dose=ckd_value, kidney=kidney_threshold) + ] + exams = { + "age": age, + "weight": 80, + "ckd": {"value": ckd_value} if ckd_value is not None else None, + } + alerts = alert_service.find_alerts( + drug_list=drugs, + exams=exams, + dialisys=dialysis, + pregnant=None, + lactating=None, + schedules_fasting=None, + ) + kidney_alerts = alerts.get("alerts", {}).get("1", []) + kidney_alert_count = alerts.get("stats", {}).get(DrugAlertTypeEnum.KIDNEY.value, 0) + + if expected_alert: + assert len(kidney_alerts) == 1 + assert kidney_alerts[0].get("type") == DrugAlertTypeEnum.KIDNEY.value + assert kidney_alerts[0].get("level") == DrugAlertLevelEnum.MEDIUM.value + assert kidney_alert_count == 1 + else: + assert len(kidney_alerts) == 0 + assert kidney_alert_count == 0 + + def test_liver(): """Liver alerts: Test for the presence of an alert type equal to 'liver'""" @@ -483,48 +670,55 @@ def test_allergy(): assert stats.get("allergy", 0) == 2 + # simple ira alert test, single condition def test_ira(): """IRA alerts: Test for the presence of an alert type equal to 'ira' - Insuficiência Renal Aguda""" - drugs =[] + drugs = [] drugs.append( - _get_mock_row(id_prescription_drug=61,dose=10, frequency=10,drug_name = "xxx vancoyyy") + _get_mock_row( + id_prescription_drug=61, dose=10, frequency=10, drug_name="xxx vancoyyy" + ) ) - exams = {'ckd' : {'value' : 1.}, 'weight': 80 } + exams = {"ckd": {"value": 1.0}, "weight": 80} alerts = alert_service.find_alerts( drug_list=drugs, exams=exams, - dialisys= None, + dialisys=None, pregnant=None, lactating=None, - schedules_fasting= None, + schedules_fasting=None, ) stats = alerts.get("stats") alert1 = alerts.get("alerts").get("61", []) - + assert len(alert1) == 1 - + assert alert1[0].get("type", None) == "ira" assert alert1[0].get("level", None) == "high" - - assert stats.get("ira", 0) == 1 + + assert stats.get("ira", 0) == 1 + ### TESTS FOR ALERT IRA CONDITIONS - MULTIPLE SCENARIOS -# input data +# input data # dose, ckd, weight, dialysis = test_input -@pytest.mark.parametrize("test_input,expected_alert", [ - ((3000, 40, 70, None), True), # Should trigger alert (baseline case) - ((2000, 50, 70, None), False), # Should not trigger alert (below threshold) - ((4000, 30, 60, None), True), # Should trigger alert (high dose, low CKD) - ((3000, 40, 70, 'c'), False), # Should not trigger alert (patient on dialysis) - ((3000, 0, 70, None), False), # Should not trigger alert (CKD is 0) - ((3000, 40, 0, None), False), # Should not trigger alert (weight is 0) - ((1000, 20, 50, None), True), # Should trigger alert (low dose, very low CKD) -]) +@pytest.mark.parametrize( + "test_input,expected_alert", + [ + ((3000, 40, 70, None), True), # Should trigger alert (baseline case) + ((2000, 50, 70, None), False), # Should not trigger alert (below threshold) + ((4000, 30, 60, None), True), # Should trigger alert (high dose, low CKD) + ((3000, 40, 70, "c"), False), # Should not trigger alert (patient on dialysis) + ((3000, 0, 70, None), False), # Should not trigger alert (CKD is 0) + ((3000, 40, 0, None), False), # Should not trigger alert (weight is 0) + ((1000, 20, 50, None), True), # Should trigger alert (low dose, very low CKD) + ], +) def test_ira_alert_conditions(test_input, expected_alert): """IRA alerts: Test various conditions for triggering or not triggering the IRA alert""" dose, ckd, weight, dialysis = test_input @@ -532,14 +726,11 @@ def test_ira_alert_conditions(test_input, expected_alert): drugs = [] drugs.append( _get_mock_row( - id_prescription_drug=61, - dose=dose, - frequency=1, - drug_name="Vancomicina" + id_prescription_drug=61, dose=dose, frequency=1, drug_name="Vancomicina" ) ) - exams = {'ckd': {'value': ckd}, 'weight': weight} + exams = {"ckd": {"value": ckd}, "weight": weight} alerts = alert_service.find_alerts( drug_list=drugs, @@ -551,17 +742,26 @@ def test_ira_alert_conditions(test_input, expected_alert): ) alert1 = alerts.get("alerts", {}).get("61", []) - + if expected_alert: assert len(alert1) == 1, f"Expected 1 alert, but got {len(alert1)}" - assert alert1[0].get("type") == "ira", f"Expected alert type 'ira', but got {alert1[0].get('type')}" - assert alert1[0].get("level") == "high", f"Expected alert level 'high', but got {alert1[0].get('level')}" - assert alerts.get("stats", {}).get("ira", 0) == 1, f"Expected 1 IRA alert in stats, but got {alerts.get('stats', {}).get('ira', 0)}" - assert "Risco de desenvolvimento de Insuficiência Renal Aguda (IRA)" in alert1[0].get("text", ""), "Expected IRA risk message in alert text" + assert ( + alert1[0].get("type") == "ira" + ), f"Expected alert type 'ira', but got {alert1[0].get('type')}" + assert ( + alert1[0].get("level") == "high" + ), f"Expected alert level 'high', but got {alert1[0].get('level')}" + assert ( + alerts.get("stats", {}).get("ira", 0) == 1 + ), f"Expected 1 IRA alert in stats, but got {alerts.get('stats', {}).get('ira', 0)}" + assert "Risco de desenvolvimento de Insuficiência Renal Aguda (IRA)" in alert1[ + 0 + ].get("text", ""), "Expected IRA risk message in alert text" else: assert len(alert1) == 0, f"Expected no alerts, but got {len(alert1)}" - assert alerts.get("stats", {}).get("ira", 0) == 0, f"Expected 0 IRA alerts in stats, but got {alerts.get('stats', {}).get('ira', 0)}" - + assert ( + alerts.get("stats", {}).get("ira", 0) == 0 + ), f"Expected 0 IRA alerts in stats, but got {alerts.get('stats', {}).get('ira', 0)}" def test_pregnant():