Skip to content

Commit

Permalink
Merge pull request #363 from noharm-ai/develop
Browse files Browse the repository at this point in the history
v3.26-beta
  • Loading branch information
marceloarocha authored Aug 26, 2024
2 parents c087a16 + 9981ce2 commit 036ba8b
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 19 deletions.
2 changes: 1 addition & 1 deletion mobile.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@

@app.route("/version", methods=["GET"])
def getVersion():
return {"status": "success", "data": "v3.25-beta"}, status.HTTP_200_OK
return {"status": "success", "data": "v3.26-beta"}, status.HTTP_200_OK


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion requirements-prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ SQLAlchemy==2.0.29
Flask-SQLAlchemy==3.1.1
troposphere
zappa
flask-cors==4.0.0
flask-cors==4.0.1
psycopg2-binary
pandas==1.3.4
scipy==1.10.0
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Flask-SQLAlchemy==3.1.1
lambda-packages
troposphere
zappa
flask-cors==4.0.0
flask-cors==4.0.1
psycopg2-binary
pytest==5.4.1
pytest-flask
Expand Down
64 changes: 57 additions & 7 deletions services/outlier_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from services.admin import drug_service, integration_status_service
from services import data_authorization_service, permission_service

FOLD_SIZE = 15
FOLD_SIZE = 10


def prepare(id_drug, id_segment, user):
Expand Down Expand Up @@ -69,8 +69,9 @@ def add_history_and_validate():
return refresh_outliers(id_drug=id_drug, id_segment=id_segment, user=user)


def generate(id_drug, id_segment, fold, user):
def generate(id_drug, id_segment, fold, user: User):
# call prepare before generate score (only for wizard)
start_date = datetime.now()

if fold != None:
if not permission_service.has_maintainer_permission(user):
Expand All @@ -92,6 +93,9 @@ def generate(id_drug, id_segment, fold, user):
csv_buffer = _get_csv_buffer(
id_segment=id_segment, schema=user.schema, id_drug=id_drug, fold=fold
)
_log_perf(start_date, "GENERATE CSV BUFFER")

start_date = datetime.now()

manager = Manager()
drugs = pandas.read_csv(csv_buffer)
Expand All @@ -102,6 +106,10 @@ def generate(id_drug, id_segment, fold, user):
.all()
)

_log_perf(start_date, "GET OUTLIERS LIST")

start_date = datetime.now()

with Manager() as manager:
poolDict = manager.dict()

Expand Down Expand Up @@ -130,19 +138,53 @@ def generate(id_drug, id_segment, fold, user):
for drug in poolDict:
new_os = new_os.append(poolDict[drug])

_log_perf(start_date, "PROCESS SCORES")

start_date = datetime.now()

updates = []
for o in outliers:
no = new_os[
(new_os["medication"] == o.idDrug)
& (new_os["dose"] == o.dose)
& (new_os["frequency"] == o.frequency)
]
if len(no) > 0:
o.score = no["score"].values[0]
o.countNum = int(no["count"].values[0])
o.update = datetime.today()
o.user = user.id
updates.append(
_to_update_row(
{
"id": o.id,
"score": no["score"].values[0],
"countNum": int(no["count"].values[0]),
},
user,
)
)

db.session.flush()
if len(updates) > 0:
update_stmt = f"""
with scores as (
select * from (values {",".join(updates)}) AS t (idoutlier, score, countnum, update_at, update_by)
)
update {user.schema}.outlier o
set
contagem = s.countnum,
escore = s.score,
update_at = s.update_at,
update_by = s.update_by
from
scores s
where
s.idoutlier = o.idoutlier
"""

db.session.execute(text(update_stmt))

_log_perf(start_date, "UPDATE SCORES")


def _to_update_row(update: dict, user: User):
return f"""({update["id"]}, {update["score"]}, {update["countNum"]}, '{datetime.today().isoformat()}'::timestamp, {user.id})"""


def refresh_outliers(id_segment, user, id_drug=None):
Expand Down Expand Up @@ -225,6 +267,14 @@ def _get_csv_buffer(id_segment, schema, id_drug=None, fold=None):
return csv_buffer


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 _compute_outlier(idDrug, drugsItem, poolDict, fold):
print("Starting...", fold, idDrug)
poolDict[idDrug] = add_score(drugsItem)
Expand Down
28 changes: 20 additions & 8 deletions services/prescription_agg_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ def create_agg_prescription_by_prescription(
if p.idSegment is None:
return

if not force and not _has_new_itens(id_prescription):
processed_status = _get_processed_status(id_prescription=id_prescription)

if not force and processed_status == "PROCESSED":
return

resultPresc, stat = getPrescription(idPrescription=id_prescription)
Expand Down Expand Up @@ -103,7 +105,8 @@ def create_agg_prescription_by_prescription(
prescription=pAgg, user=prescalc_user, extra={"prescalc": True}
)

if p.status == "s":
# if it was checked before this process, keep it
if p.status == "s" and processed_status == "NEW_ITENS":
p.update = datetime.today()
p.user = None
p.status = 0
Expand Down Expand Up @@ -282,9 +285,11 @@ def get_date_range(p):
return [start_date + timedelta(days=x) for x in range((end_date - start_date).days)]


def _has_new_itens(id_prescription: int):
def _get_processed_status(id_prescription: int):
query = (
select(PrescriptionDrug.id, func.count(PrescriptionDrugAudit.id))
select(
PrescriptionDrug.id, func.count(PrescriptionDrugAudit.id).label("p_count")
)
.select_from(PrescriptionDrug)
.outerjoin(
PrescriptionDrugAudit,
Expand All @@ -296,12 +301,19 @@ def _has_new_itens(id_prescription: int):
)
.where(PrescriptionDrug.idPrescription == id_prescription)
.group_by(PrescriptionDrug.id)
.having(func.count(PrescriptionDrugAudit.id) == 0)
)

results = db.session.execute(query).all()

if len(results) > 0:
return True
not_processed_count = 0
for r in results:
if r.p_count == 0:
not_processed_count += 1

if not_processed_count == len(results):
return "NEW_PRESCRIPTION"

if not_processed_count > 0:
return "NEW_ITENS"

return False
return "PROCESSED"
80 changes: 79 additions & 1 deletion tests/test_alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,85 @@ def test_allergy():

assert stats.get("allergy", 0) == 2

## _alert_ira ---- NAO FIZ , DIFICIL -- dose_total[idDrugAgg]
# 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.append(
_get_mock_row(id_prescription_drug=61,dose=10, frequency=10,drug_name = "xxx vancoyyy")
)

exams = {'ckd' : {'value' : 1.}, 'weight': 80 }

alerts = alert_service.find_alerts(
drug_list=drugs,
exams=exams,
dialisys= None,
pregnant=None,
lactating=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

### TESTS FOR ALERT IRA CONDITIONS - MULTIPLE SCENARIOS
# 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)
])
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

drugs = []
drugs.append(
_get_mock_row(
id_prescription_drug=61,
dose=dose,
frequency=1,
drug_name="Vancomicina"
)
)

exams = {'ckd': {'value': ckd}, 'weight': weight}

alerts = alert_service.find_alerts(
drug_list=drugs,
exams=exams,
dialisys=dialysis,
pregnant=None,
lactating=None,
schedules_fasting=None,
)

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"
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)}"



def test_pregnant():
Expand Down

0 comments on commit 036ba8b

Please sign in to comment.