diff --git a/mobile.py b/mobile.py
index fda6e94d..573a3a2a 100644
--- a/mobile.py
+++ b/mobile.py
@@ -111,11 +111,12 @@
if Config.ENV != NoHarmENV.PRODUCTION.value:
logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
+ logging.getLogger("noharm.backend").setLevel(logging.DEBUG)
@app.route("/version", methods=["GET"])
def getVersion():
- return {"status": "success", "data": "v3.12-beta"}, status.HTTP_200_OK
+ return {"status": "success", "data": "v3.13-beta"}, status.HTTP_200_OK
@app.route("/exc", methods=["GET"])
diff --git a/models/enums.py b/models/enums.py
index 860decd6..9984e5ed 100644
--- a/models/enums.py
+++ b/models/enums.py
@@ -71,6 +71,7 @@ class PrescriptionAuditTypeEnum(Enum):
UNDO_REVISION = 4
INTEGRATION_CLINICAL_NOTES = 5
INTEGRATION_PRESCRIPTION_RELEASE = 6
+ UPSERT_CLINICAL_NOTES = 7
class PrescriptionDrugAuditTypeEnum(Enum):
@@ -132,3 +133,23 @@ class NifiQueueActionTypeEnum(Enum):
CLEAR_STATE = "CLEAR_STATE"
TERMINATE_PROCESS = "TERMINATE_PROCESS"
CUSTOM_CALLBACK = "CUSTOM_CALLBACK"
+
+
+class DrugAlertTypeEnum(Enum):
+ KIDNEY = "kidney"
+ LIVER = "liver"
+ PLATELETS = "platelets"
+ ELDERLY = "elderly"
+ TUBE = "tube"
+ ALLERGY = "allergy"
+ MAX_TIME = "maxTime"
+ MAX_DOSE = "maxDose"
+ IRA = "ira"
+ PREGNANT = "pregnant"
+ LACTATING = "lactating"
+
+
+class DrugAlertLevelEnum(Enum):
+ LOW = "low"
+ MEDIUM = "medium"
+ HIGH = "high"
diff --git a/models/main.py b/models/main.py
index 9b3cbbd7..339308af 100644
--- a/models/main.py
+++ b/models/main.py
@@ -113,6 +113,7 @@ class Relation(db.Model):
sctidb = db.Column("sctidb", db.BigInteger, primary_key=True)
kind = db.Column("tprelacao", db.String(2), primary_key=True)
text = db.Column("texto", db.String, nullable=True)
+ level = db.Column("nivel", db.String, nullable=True)
active = db.Column("ativo", db.Boolean, nullable=True)
update = db.Column("update_at", db.DateTime, nullable=True)
user = db.Column("update_by", db.BigInteger, nullable=True)
@@ -146,6 +147,7 @@ def findBySctid(sctid, user):
"type": r[0].kind,
"text": r[0].text,
"active": r[0].active,
+ "level": r[0].level,
"editable": bool(r[0].creator == user.id)
or (not User.permission(user)),
}
diff --git a/models/prescription.py b/models/prescription.py
index 98ff29bf..a42b13a6 100644
--- a/models/prescription.py
+++ b/models/prescription.py
@@ -398,7 +398,18 @@ def getPatients(
if len(indicators) > 0:
for i in indicators:
- q = q.filter(Prescription.features["alertStats"][i].as_integer() > 0)
+ interactions = ["it", "dt", "dm", "iy", "sl", "rx"]
+ if i in interactions:
+ q = q.filter(
+ Prescription.features["alertStats"]["interactions"][
+ i
+ ].as_integer()
+ > 0
+ )
+ else:
+ q = q.filter(
+ Prescription.features["alertStats"][i].as_integer() > 0
+ )
if len(drugAttributes) > 0:
for a in drugAttributes:
diff --git a/routes/drugList.py b/routes/drugList.py
index 95abc8db..26a0b029 100644
--- a/routes/drugList.py
+++ b/routes/drugList.py
@@ -19,11 +19,20 @@ def _get_legacy_alert(kind):
class DrugList:
def __init__(
- self, drugList, interventions, relations, exams, agg, dialysis, is_cpoe=False
+ self,
+ drugList,
+ interventions,
+ relations,
+ exams,
+ agg,
+ dialysis,
+ alerts,
+ is_cpoe=False,
):
self.drugList = drugList
self.interventions = interventions
self.relations = relations
+ self.alerts = alerts
self.exams = exams
self.agg = agg
self.dialysis = dialysis
@@ -45,6 +54,8 @@ def __init__(
"exams": 0, # kidney + liver + platelets
"allergy": 0, # allergy + rea,
"interactions": {},
+ "total": 0,
+ "level": "low",
}
def sumAlerts(self):
@@ -53,6 +64,13 @@ def sumAlerts(self):
for k, v in self.relations["stats"].items():
self.alertStats[_get_legacy_alert(k)] = v
self.alertStats["interactions"][k] = v
+ self.alertStats["total"] += v
+
+ # alerts stats
+ if self.alerts["stats"]:
+ for k, v in self.alerts["stats"].items():
+ self.alertStats[k] = v
+ self.alertStats["total"] += v
# keep legacy data
if (
@@ -70,7 +88,23 @@ def sumAlerts(self):
+ self.alertStats["liver"]
+ self.alertStats["platelets"]
)
- self.alertStats["allergy"] += self.alertStats["rea"]
+
+ levels = []
+ if self.relations["alerts"]:
+ for k, v in self.relations["alerts"].items():
+ for alert in v:
+ levels.append(alert["level"])
+
+ if self.alerts["alerts"]:
+ for k, v in self.alerts["alerts"].items():
+ for alert in v:
+ levels.append(alert["level"])
+
+ if "medium" in levels:
+ self.alertStats["level"] = "medium"
+
+ if "high" in levels:
+ self.alertStats["level"] = "high"
@staticmethod
def sortDrugs(d):
@@ -110,306 +144,65 @@ def getDrugType(self, pDrugs, source):
if pd[0].source not in source:
continue
- pdFrequency = (
- 1 if pd[0].frequency in [33, 44, 55, 66, 99] else pd[0].frequency
- )
-
- if pd[2] != None and pd[6] != None and pd[6].division != None:
- measureUnitFactor = 1
-
- if pd.measure_unit_convert_factor != None:
- measureUnitFactor = pd.measure_unit_convert_factor
-
- pdDoseconv = (
- none2zero(pd[0].dose) * measureUnitFactor * none2zero(pdFrequency)
- )
- else:
- pdDoseconv = none2zero(pd[0].doseconv) * none2zero(pdFrequency)
-
pdUnit = strNone(pd[2].id) if pd[2] else ""
pdWhiteList = bool(pd[6].whiteList) if pd[6] is not None else False
doseWeightStr = None
doseBodySurfaceStr = None
- expireDay = pd[10].day if pd[10] else 0
-
- idDrugAgg = str(pd[0].idDrug) + "_" + str(expireDay)
- if idDrugAgg not in self.maxDoseAgg:
- self.maxDoseAgg[idDrugAgg] = {"value": 0, "count": 0}
tubeAlert = False
alerts = []
-
- if self.relations["alerts"] and pd[0].id in self.relations["alerts"]:
- for a in self.relations["alerts"][pd[0].id]:
- # self.alertStats[a[:3].lower()] += 1
- alerts.append(a)
-
- if not bool(pd[0].suspendedDate):
- self.maxDoseAgg[idDrugAgg]["value"] += pdDoseconv
- self.maxDoseAgg[idDrugAgg]["count"] += 1
-
- if self.exams and pd[6]:
- if pd[6].kidney:
- if self.dialysis == "c":
- alerts.append(
- "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está diálise contínua."
- )
- self.alertStats["kidney"] += 1
- elif self.dialysis == "x":
- alerts.append(
- "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está diálise estendida, também conhecida como SLED."
- )
- self.alertStats["kidney"] += 1
- elif self.dialysis == "v":
- alerts.append(
- "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise intermitente."
- )
- self.alertStats["kidney"] += 1
- elif self.dialysis == "p":
- alerts.append(
- "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise peritoneal."
- )
- self.alertStats["kidney"] += 1
- elif (
- "ckd" in self.exams
- and self.exams["ckd"]["value"]
- and pd[6].kidney > self.exams["ckd"]["value"]
- and self.exams["age"] > 17
- ):
- alerts.append(
- "Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente ("
- + str(self.exams["ckd"]["value"])
- + " mL/min) está abaixo de "
- + str(pd[6].kidney)
- + " mL/min."
- )
- self.alertStats["kidney"] += 1
- elif (
- "swrtz2" in self.exams
- and self.exams["swrtz2"]["value"]
- and pd[6].kidney > self.exams["swrtz2"]["value"]
- and self.exams["age"] <= 17
- ):
- alerts.append(
- "Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente ("
- + str(self.exams["swrtz2"]["value"])
- + " mL/min/1.73m²) está abaixo de "
- + str(pd[6].kidney)
- + " mL/min. (Schwartz 2)"
- )
- self.alertStats["kidney"] += 1
- elif (
- "swrtz1" in self.exams
- and self.exams["swrtz1"]["value"]
- and pd[6].kidney > self.exams["swrtz1"]["value"]
- and self.exams["age"] <= 17
- ):
- alerts.append(
- "Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente ("
- + str(self.exams["swrtz1"]["value"])
- + " mL/min/1.73m²) está abaixo de "
- + str(pd[6].kidney)
- + " mL/min. (Schwartz 1)"
- )
- self.alertStats["kidney"] += 1
-
- if pd[6].liver:
- if (
- "tgp" in self.exams
- and self.exams["tgp"]["value"]
- and float(self.exams["tgp"]["value"]) > pd[6].liver
- ) or (
- "tgo" in self.exams
- and self.exams["tgo"]["value"]
- and float(self.exams["tgo"]["value"]) > pd[6].liver
- ):
- alerts.append(
- "Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função hepática do paciente está reduzida (acima de "
- + str(pd[6].liver)
- + " U/L)."
- )
- self.alertStats["liver"] += 1
+ alerts_complete = []
+
+ if self.relations["alerts"] and str(pd[0].id) in self.relations["alerts"]:
+ for a in self.relations["alerts"][str(pd[0].id)]:
+ alerts.append(a["text"])
+ alerts_complete.append(a)
+
+ if self.alerts["alerts"] and str(pd[0].id) in self.alerts["alerts"]:
+ for a in self.alerts["alerts"][str(pd[0].id)]:
+ alerts.append(a["text"])
+ alerts_complete.append(a)
+
+ if self.exams and pd[6]:
+ if pd[6].chemo and pd[0].dose:
+ bs_weight = none2zero(self.exams["weight"])
+ bs_height = none2zero(self.exams["height"])
+
+ if bs_weight != 0 and bs_height != 0:
+ body_surface = math.sqrt((bs_weight * bs_height) / 3600)
+ doseBodySurfaceStr = f"""{strFormatBR(round(pd[0].dose / body_surface, 2))} {pdUnit}/m²"""
+
+ if pd[6].useWeight and pd[0].dose:
+ weight = none2zero(self.exams["weight"])
+ weight = weight if weight > 0 else 1
+
+ doseWeightStr = (
+ strFormatBR(round(pd[0].dose / float(weight), 2))
+ + " "
+ + pdUnit
+ + "/Kg"
+ )
if (
- pd[6].platelets
- and "plqt" in self.exams
- and self.exams["plqt"]["value"]
- and pd[6].platelets > self.exams["plqt"]["value"]
+ pd[6].idMeasureUnit != None
+ and pd[6].idMeasureUnit != pdUnit
+ and pd[0].doseconv != None
):
- alerts.append(
- "Medicamento contraindicado para paciente com plaquetas ("
- + str(self.exams["plqt"]["value"])
- + " plaquetas/µL) abaixo de "
- + str(pd[6].platelets)
- + " plaquetas/µL."
- )
- self.alertStats["platelets"] += 1
-
- if pd[6].elderly and self.exams["age"] > 60:
- alerts.append(
- "Medicamento potencialmente inapropriado para idosos, independente das comorbidades do paciente."
- )
- self.alertStats["elderly"] += 1
-
- if pd[6].chemo and pd[0].dose:
- bs_weight = none2zero(self.exams["weight"])
- bs_height = none2zero(self.exams["height"])
-
- if bs_weight != 0 and bs_height != 0:
- body_surface = math.sqrt((bs_weight * bs_height) / 3600)
- doseBodySurfaceStr = f"""{strFormatBR(round(pd[0].dose / body_surface, 2))} {pdUnit}/m²"""
-
- if pd[6].useWeight and pd[0].dose:
- weight = none2zero(self.exams["weight"])
- weight = weight if weight > 0 else 1
-
- doseWeight = round(pdDoseconv / float(weight), 2)
- doseWeightStr = (
- strFormatBR(round(pd[0].dose / float(weight), 2))
+ doseWeightStr += (
+ " ou "
+ + strFormatBR(pd[0].doseconv)
+ " "
- + pdUnit
- + "/Kg"
+ + str(pd[6].idMeasureUnit)
+ + "/Kg (faixa arredondada)"
)
- if (
- pd[6].idMeasureUnit != None
- and pd[6].idMeasureUnit != pdUnit
- and pd[0].doseconv != None
- ):
- doseWeightStr += (
- " ou "
- + strFormatBR(pd[0].doseconv)
- + " "
- + str(pd[6].idMeasureUnit)
- + "/Kg (faixa arredondada)"
- )
-
- keyDrugKg = str(idDrugAgg) + "kg"
- if keyDrugKg not in self.maxDoseAgg:
- self.maxDoseAgg[keyDrugKg] = {"value": 0, "count": 0}
-
- self.maxDoseAgg[keyDrugKg]["value"] += doseWeight
- self.maxDoseAgg[keyDrugKg]["count"] += 1
-
- if pd[6].maxDose and pd[6].maxDose < doseWeight:
- alerts.append(
- "Dose diária prescrita ("
- + strFormatBR(doseWeight)
- + " "
- + str(pd[6].idMeasureUnit)
- + "/Kg) maior que a dose de alerta ("
- + strFormatBR(pd[6].maxDose)
- + " "
- + str(pd[6].idMeasureUnit)
- + "/Kg) usualmente recomendada (considerada a dose diária independente da indicação)."
- )
- self.alertStats["maxDose"] += 1
-
- if (
- pd[6].maxDose
- and self.maxDoseAgg[keyDrugKg]["count"] > 1
- and pd[6].maxDose
- < none2zero(self.maxDoseAgg[keyDrugKg]["value"])
- ):
- alerts.append(
- "Dose diária prescrita SOMADA ("
- + str(self.maxDoseAgg[keyDrugKg]["value"])
- + " "
- + str(pd[6].idMeasureUnit)
- + "/Kg) maior que a dose de alerta ("
- + str(pd[6].maxDose)
- + " "
- + str(pd[6].idMeasureUnit)
- + "/Kg) usualmente recomendada (considerada a dose diária independente da indicação)."
- )
- self.alertStats["maxDose"] += 1
-
- else:
- if pd[6].maxDose and pd[6].maxDose < pdDoseconv:
- alerts.append(
- "Dose diária prescrita ("
- + str(pdDoseconv)
- + " "
- + str(pd[6].idMeasureUnit)
- + ") maior que a dose de alerta ("
- + str(pd[6].maxDose)
- + " "
- + str(pd[6].idMeasureUnit)
- + ") usualmente recomendada (considerada a dose diária independente da indicação)."
- )
- self.alertStats["maxDose"] += 1
-
- if (
- pd[6].maxDose
- and self.maxDoseAgg[idDrugAgg]["count"] > 1
- and pd[6].maxDose
- < none2zero(self.maxDoseAgg[idDrugAgg]["value"])
- ):
- alerts.append(
- "Dose diária prescrita SOMADA ("
- + str(self.maxDoseAgg[idDrugAgg]["value"])
- + " "
- + str(pd[6].idMeasureUnit)
- + ") maior que a dose de alerta ("
- + str(pd[6].maxDose)
- + " "
- + str(pd[6].idMeasureUnit)
- + ") usualmente recomendada (considerada a dose diária independente da indicação)."
- )
- self.alertStats["maxDose"] += 1
-
- if pd[6] and pd[6].tube and pd[0].tube:
- alerts.append(
- "Medicamento contraindicado via sonda ("
- + strNone(pd[0].route)
- + ")"
- )
- self.alertStats["tube"] += 1
- tubeAlert = True
-
- if pd[0].allergy == "S":
- alerts.append("Paciente alérgico a este medicamento.")
- self.alertStats["allergy"] += 1
-
if (
- pd[6]
- and pd[6].maxTime
- and pd[0].period
- and pd[0].period > pd[6].maxTime
+ not bool(pd[0].suspendedDate)
+ and pd[6]
+ and pd[6].tube
+ and pd[0].tube
):
- alerts.append(
- "Tempo de tratamento atual ("
- + str(pd[0].period)
- + " dias) maior que o tempo máximo de tratamento ("
- + str(pd[6].maxTime)
- + " dias) usualmente recomendado."
- )
- self.alertStats["maxTime"] += 1
-
- if pd[1] and "vanco" in pd[1].name.lower():
- maxdose = self.maxDoseAgg[idDrugAgg]["value"]
- ckd = (
- self.exams["ckd"]["value"]
- if "ckd" in self.exams and self.exams["ckd"]["value"]
- else None
- )
- weight = self.exams["weight"]
-
- if (
- maxdose != None
- and ckd != None
- and weight != None
- and ckd > 0
- and weight > 0
- ):
- ira = maxdose / ckd / weight
- maxira = 0.6219
-
- if ira > maxira and self.dialysis is None:
- self.alertStats["exams"] += 1
- alerts.append(
- 'Risco de desenvolvimento de Insuficiência Renal Aguda (IRA), já que o resultado do cálculo [dose diária de VANCOMICINA/TFG/peso] é superior a 0,6219. Caso o paciente esteja em diálise, desconsiderar. Referência: CaEPS'
- )
+ tubeAlert = True
total_period = 0
if self.is_cpoe:
@@ -518,9 +311,9 @@ def getDrugType(self, pDrugs, source):
"existIntervention": self.getExistIntervention(
pd[0].idDrug, pd[0].idPrescription
),
- # remove intervention attribute after transition (new attribute = interventionList)
- "intervention": self.getIntervention(pd[0].id),
- "alerts": alerts,
+ # remove alerts attribute after migration to alertsComplete
+ # "alerts": alerts,
+ "alertsComplete": alerts_complete,
"tubeAlert": tubeAlert,
"notes": pd[7],
"prevNotes": prevNotes,
@@ -532,11 +325,6 @@ def getDrugType(self, pDrugs, source):
"infusionKey": self.getInfusionKey(pd),
"formValues": pd[0].form,
"drugAttributes": self.getDrugAttributes(pd),
- "relations": (
- self.relations["list"][pd[0].id]
- if pd[0].id in self.relations["list"]
- else []
- ),
}
)
diff --git a/routes/prescription.py b/routes/prescription.py
index 2302c9af..c606cdc2 100644
--- a/routes/prescription.py
+++ b/routes/prescription.py
@@ -1,5 +1,6 @@
import os
import random
+import logging
from utils import status
from models.main import *
from models.appendix import *
@@ -20,12 +21,13 @@
from datetime import date, datetime
from .drugList import DrugList
from services import (
+ alert_service,
+ alert_interaction_service,
memory_service,
prescription_service,
prescription_drug_service,
intervention_service,
patient_service,
- alert_service,
data_authorization_service,
)
from converter import prescription_converter
@@ -189,7 +191,7 @@ def getPrescriptions():
id_segment=p[0].idSegment,
pdate=p[0].date,
),
- }
+ },
)
)
@@ -306,6 +308,14 @@ def getExistIntervention(interventions, dtPrescription):
return result
+def _log_perf(start_date, section):
+ end_date = datetime.now()
+ logging.basicConfig()
+ logger = logging.getLogger("noharm.backend")
+
+ logger.debug(f"PERF {section}: {(end_date-start_date).total_seconds()}")
+
+
def getPrescription(
idPrescription=None,
admissionNumber=None,
@@ -315,6 +325,8 @@ def getPrescription(
is_pmc=False,
is_complete=False,
):
+ start_date = datetime.now()
+
if idPrescription:
prescription = Prescription.getPrescription(idPrescription)
else:
@@ -356,33 +368,36 @@ def getPrescription(
ref_date=aggDate if aggDate != None else prescription[0].date,
)
+ _log_perf(start_date, "GET PRESCRIPTION")
+
+ start_date = datetime.now()
drugs = PrescriptionDrug.findByPrescription(
prescription[0].id, patient.admissionNumber, aggDate, idSegment, is_cpoe, is_pmc
)
interventions = intervention_service.get_interventions(
admissionNumber=patient.admissionNumber
)
-
- relations = alert_service.find_relations(
- drug_list=drugs, is_cpoe=is_cpoe, id_patient=patient.idPatient
- )
headers = (
Prescription.getHeaders(admissionNumber, aggDate, idSegment, is_pmc, is_cpoe)
if aggDate
else []
)
+ _log_perf(start_date, "GET DRUGS AND INTERVENTIONS")
+
formTemplate = memory_service.get_memory(MemoryEnum.PRESMED_FORM.value)
admission_reports = memory_service.get_memory(MemoryEnum.ADMISSION_REPORTS.value)
admission_reports_internal = memory_service.get_memory(
MemoryEnum.ADMISSION_REPORTS_INTERNAL.value
)
+ start_date = datetime.now()
clinicalNotesCount = ClinicalNotes.getCountIfExists(
prescription[0].admissionNumber, is_pmc
)
notesTotal = ClinicalNotes.getTotalIfExists(
prescription[0].admissionNumber, admission_date=patient.admissionDate
)
+
notesSigns = None
notesInfo = None
notesAllergies = []
@@ -406,6 +421,9 @@ def getPrescription(
for a in dialysis:
notesDialysis.append({"date": a[1].isoformat(), "text": a[0], "id": a[3]})
+ _log_perf(start_date, "GET CLINICAL NOTES")
+
+ start_date = datetime.now()
registeredAllergies = patient_service.get_patient_allergies(patient.idPatient)
for a in registeredAllergies:
notesAllergies.append({"date": a[0], "text": a[1], "source": "pep"})
@@ -426,6 +444,19 @@ def getPrescription(
exams = dict(
exams, **{"age": age, "weight": patientWeight, "height": patientHeight}
)
+ _log_perf(start_date, "ALLERGIES AND EXAMS")
+
+ start_date = datetime.now()
+ relations = alert_interaction_service.find_relations(
+ drug_list=drugs, is_cpoe=is_cpoe, id_patient=patient.idPatient
+ )
+ alerts = alert_service.find_alerts(
+ drug_list=drugs,
+ exams=exams,
+ dialisys=patient.dialysis,
+ pregnant=patient.pregnant,
+ lactating=patient.lactating,
+ )
drugList = DrugList(
drugs,
@@ -434,9 +465,12 @@ def getPrescription(
exams,
aggDate is not None,
patient.dialysis,
+ alerts,
is_cpoe,
)
+ _log_perf(start_date, "ALERTS")
+ start_date = datetime.now()
disable_solution_tab = memory_service.has_feature(
FeatureEnum.DISABLE_SOLUTION_TAB.value
)
@@ -511,6 +545,8 @@ def getPrescription(
if int(i["id"]) == 0 and int(i["idPrescription"]) == prescription[0].id
]
+ _log_perf(start_date, "ADDITIONAL QUERIES")
+
return {
"status": "success",
"data": {
@@ -651,6 +687,22 @@ def setPrescriptionData(idPrescription):
p.notes = data.get("notes", None)
p.notes_at = datetime.today()
+ audit = PrescriptionAudit()
+ audit.auditType = PrescriptionAuditTypeEnum.UPSERT_CLINICAL_NOTES.value
+ audit.admissionNumber = p.admissionNumber
+ audit.idPrescription = p.id
+ audit.prescriptionDate = p.date
+ audit.idDepartment = p.idDepartment
+ audit.idSegment = p.idSegment
+ audit.totalItens = 0
+ audit.agg = p.agg
+ audit.concilia = p.concilia
+ audit.bed = p.bed
+ audit.extra = {"text": data.get("notes", None)}
+ audit.createdAt = datetime.today()
+ audit.createdBy = user.id
+ db.session.add(audit)
+
if "concilia" in data.keys():
concilia = data.get("concilia", "s")
p.concilia = str(concilia)[:1]
@@ -675,6 +727,7 @@ def setPrescriptionStatus():
else None
)
evaluation_time = data.get("evaluationTime", None)
+ alerts = data.get("alerts", [])
try:
result = prescription_service.check_prescription(
@@ -682,6 +735,7 @@ def setPrescriptionStatus():
p_status=p_status,
user=user,
evaluation_time=evaluation_time,
+ alerts=alerts,
)
except ValidationError as e:
return {
diff --git a/routes/substance.py b/routes/substance.py
index f748154c..286ebef3 100644
--- a/routes/substance.py
+++ b/routes/substance.py
@@ -178,6 +178,9 @@ def setRelation(sctidA, sctidB, kind):
if "active" in data.keys():
relation.active = bool(data.get("active", False))
+ if "level" in data.keys():
+ relation.level = data.get("level", None)
+
relation.update = datetime.today()
relation.user = user.id
diff --git a/routes/utils.py b/routes/utils.py
index 0dbcb847..3d3645bc 100644
--- a/routes/utils.py
+++ b/routes/utils.py
@@ -465,13 +465,15 @@ def getFeatures(result):
drugList.extend(result["data"]["solution"])
drugList.extend(result["data"]["procedures"])
- allergy = alerts = pScore = score1 = score2 = score3 = 0
+ allergy = alerts = alerts_prescription = pScore = score1 = score2 = score3 = 0
am = av = control = np = tube = diff = 0
drugIDs = []
substanceIDs = []
substanceClassIDs = []
frequencies = []
drug_attributes = {}
+ alert_levels = []
+ alert_level = "low"
for attr in get_bool_drug_attributes_list():
drug_attributes[attr] = 0
@@ -492,7 +494,7 @@ def getFeatures(result):
continue
allergy += int(d["allergy"])
- alerts += len(d["alerts"])
+ alerts_prescription += len(d["alertsComplete"])
pScore += int(d["score"])
score1 += int(d["score"] == "1")
score2 += int(d["score"] == "2")
@@ -507,6 +509,9 @@ def getFeatures(result):
if d["frequency"]["value"] != "":
frequencies.append(d["frequency"]["value"])
+ for a in d["alertsComplete"]:
+ alert_levels.append(a["level"])
+
interventions = 0
for i in result["data"]["interventions"]:
interventions += int(i["status"] == "s")
@@ -514,10 +519,25 @@ def getFeatures(result):
exams = result["data"]["alertExams"]
complicationCount = result["data"]["complication"]
+ if "alertStats" in result["data"]:
+ # entire prescription (agg features)
+ alerts = result["data"]["alertStats"]["total"]
+ alert_level = result["data"]["alertStats"].get("level", "low")
+ else:
+ # headers
+ alerts = alerts_prescription
+
+ if "medium" in alert_levels:
+ alert_level = "medium"
+
+ if "high" in alert_levels:
+ alert_level = "high"
+
return {
"alergy": allergy,
"allergy": allergy,
"alerts": alerts,
+ "alertLevel": alert_level,
"prescriptionScore": pScore,
"scoreOne": score1,
"scoreTwo": score2,
diff --git a/services/alert_interaction_service.py b/services/alert_interaction_service.py
new file mode 100644
index 00000000..31039534
--- /dev/null
+++ b/services/alert_interaction_service.py
@@ -0,0 +1,302 @@
+from datetime import datetime
+from sqlalchemy import text
+
+from models.prescription import PrescriptionDrug, Allergy
+from models.main import db, Drug, Substance
+from models.enums import DrugTypeEnum, DrugAlertLevelEnum
+from routes.utils import typeRelations, strNone
+
+
+# analyze interactions between drugs.
+# drug_list (PrescriptionDrug.findByPrescription)
+def find_relations(drug_list, id_patient: int, is_cpoe: bool):
+ filtered_list = _filter_drug_list(drug_list=drug_list)
+ allergies = _get_allergies(id_patient=id_patient)
+ overlap_drugs = []
+
+ for item in filtered_list:
+ prescription_drug: PrescriptionDrug = item[0]
+ drug: Drug = item[1]
+ prescription_date = item[13]
+ prescription_expire_date = item[10] if item[10] != None else datetime.today()
+
+ for compare_item in filtered_list:
+ cp_prescription_drug: PrescriptionDrug = compare_item[0]
+ cp_drug: Drug = compare_item[1]
+ cp_prescription_date = compare_item[13]
+ cp_prescription_expire_date = (
+ compare_item[10] if compare_item[10] != None else datetime.today()
+ )
+
+ if prescription_drug.id == cp_prescription_drug.id:
+ continue
+
+ if is_cpoe:
+ # period overlap
+ if not (
+ (prescription_date.date() <= cp_prescription_expire_date.date())
+ and (cp_prescription_date.date() <= prescription_expire_date.date())
+ ):
+ continue
+ else:
+ # same expire date
+ if not (
+ prescription_expire_date.date()
+ == cp_prescription_expire_date.date()
+ ):
+ continue
+
+ overlap_drugs.append(
+ {
+ "from": {
+ "id": str(prescription_drug.id),
+ "drug": drug.name,
+ "sctid": drug.sctid,
+ "intravenous": (
+ prescription_drug.intravenous
+ if prescription_drug.intravenous != None
+ else False
+ ),
+ "group": _get_solution_group_key(
+ pd=prescription_drug, is_cpoe=is_cpoe
+ ),
+ "expireDate": prescription_expire_date.isoformat(),
+ "rx": False,
+ },
+ "to": {
+ "id": str(cp_prescription_drug.id),
+ "drug": cp_drug.name,
+ "sctid": cp_drug.sctid,
+ "intravenous": (
+ cp_prescription_drug.intravenous
+ if cp_prescription_drug.intravenous != None
+ else False
+ ),
+ "group": _get_solution_group_key(
+ pd=cp_prescription_drug, is_cpoe=is_cpoe
+ ),
+ "expireDate": cp_prescription_expire_date.isoformat(),
+ "rx": False,
+ },
+ }
+ )
+
+ for a in allergies:
+ overlap_drugs.append(
+ {
+ "from": {
+ "id": str(prescription_drug.id),
+ "drug": drug.name,
+ "sctid": drug.sctid,
+ "intravenous": (
+ prescription_drug.intravenous
+ if prescription_drug.intravenous != None
+ else False
+ ),
+ "group": _get_solution_group_key(
+ pd=prescription_drug, is_cpoe=is_cpoe
+ ),
+ "expireDate": prescription_expire_date.isoformat(),
+ "rx": True,
+ },
+ "to": a,
+ }
+ )
+
+ if len(overlap_drugs) == 0:
+ return {"alerts": {}, "list": {}, "stats": {}}
+
+ uniq_overlap_keys = []
+ for d in overlap_drugs:
+ key = f"""({d["from"]["sctid"]},{d["to"]["sctid"]})"""
+ if key not in uniq_overlap_keys:
+ uniq_overlap_keys.append(key)
+
+ query = text(
+ f"""
+ with cruzamento as (
+ select * from (values {",".join(uniq_overlap_keys)}) AS t (sctida, sctidb)
+ )
+ select
+ r.sctida,
+ r.sctidb,
+ r.tprelacao as "kind",
+ r.texto as "text",
+ r.nivel as "level"
+ from
+ public.relacao r
+ inner join cruzamento c on (r.sctida = c.sctida and r.sctidb = c.sctidb)
+ where
+ r.ativo = true
+ """
+ )
+
+ active_relations = {}
+
+ for item in db.session.execute(query).all():
+ key = f"{item.sctida}-{item.sctidb}-{item.kind}"
+ active_relations[key] = {
+ "sctida": item.sctida,
+ "sctidb": item.sctidb,
+ "kind": item.kind,
+ "text": item.text,
+ "level": item.level,
+ }
+
+ alerts = {}
+ stats = {}
+ unique_relations = {}
+ kinds = ["it", "dt", "dm", "iy", "sl", "rx"]
+
+ for kind in kinds:
+ stats[kind] = 0
+
+ for drug in overlap_drugs:
+ drug_from = drug["from"]
+ drug_to = drug["to"]
+
+ for kind in kinds:
+ key = f"""{drug_from["sctid"]}-{drug_to["sctid"]}-{kind}"""
+ invert_key = f"""{drug_to["sctid"]}-{drug_from["sctid"]}-{kind}"""
+
+ # iy must have intravenous route
+ if kind == "iy" and (
+ not drug_from["intravenous"] or not drug_to["intravenous"]
+ ):
+ continue
+
+ # sl must be in the same group
+ if kind == "sl" and (
+ drug_from["group"] != drug_to["group"] or drug_from["group"] == None
+ ):
+ continue
+
+ # rx rules
+ if kind == "rx":
+ if not drug_from["rx"]:
+ continue
+ else:
+ if drug_from["rx"]:
+ continue
+
+ if key in active_relations:
+ if is_cpoe:
+ uniq_key = key
+ uniq_invert_key = invert_key
+ else:
+ uniq_key = f"""{key}-{drug_from["expireDate"]}"""
+ uniq_invert_key = f"""{invert_key}-{drug_from["expireDate"]}"""
+
+ if (
+ not uniq_key in unique_relations
+ and not uniq_invert_key in unique_relations
+ ):
+ stats[kind] += 1
+ unique_relations[uniq_key] = 1
+ unique_relations[uniq_invert_key] = 1
+
+ alert_text = typeRelations[kind] + ": "
+ alert_text += (
+ strNone(active_relations[key]["text"])
+ + " ("
+ + strNone(drug_from["drug"])
+ + " e "
+ + strNone(drug_to["drug"])
+ + ")"
+ )
+
+ if kind == "dm":
+ # one way
+ ids = [drug_from["id"]]
+ else:
+ # both ways
+ ids = [drug_from["id"], drug_to["id"]]
+
+ for id in ids:
+ alert_obj = {
+ "idPrescriptionDrug": id,
+ "key": key,
+ "type": kind,
+ "level": (
+ active_relations[key]["level"]
+ if active_relations[key]["level"] != None
+ else DrugAlertLevelEnum.LOW
+ ),
+ "relation": drug_to["id"],
+ "text": alert_text,
+ }
+
+ if id in alerts:
+ # avoid alert repetition
+ text_array = [a["text"] for a in alerts[id]]
+ if alert_text not in text_array:
+ alerts[id].append(alert_obj)
+ else:
+ alerts[id] = [alert_obj]
+
+ return {"alerts": alerts, "stats": stats}
+
+
+def _filter_drug_list(drug_list):
+ filtered_list = []
+ valid_sources = [
+ DrugTypeEnum.DRUG.value,
+ DrugTypeEnum.SOLUTION.value,
+ DrugTypeEnum.PROCEDURE.value,
+ ]
+
+ for item in drug_list:
+ prescription_drug: PrescriptionDrug = item[0]
+ drug: Drug = item[1]
+ if prescription_drug.source not in valid_sources:
+ continue
+
+ if prescription_drug.suspendedDate != None:
+ continue
+
+ if drug == None or drug.sctid == None:
+ continue
+
+ filtered_list.append(item)
+
+ return filtered_list
+
+
+def _get_solution_group_key(pd: PrescriptionDrug, is_cpoe: bool):
+ if is_cpoe:
+ if pd.cpoe_group:
+ return f"{pd.idPrescription}-{pd.cpoe_group}"
+ else:
+ if pd.solutionGroup:
+ return f"{pd.idPrescription}-{pd.solutionGroup}"
+
+ return None
+
+
+def _get_allergies(id_patient: int):
+ allergies = (
+ db.session.query(Substance.id, Substance.name)
+ .select_from(Allergy)
+ .join(Drug, Allergy.idDrug == Drug.id)
+ .join(Substance, Substance.id == Drug.sctid)
+ .filter(Allergy.idPatient == id_patient)
+ .filter(Allergy.active == True)
+ .group_by(Substance.id, Substance.name)
+ .all()
+ )
+
+ results = []
+ for a in allergies:
+ if a.id != None:
+ results.append(
+ {
+ "id": None,
+ "drug": a.name,
+ "sctid": a.id,
+ "intravenous": False,
+ "group": None,
+ "rx": True,
+ }
+ )
+
+ return results
diff --git a/services/alert_service.py b/services/alert_service.py
index f993e945..93dab53f 100644
--- a/services/alert_service.py
+++ b/services/alert_service.py
@@ -1,225 +1,721 @@
-from datetime import datetime
-from sqlalchemy import text
+import re
-from models.prescription import PrescriptionDrug, Allergy
-from models.main import db, Drug, Relation, Substance
-from models.enums import DrugTypeEnum
-from routes.utils import typeRelations, strNone
+from routes.utils import none2zero, strNone, strFormatBR
+from models.enums import DrugTypeEnum, DrugAlertTypeEnum, DrugAlertLevelEnum
+from models.prescription import (
+ PrescriptionDrug,
+ Drug,
+ DrugAttributes,
+)
-def find_relations(drug_list, id_patient: int, is_cpoe: bool):
+# analyze alerts
+# drug_list (PrescriptionDrug.findByPrescription)
+def find_alerts(drug_list, exams: dict, dialisys: str, pregnant: bool, lactating: bool):
filtered_list = _filter_drug_list(drug_list=drug_list)
- allergies = _get_allergies(id_patient=id_patient)
- overlap_drugs = []
+ dose_total = _get_dose_total(drug_list=filtered_list, exams=exams)
+ alerts = {}
+ stats = _get_empty_stats()
+
+ def add_alert(a):
+ if a != None:
+ key = a["idPrescriptionDrug"]
+ stats[a["type"]] += 1
+ a["text"] = re.sub(" {2,}", "", a["text"])
+ a["text"] = re.sub("\n", "", a["text"])
+
+ if key not in alerts:
+ alerts[key] = [a]
+ else:
+ alerts[key].append(a)
for item in filtered_list:
prescription_drug: PrescriptionDrug = item[0]
drug: Drug = item[1]
- prescription_date = item[13]
- prescription_expire_date = item[10] if item[10] != None else datetime.today()
-
- for compare_item in filtered_list:
- cp_prescription_drug: PrescriptionDrug = compare_item[0]
- cp_drug: Drug = compare_item[1]
- cp_prescription_date = compare_item[13]
- cp_prescription_expire_date = (
- compare_item[10] if compare_item[10] != None else datetime.today()
+ measure_unit_convert_factor = (
+ item.measure_unit_convert_factor
+ if item.measure_unit_convert_factor != None
+ else 1
+ )
+ drug_attributes: DrugAttributes = item[6]
+ prescription_expire_date = item[10]
+
+ # kidney alert
+ add_alert(
+ _alert_kidney(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
+ dialysis=dialisys,
)
+ )
- if prescription_drug.id == cp_prescription_drug.id:
- continue
+ # liver alert
+ add_alert(
+ _alert_liver(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
+ )
+ )
- if is_cpoe:
- # period overlap
- if not (
- (prescription_date.date() <= cp_prescription_expire_date.date())
- and (cp_prescription_date.date() <= prescription_expire_date.date())
- ):
- continue
- else:
- # same expire date
- if not (prescription_expire_date == cp_prescription_expire_date):
- continue
-
- overlap_drugs.append(
- {
- "from": {
- "id": prescription_drug.id,
- "drug": drug.name,
- "sctid": drug.sctid,
- "intravenous": (
- prescription_drug.intravenous
- if prescription_drug.intravenous != None
- else False
- ),
- "group": _get_solution_group_key(
- pd=prescription_drug, is_cpoe=is_cpoe
- ),
- "expireDate": prescription_expire_date.isoformat(),
- "rx": False,
- },
- "to": {
- "id": cp_prescription_drug.id,
- "drug": cp_drug.name,
- "sctid": cp_drug.sctid,
- "intravenous": (
- cp_prescription_drug.intravenous
- if cp_prescription_drug.intravenous != None
- else False
- ),
- "group": _get_solution_group_key(
- pd=cp_prescription_drug, is_cpoe=is_cpoe
- ),
- "expireDate": cp_prescription_expire_date.isoformat(),
- "rx": False,
- },
- }
+ # platelets
+ add_alert(
+ _alert_platelets(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
)
+ )
- for a in allergies:
- overlap_drugs.append(
- {
- "from": {
- "id": prescription_drug.id,
- "drug": drug.name,
- "sctid": drug.sctid,
- "intravenous": (
- prescription_drug.intravenous
- if prescription_drug.intravenous != None
- else False
- ),
- "group": _get_solution_group_key(
- pd=prescription_drug, is_cpoe=is_cpoe
- ),
- "expireDate": prescription_expire_date.isoformat(),
- "rx": True,
- },
- "to": a,
- }
+ # elderly
+ add_alert(
+ _alert_elderly(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
)
+ )
- if len(overlap_drugs) == 0:
- return {"alerts": {}, "list": {}, "stats": {}}
+ # tube
+ add_alert(
+ _alert_tube(
+ prescription_drug=prescription_drug, drug_attributes=drug_attributes
+ )
+ )
- uniq_overlap_keys = []
- for d in overlap_drugs:
- key = f"""({d["from"]["sctid"]},{d["to"]["sctid"]})"""
- if key not in uniq_overlap_keys:
- uniq_overlap_keys.append(key)
+ # allergy
+ add_alert(_alert_allergy(prescription_drug=prescription_drug))
- query = text(
- f"""
- with cruzamento as (
- select * from (values {",".join(uniq_overlap_keys)}) AS t (sctida, sctidb)
+ # maximum treatment period
+ add_alert(
+ _alert_max_time(
+ prescription_drug=prescription_drug, drug_attributes=drug_attributes
+ )
)
- select
- r.sctida,
- r.sctidb,
- r.tprelacao as "kind",
- r.texto as "text"
- from
- public.relacao r
- inner join cruzamento c on (r.sctida = c.sctida and r.sctidb = c.sctidb)
- where
- r.ativo = true
- """
- )
- active_relations = {}
+ # max dose
+ add_alert(
+ _alert_max_dose(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
+ measure_unit_convert_factor=measure_unit_convert_factor,
+ )
+ )
- for item in db.session.execute(query).all():
- key = f"{item.sctida}-{item.sctidb}-{item.kind}"
- active_relations[key] = {
- "sctida": item.sctida,
- "sctidb": item.sctidb,
- "kind": item.kind,
- "text": item.text,
- }
+ # max dose total
+ add_alert(
+ _alert_max_dose_total(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ exams=exams,
+ prescription_expire_date=prescription_expire_date,
+ dose_total=dose_total,
+ )
+ )
- alerts = {}
- relations = {}
+ # IRA
+ add_alert(
+ _alert_ira(
+ prescription_drug=prescription_drug,
+ drug=drug,
+ exams=exams,
+ prescription_expire_date=prescription_expire_date,
+ dose_total=dose_total,
+ dialysis=dialisys,
+ )
+ )
+
+ # pregnant
+ add_alert(
+ _alert_pregnant(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ pregnant=pregnant,
+ )
+ )
+
+ # lactating
+ add_alert(
+ _alert_lactating(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ lactating=lactating,
+ )
+ )
+
+ return {"alerts": alerts, "stats": stats}
+
+
+def _get_empty_stats():
stats = {}
- unique_relations = {}
- kinds = ["it", "dt", "dm", "iy", "sl", "rx"]
-
- for kind in kinds:
- stats[kind] = 0
-
- for drug in overlap_drugs:
- drug_from = drug["from"]
- drug_to = drug["to"]
-
- for kind in kinds:
- key = f"""{drug_from["sctid"]}-{drug_to["sctid"]}-{kind}"""
- invert_key = f"""{drug_to["sctid"]}-{drug_from["sctid"]}-{kind}"""
-
- # iy must have intravenous route
- if kind == "iy" and (
- not drug_from["intravenous"] or not drug_to["intravenous"]
- ):
- continue
-
- # sl must be in the same group
- if kind == "sl" and (drug_from["group"] != drug_to["group"]):
- continue
-
- # rx rules
- if kind == "rx":
- if not drug_from["rx"]:
- continue
- else:
- if drug_from["rx"]:
- continue
-
- if key in active_relations:
- if is_cpoe:
- uniq_key = key
- uniq_invert_key = invert_key
- else:
- uniq_key = f"""{key}-{drug_from["expireDate"]}"""
- uniq_invert_key = f"""{invert_key}-{drug_from["expireDate"]}"""
-
- if (
- not uniq_key in unique_relations
- and not uniq_invert_key in unique_relations
- ):
- stats[kind] += 1
- unique_relations[uniq_key] = 1
- unique_relations[uniq_invert_key] = 1
-
- alert = typeRelations[kind] + ": "
- alert += (
- strNone(active_relations[key]["text"])
- + " ("
- + strNone(drug_from["drug"])
- + " e "
- + strNone(drug_to["drug"])
- + ")"
- )
-
- relation = {
- "key": key,
- "kind": kind,
- "to": drug_to["id"],
- }
-
- if kind == "dm":
- # one way
- ids = [drug_from["id"]]
- else:
- # both ways
- ids = [drug_from["id"], drug_to["id"]]
-
- for id in ids:
- if id in alerts:
- relations[id].append(relation)
- if alert not in alerts[id]:
- alerts[id].append(alert)
- else:
- alerts[id] = [alert]
- relations[id] = [relation]
-
- return {"alerts": alerts, "list": relations, "stats": stats}
+ for t in DrugAlertTypeEnum:
+ stats[t.value] = 0
+
+ return stats
+
+
+def _create_alert(
+ id_prescription_drug: str,
+ key: str,
+ alert_type: DrugAlertTypeEnum,
+ alert_level: DrugAlertLevelEnum,
+ text: str,
+):
+ return {
+ "idPrescriptionDrug": id_prescription_drug,
+ "key": key,
+ "type": alert_type.value,
+ "level": alert_level.value,
+ "text": text,
+ }
+
+
+def _get_dose_conv(
+ prescription_drug: PrescriptionDrug,
+ drug_attributes: DrugAttributes,
+ measure_unit_convert_factor: float,
+):
+ pd_frequency = (
+ 1
+ if prescription_drug.frequency in [33, 44, 55, 66, 99]
+ else prescription_drug.frequency
+ )
+
+ if drug_attributes != None and drug_attributes.division != None:
+ return (
+ none2zero(prescription_drug.dose)
+ * (
+ measure_unit_convert_factor
+ if measure_unit_convert_factor != None
+ else 1
+ )
+ * none2zero(pd_frequency)
+ )
+
+ return none2zero(prescription_drug.doseconv) * none2zero(pd_frequency)
+
+
+def _get_dose_total(drug_list, exams: dict):
+ dose_total = {}
+ for item in drug_list:
+ prescription_drug: PrescriptionDrug = item[0]
+ drug_attributes: DrugAttributes = item[6]
+ prescription_expire_date = item[10]
+ expireDay = prescription_expire_date.day if prescription_expire_date else 0
+ measure_unit_convert_factor = (
+ item.measure_unit_convert_factor
+ if item.measure_unit_convert_factor != None
+ else 1
+ )
+ pd_dose_conv = _get_dose_conv(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ measure_unit_convert_factor=measure_unit_convert_factor,
+ )
+
+ if prescription_drug.frequency in [66]:
+ # do not sum some types of frequency
+ continue
+
+ idDrugAgg = str(prescription_drug.idDrug) + "_" + str(expireDay)
+ idDrugAggWeight = str(idDrugAgg) + "kg"
+
+ # dose
+ if idDrugAgg not in dose_total:
+ dose_total[idDrugAgg] = {"value": pd_dose_conv, "count": 1}
+ else:
+ dose_total[idDrugAgg]["value"] += pd_dose_conv
+ dose_total[idDrugAgg]["count"] += 1
+
+ # dose / kg
+ weight = none2zero(exams["weight"])
+ weight = weight if weight > 0 else 1
+ doseWeight = round(pd_dose_conv / float(weight), 2)
+
+ if idDrugAggWeight not in dose_total:
+ dose_total[idDrugAggWeight] = {"value": doseWeight, "count": 1}
+ else:
+ dose_total[idDrugAggWeight]["value"] += doseWeight
+ dose_total[idDrugAggWeight]["count"] += 1
+
+ return dose_total
+
+
+def _alert_ira(
+ prescription_drug: PrescriptionDrug,
+ drug: Drug,
+ exams: dict,
+ prescription_expire_date,
+ dose_total: dict,
+ dialysis: str,
+):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.IRA,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if drug and "vanco" in drug.name.lower():
+ expireDay = prescription_expire_date.day if prescription_expire_date else 0
+ idDrugAgg = str(prescription_drug.idDrug) + "_" + str(expireDay)
+ maxdose = dose_total[idDrugAgg]["value"]
+ ckd = (
+ exams["ckd"]["value"] if "ckd" in exams and exams["ckd"]["value"] else None
+ )
+ weight = exams["weight"]
+
+ if (
+ maxdose != None
+ and ckd != None
+ and weight != None
+ and ckd > 0
+ and weight > 0
+ ):
+ ira = maxdose / ckd / weight
+ maxira = 0.6219
+
+ if ira > maxira and dialysis is None:
+ alert[
+ "text"
+ ] = f"""
+ Risco de desenvolvimento de Insuficiência Renal Aguda (IRA), já que o resultado do cálculo
+ [dose diária de VANCOMICINA/TFG/peso] é superior a 0,6219. Caso o paciente esteja em diálise,
+ desconsiderar. Referência: CaEPS
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_max_dose(
+ prescription_drug: PrescriptionDrug,
+ drug_attributes: DrugAttributes,
+ exams: dict,
+ measure_unit_convert_factor: float,
+):
+ if not drug_attributes:
+ return None
+
+ pd_dose_conv = _get_dose_conv(
+ prescription_drug=prescription_drug,
+ drug_attributes=drug_attributes,
+ measure_unit_convert_factor=measure_unit_convert_factor,
+ )
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.MAX_DOSE,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if drug_attributes.useWeight and prescription_drug.dose:
+ weight = none2zero(exams["weight"])
+ weight = weight if weight > 0 else 1
+ doseWeight = round(pd_dose_conv / float(weight), 2)
+
+ if drug_attributes.maxDose and drug_attributes.maxDose < doseWeight:
+ alert[
+ "text"
+ ] = f"""
+ Dose diária prescrita ({strFormatBR(doseWeight)} {str(drug_attributes.idMeasureUnit)}/Kg)
+ maior que a dose de alerta
+ ({strFormatBR(drug_attributes.maxDose)} {str(drug_attributes.idMeasureUnit)}/Kg)
+ usualmente recomendada (considerada a dose diária independente da indicação).
+ """
+
+ return alert
+
+ else:
+ if drug_attributes.maxDose and drug_attributes.maxDose < pd_dose_conv:
+ alert[
+ "text"
+ ] = f"""
+ Dose diária prescrita ({str(pd_dose_conv)} {str(drug_attributes.idMeasureUnit)})
+ maior que a dose de alerta ({str(drug_attributes.maxDose)} {str(drug_attributes.idMeasureUnit)})
+ usualmente recomendada (considerada a dose diária independente da indicação).
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_max_dose_total(
+ prescription_drug: PrescriptionDrug,
+ drug_attributes: DrugAttributes,
+ exams: dict,
+ prescription_expire_date,
+ dose_total,
+):
+ if not drug_attributes:
+ return None
+
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.MAX_DOSE,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ expireDay = prescription_expire_date.day if prescription_expire_date else 0
+ idDrugAgg = str(prescription_drug.idDrug) + "_" + str(expireDay)
+ idDrugAggWeight = str(idDrugAgg) + "kg"
+
+ if drug_attributes.useWeight and prescription_drug.dose:
+ weight = none2zero(exams["weight"])
+ weight = weight if weight > 0 else 1
+
+ if (
+ drug_attributes.maxDose
+ and idDrugAggWeight in dose_total
+ and dose_total[idDrugAggWeight]["count"] > 1
+ and drug_attributes.maxDose
+ < none2zero(dose_total[idDrugAggWeight]["value"])
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Dose diária prescrita SOMADA (
+ {str(dose_total[idDrugAggWeight]["value"])} {str(drug_attributes.idMeasureUnit)}/Kg) maior
+ que a dose de alerta (
+ {str(drug_attributes.maxDose)} {str(drug_attributes.idMeasureUnit)}/Kg)
+ usualmente recomendada (Frequência "AGORA" não é considerada no cálculo)."
+ """
+
+ return alert
+
+ else:
+ if (
+ drug_attributes.maxDose
+ and idDrugAgg in dose_total
+ and dose_total[idDrugAgg]["count"] > 1
+ and drug_attributes.maxDose < none2zero(dose_total[idDrugAgg]["value"])
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Dose diária prescrita SOMADA (
+ {str(dose_total[idDrugAgg]["value"])} {str(drug_attributes.idMeasureUnit)}) maior que a
+ dose de alerta ({str(drug_attributes.maxDose)} {str(drug_attributes.idMeasureUnit)})
+ usualmente recomendada (Frequência "AGORA" não é considerada no cálculo).
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_max_time(
+ prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes
+):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.MAX_TIME,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if (
+ drug_attributes
+ and drug_attributes.maxTime
+ and prescription_drug.period
+ and prescription_drug.period > drug_attributes.maxTime
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Tempo de tratamento atual ({str(prescription_drug.period)} dias) maior que o tempo máximo de tratamento (
+ {str(drug_attributes.maxTime)} dias) usualmente recomendado.
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_pregnant(
+ prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes, pregnant: bool
+):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.PREGNANT,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if pregnant and drug_attributes != None:
+ if drug_attributes.pregnant == "D" or drug_attributes.pregnant == "X":
+ alert["text"] = (
+ f"Paciente gestante com medicamento classificado como {drug_attributes.pregnant} prescrito. Avaliar manutenção deste medicamento com a equipe médica."
+ )
+ alert["level"] = (
+ DrugAlertLevelEnum.HIGH.value
+ if drug_attributes.pregnant == "X"
+ else DrugAlertLevelEnum.MEDIUM.value
+ )
+
+ return alert
+
+ return None
+
+
+def _alert_lactating(
+ prescription_drug: PrescriptionDrug,
+ drug_attributes: DrugAttributes,
+ lactating: bool,
+):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.LACTATING,
+ alert_level=DrugAlertLevelEnum.MEDIUM,
+ text="",
+ )
+
+ if lactating and drug_attributes != None and drug_attributes.lactating == "3":
+ alert["text"] = (
+ f"""Paciente amamentando com medicamento classificado como Alto risco prescrito.
+ Avaliar manutenção deste medicamento com a equipe médica ou cessação da amamentação."""
+ )
+
+ return alert
+
+ return None
+
+
+def _alert_allergy(prescription_drug: PrescriptionDrug):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.ALLERGY,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if prescription_drug.allergy == "S":
+ alert["text"] = "Paciente alérgico a este medicamento."
+
+ return alert
+
+ return None
+
+
+def _alert_tube(prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes):
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.TUBE,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if drug_attributes and drug_attributes.tube and prescription_drug.tube:
+ alert["text"] = (
+ f"""Medicamento contraindicado via sonda ({strNone(prescription_drug.route)})"""
+ )
+
+ return alert
+
+ return None
+
+
+def _alert_elderly(
+ prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes, exams: dict
+):
+ if not drug_attributes or not exams:
+ return None
+
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.ELDERLY,
+ alert_level=DrugAlertLevelEnum.LOW,
+ text="",
+ )
+
+ if drug_attributes.elderly and exams["age"] > 60:
+ alert[
+ "text"
+ ] = f"""
+ Medicamento potencialmente inapropriado para idosos, independente das comorbidades do paciente.
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_platelets(
+ prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes, exams: dict
+):
+ if not drug_attributes or not exams:
+ return None
+
+ if not drug_attributes.platelets:
+ return None
+
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.PLATELETS,
+ alert_level=DrugAlertLevelEnum.HIGH,
+ text="",
+ )
+
+ if (
+ "plqt" in exams
+ and exams["plqt"]["value"]
+ and drug_attributes.platelets > exams["plqt"]["value"]
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Medicamento contraindicado para paciente com plaquetas ({str(exams["plqt"]["value"])} plaquetas/µL)
+ abaixo de {str(drug_attributes.platelets)} plaquetas/µL.
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_liver(
+ prescription_drug: PrescriptionDrug, drug_attributes: DrugAttributes, exams: dict
+):
+ if not drug_attributes or not exams:
+ return None
+
+ if not drug_attributes.liver:
+ return None
+
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.LIVER,
+ alert_level=DrugAlertLevelEnum.MEDIUM,
+ text="",
+ )
+
+ if (
+ "tgp" in exams
+ and exams["tgp"]["value"]
+ and float(exams["tgp"]["value"]) > drug_attributes.liver
+ ) or (
+ "tgo" in exams
+ and exams["tgo"]["value"]
+ and float(exams["tgo"]["value"]) > drug_attributes.liver
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função hepática do paciente
+ está reduzida (acima de {str(drug_attributes.liver)} U/L).
+ """
+
+ return alert
+
+ return None
+
+
+def _alert_kidney(
+ prescription_drug: PrescriptionDrug,
+ drug_attributes: DrugAttributes,
+ exams: dict,
+ dialysis: str,
+):
+ if not drug_attributes or not exams:
+ return None
+
+ if not drug_attributes.kidney:
+ return None
+
+ alert = _create_alert(
+ id_prescription_drug=str(prescription_drug.id),
+ key="",
+ alert_type=DrugAlertTypeEnum.KIDNEY,
+ alert_level=DrugAlertLevelEnum.MEDIUM,
+ text="",
+ )
+
+ if dialysis == "c":
+ alert["text"] = (
+ "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise contínua."
+ )
+
+ return alert
+
+ if dialysis == "x":
+ alert["text"] = (
+ "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise estendida, também conhecida como SLED."
+ )
+ return alert
+
+ if dialysis == "v":
+ alert["text"] = (
+ "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise intermitente."
+ )
+ return alert
+
+ if dialysis == "p":
+ alert["text"] = (
+ "Medicamento é contraindicado ou deve sofrer ajuste de posologia, já que o paciente está em diálise peritoneal."
+ )
+ return alert
+
+ if (
+ "ckd" in exams
+ and exams["ckd"]["value"]
+ and drug_attributes.kidney > exams["ckd"]["value"]
+ and exams["age"] > 17
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente (
+ {str(exams["ckd"]["value"])} mL/min) está abaixo de {str(drug_attributes.kidney)} mL/min.
+ """
+ return alert
+
+ if (
+ "swrtz2" in exams
+ and exams["swrtz2"]["value"]
+ and drug_attributes.kidney > exams["swrtz2"]["value"]
+ and exams["age"] <= 17
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente
+ ({str(exams["swrtz2"]["value"])} mL/min/1.73m²) está abaixo de
+ {str(drug_attributes.kidney)} mL/min. (Schwartz 2)
+ """
+ return alert
+
+ if (
+ "swrtz1" in exams
+ and exams["swrtz1"]["value"]
+ and drug_attributes.kidney > exams["swrtz1"]["value"]
+ and exams["age"] <= 17
+ ):
+ alert[
+ "text"
+ ] = f"""
+ Medicamento deve sofrer ajuste de posologia ou contraindicado, já que a função renal do paciente
+ ({str(exams["swrtz1"]["value"])} mL/min/1.73m²) está abaixo de {str(drug_attributes.kidney)}
+ mL/min. (Schwartz 1)
+ """
+ return alert
+
+ return None
def _filter_drug_list(drug_list):
@@ -232,52 +728,13 @@ def _filter_drug_list(drug_list):
for item in drug_list:
prescription_drug: PrescriptionDrug = item[0]
- drug: Drug = item[1]
+
if prescription_drug.source not in valid_sources:
continue
if prescription_drug.suspendedDate != None:
continue
- if drug == None or drug.sctid == None:
- continue
-
filtered_list.append(item)
return filtered_list
-
-
-def _get_solution_group_key(pd: PrescriptionDrug, is_cpoe: bool):
- if is_cpoe:
- return f"{pd.idPrescription}-{pd.cpoe_group}"
- else:
- return f"{pd.idPrescription}-{pd.solutionGroup}"
-
-
-def _get_allergies(id_patient: int):
- allergies = (
- db.session.query(Substance.id, Substance.name)
- .select_from(Allergy)
- .join(Drug, Allergy.idDrug == Drug.id)
- .join(Substance, Substance.id == Drug.sctid)
- .filter(Allergy.idPatient == id_patient)
- .filter(Allergy.active == True)
- .group_by(Substance.id, Substance.name)
- .all()
- )
-
- results = []
- for a in allergies:
- if a.id != None:
- results.append(
- {
- "id": None,
- "drug": a.name,
- "sctid": a.id,
- "intravenous": False,
- "group": None,
- "rx": True,
- }
- )
-
- return results
diff --git a/services/prescription_service.py b/services/prescription_service.py
index e9766405..ce345263 100644
--- a/services/prescription_service.py
+++ b/services/prescription_service.py
@@ -87,7 +87,7 @@ def search(search_key):
)
-def check_prescription(idPrescription, p_status, user, evaluation_time):
+def check_prescription(idPrescription, p_status, user, evaluation_time, alerts):
roles = user.config["roles"] if user.config and "roles" in user.config else []
if not permission_service.is_pharma(user):
raise ValidationError(
@@ -136,6 +136,7 @@ def check_prescription(idPrescription, p_status, user, evaluation_time):
else None
),
"evaluationTime": evaluation_time or 0,
+ "alerts": alerts,
}
results = []