diff --git a/mobile.py b/mobile.py index ff9b64ef..baea2b54 100644 --- a/mobile.py +++ b/mobile.py @@ -116,7 +116,7 @@ @app.route("/version", methods=["GET"]) def getVersion(): - return {"status": "success", "data": "v3.21-beta"}, status.HTTP_200_OK + return {"status": "success", "data": "v3.22-beta"}, status.HTTP_200_OK if __name__ == "__main__": diff --git a/models/prescription.py b/models/prescription.py index bb90061e..b5ddf7ac 100644 --- a/models/prescription.py +++ b/models/prescription.py @@ -313,6 +313,7 @@ def getPatients( drugAttributes=[], idPatient=[], intervals=[], + prescriber=None, ): q = ( db.session.query( @@ -491,6 +492,9 @@ def getPatients( ) ) + if prescriber != None: + q = q.filter(Prescription.prescriber.ilike(f"%{prescriber}%")) + if endDate is None: endDate = startDate diff --git a/routes/admin/exam.py b/routes/admin/exam.py index 1f6357fb..1a50f7e2 100644 --- a/routes/admin/exam.py +++ b/routes/admin/exam.py @@ -49,6 +49,43 @@ def get_most_frequent(): }, status.HTTP_200_OK +@app_admin_exam.route("/admin/exam/list", methods=["POST"]) +@jwt_required() +def list_exams(): + user = User.find(get_jwt_identity()) + dbSession.setSchema(user.schema) + data = request.get_json() + + try: + list = exam_service.get_segment_exams( + user=user, id_segment=data.get("idSegment", None) + ) + except ValidationError as e: + return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus + + return { + "status": "success", + "data": list, + }, status.HTTP_200_OK + + +@app_admin_exam.route("/admin/exam/types", methods=["GET"]) +@jwt_required() +def list_exam_types(): + user = User.find(get_jwt_identity()) + dbSession.setSchema(user.schema) + + try: + list = exam_service.get_exam_types() + except ValidationError as e: + return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus + + return { + "status": "success", + "data": list, + }, status.HTTP_200_OK + + @app_admin_exam.route("/admin/exam/most-frequent/add", methods=["POST"]) @jwt_required() def add_most_frequent(): @@ -67,3 +104,37 @@ def add_most_frequent(): return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus return tryCommit(db, True) + + +@app_admin_exam.route("/admin/exam/upsert", methods=["POST"]) +@jwt_required() +def upsert_seg_exam(): + user = User.find(get_jwt_identity()) + dbSession.setSchema(user.schema) + data = request.get_json() + + try: + result = exam_service.upsert_seg_exam(data=data, user=user) + except ValidationError as e: + return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus + + return tryCommit(db, result) + + +@app_admin_exam.route("/admin/exam/order", methods=["POST"]) +@jwt_required() +def set_exams_order(): + user = User.find(get_jwt_identity()) + dbSession.setSchema(user.schema) + data = request.get_json() + + try: + result = exam_service.set_exams_order( + exams=data.get("exams", None), + id_segment=data.get("idSegment", None), + user=user, + ) + except ValidationError as e: + return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus + + return tryCommit(db, result) diff --git a/routes/drugList.py b/routes/drugList.py index e01ea9a6..3e9700f7 100644 --- a/routes/drugList.py +++ b/routes/drugList.py @@ -3,6 +3,7 @@ from .utils import * from models.appendix import * from utils.dateutils import to_iso +from services import drug_service def _get_legacy_alert(kind): @@ -327,7 +328,7 @@ def getDrugType(self, pDrugs, source): "cpoe_group": pd[0].cpoe_group, "infusionKey": self.getInfusionKey(pd), "formValues": pd[0].form, - "drugAttributes": self.getDrugAttributes(pd), + "drugAttributes": drug_service.to_dict(pd[6]), "prescriptionDate": to_iso(pd.prescription_date), } ) @@ -340,15 +341,6 @@ def getInfusionKey(self, pd): return str(pd[0].idPrescription) + str(pd[0].solutionGroup) - def getDrugAttributes(self, pd): - attr_list = get_bool_drug_attributes_list() - attributes = {} - - for a in attr_list: - attributes[a] = 1 if pd[6] and getattr(pd[6], a) else 0 - - return attributes - def getInfusionList(self): result = {} for pd in self.drugList: diff --git a/routes/prescription.py b/routes/prescription.py index c9132ebf..c12b22ce 100644 --- a/routes/prescription.py +++ b/routes/prescription.py @@ -70,6 +70,7 @@ def getPrescriptions(): substanceClasses = request.args.getlist("substanceClasses[]") patientStatus = request.args.get("patientStatus", None) patientReviewType = request.args.get("patientReviewType", None) + prescriber = request.args.get("prescriber", None) patients = Patient.getPatients( idSegment=idSegment, @@ -94,6 +95,7 @@ def getPrescriptions(): drugAttributes=drugAttributes, idPatient=idPatient, intervals=intervals, + prescriber=prescriber, ) results = [] diff --git a/routes/segment.py b/routes/segment.py index 69bfb9e2..d9729382 100644 --- a/routes/segment.py +++ b/routes/segment.py @@ -5,14 +5,12 @@ get_jwt_identity, ) from sqlalchemy import asc, func -from .utils import tryCommit -from datetime import date, timedelta from utils import status from models.main import * from models.appendix import * from models.prescription import * -from services import exams_service, segment_service +from services import segment_service from exception.validation_error import ValidationError app_seg = Blueprint("app_seg", __name__) @@ -143,27 +141,6 @@ def getSegmentsId(idSegment, idHospital=None): }, status.HTTP_200_OK -@app_seg.route("/segments/exams/types", methods=["GET"]) -@jwt_required() -def getCodes(): - user = User.find(get_jwt_identity()) - dbSession.setSchema(user.schema) - - typesExam = ( - db.session.query(Exams.typeExam) - .filter(Exams.date > (date.today() - timedelta(days=30))) - .group_by(Exams.typeExam) - .order_by(asc(Exams.typeExam)) - .all() - ) - - results = ["mdrd", "ckd", "ckd21", "cg", "swrtz2", "swrtz1"] - for t in typesExam: - results.append(t[0].lower()) - - return {"status": "success", "data": {"types": results}}, status.HTTP_200_OK - - @app_seg.route("/segments/exams/refs", methods=["GET"]) @jwt_required() def getRefs(): @@ -193,37 +170,3 @@ def getRefs(): ) return {"status": "success", "data": results}, status.HTTP_200_OK - - -@app_seg.route("/segments//exams", methods=["PUT"]) -@jwt_required() -def setExams(idSegment): - user = User.find(get_jwt_identity()) - dbSession.setSchema(user.schema) - data = request.get_json() - - try: - result = exams_service.upsert_seg_exam( - data=data, id_segment=idSegment, user=user - ) - except ValidationError as e: - return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus - - return tryCommit(db, escape_html(result.typeExam)) - - -@app_seg.route("/segments//exams-order", methods=["PUT"]) -@jwt_required() -def setExamsOrder(idSegment): - user = User.find(get_jwt_identity()) - dbSession.setSchema(user.schema) - data = request.get_json() - - try: - result = exams_service.exams_reorder( - exams=data.get("exams", None), id_segment=idSegment, user=user - ) - except ValidationError as e: - return {"status": "error", "message": str(e), "code": e.code}, e.httpStatus - - return tryCommit(db, result) diff --git a/routes/utils.py b/routes/utils.py index e84dc65e..74b996ae 100644 --- a/routes/utils.py +++ b/routes/utils.py @@ -448,7 +448,7 @@ def tryCommit(db, recId, allow=True): }, status.HTTP_500_INTERNAL_SERVER_ERROR -def get_bool_drug_attributes_list(): +def get_numeric_drug_attributes_list(): return [ "antimicro", "mav", @@ -461,6 +461,7 @@ def get_bool_drug_attributes_list(): "chemo", "dialyzable", "fasting", + "fallRisk", ] @@ -480,7 +481,7 @@ def getFeatures(result, agg_date: datetime = None, intervals_for_agg_date=False) alert_levels = [] alert_level = "low" - for attr in get_bool_drug_attributes_list(): + for attr in get_numeric_drug_attributes_list(): drug_attributes[attr] = 0 for d in drugList: @@ -490,10 +491,10 @@ def getFeatures(result, agg_date: datetime = None, intervals_for_agg_date=False) if d["idSubstanceClass"] != None: substanceClassIDs.append(d["idSubstanceClass"]) - if "drugAttributes" in d: + if "drugAttributes" in d and d["drugAttributes"] != None: for attr in d["drugAttributes"]: if attr in drug_attributes: - drug_attributes[attr] += int(d["drugAttributes"][attr]) + drug_attributes[attr] += int(none2zero(d["drugAttributes"][attr])) if d["whiteList"] or d["suspended"]: continue diff --git a/services/admin/exam_service.py b/services/admin/exam_service.py index 7801ce9d..c661a9ed 100644 --- a/services/admin/exam_service.py +++ b/services/admin/exam_service.py @@ -7,10 +7,43 @@ from models.segment import * from models.enums import RoleEnum from services.admin import integration_service +from services import data_authorization_service from exception.validation_error import ValidationError +def get_segment_exams(id_segment: int, user: User): + segExams = ( + db.session.query(SegmentExam, Segment) + .filter(SegmentExam.idSegment == id_segment) + .join(Segment, Segment.id == SegmentExam.idSegment) + .order_by(asc(SegmentExam.name)) + .all() + ) + + exams = [] + for e in segExams: + se: SegmentExam = e[0] + s: Segment = e[1] + + exams.append( + { + "idSegment": s.id, + "segment": s.description, + "type": se.typeExam.lower(), + "initials": se.initials, + "name": se.name, + "min": se.min, + "max": se.max, + "ref": se.ref, + "order": se.order, + "active": se.active, + } + ) + + return exams + + def copy_exams(id_segment_origin, id_segment_destiny, user): roles = user.config["roles"] if user.config and "roles" in user.config else [] if RoleEnum.ADMIN.value not in roles and RoleEnum.TRAINING.value not in roles: @@ -137,3 +170,131 @@ def add_most_frequent(user, exam_types, id_segment): se.order = 99 db.session.add(se) + + +def get_exam_types(): + typesExam = ( + db.session.query(Exams.typeExam) + .filter(Exams.date > (date.today() - timedelta(days=30))) + .group_by(Exams.typeExam) + .order_by(asc(Exams.typeExam)) + .all() + ) + + results = ["mdrd", "ckd", "ckd21", "cg", "swrtz2", "swrtz1"] + for t in typesExam: + results.append(t[0].lower()) + + return results + + +def upsert_seg_exam(data: dict, user: User): + id_segment = data.get("idSegment", None) + typeExam = data.get("type", None) + + if id_segment == None or typeExam == None: + raise ValidationError( + "Parametros inválidos", + "errors.businessRules", + status.HTTP_400_BAD_REQUEST, + ) + + if not data_authorization_service.has_segment_authorization( + id_segment=id_segment, user=user + ): + raise ValidationError( + "Usuário não autorizado neste segmento", + "errors.businessRules", + status.HTTP_401_UNAUTHORIZED, + ) + + segExam = ( + db.session.query(SegmentExam) + .filter(SegmentExam.idSegment == id_segment) + .filter(SegmentExam.typeExam == typeExam) + .first() + ) + + if data.get("new", False) == True and segExam != None: + raise ValidationError( + "Este exame já foi cadastrado", + "errors.businessRules", + status.HTTP_401_UNAUTHORIZED, + ) + + newSegExam = False + if segExam is None: + newSegExam = True + segExam = SegmentExam() + segExam.idSegment = id_segment + segExam.typeExam = typeExam + + if "initials" in data.keys(): + segExam.initials = data.get("initials", None) + if "name" in data.keys(): + segExam.name = data.get("name", None) + if "min" in data.keys(): + segExam.min = data.get("min", None) + if "max" in data.keys(): + segExam.max = data.get("max", None) + if "ref" in data.keys(): + segExam.ref = data.get("ref", None) + if "order" in data.keys(): + segExam.order = data.get("order", None) + if "active" in data.keys(): + segExam.active = bool(data.get("active", False)) + + segExam.update = datetime.today() + segExam.user = user.id + + if newSegExam: + db.session.add(segExam) + + segment = db.session.query(Segment).filter(Segment.id == id_segment).first() + + return { + "idSegment": segment.id, + "segment": segment.description, + "type": segExam.typeExam.lower(), + "initials": segExam.initials, + "name": segExam.name, + "min": segExam.min, + "max": segExam.max, + "ref": segExam.ref, + "order": segExam.order, + "active": segExam.active, + } + + +def set_exams_order(exams, id_segment, user: User): + if not exams or not id_segment: + raise ValidationError( + "Parametros inválidos", + "errors.businessRules", + status.HTTP_400_BAD_REQUEST, + ) + + if not data_authorization_service.has_segment_authorization( + id_segment=id_segment, user=user + ): + raise ValidationError( + "Usuário não autorizado neste segmento", + "errors.businessRules", + status.HTTP_401_UNAUTHORIZED, + ) + + db.session.query(SegmentExam).filter(SegmentExam.idSegment == id_segment).update( + {"order": 99}, synchronize_session="fetch" + ) + + for idx, type in enumerate(exams): + exam = ( + db.session.query(SegmentExam) + .filter(SegmentExam.idSegment == id_segment) + .filter(SegmentExam.typeExam == type) + .first() + ) + + if exam: + exam.order = idx + db.session.flush() diff --git a/services/drug_service.py b/services/drug_service.py index 1ab930a9..81627266 100644 --- a/services/drug_service.py +++ b/services/drug_service.py @@ -172,7 +172,7 @@ def get_attributes(id_segment, id_drug, user): if attr == None: return {"drugRef": drug_ref} - result = _to_dict(attr) + result = to_dict(attr) result["sctid"] = str(drug.sctid) result["drugRef"] = drug_ref @@ -425,7 +425,10 @@ def create_attributes_from_reference(id_drug, id_segment, user): return None -def _to_dict(attr: DrugAttributes): +def to_dict(attr: DrugAttributes): + if attr == None: + return None + return { "antimicro": attr.antimicro, "mav": attr.mav, @@ -462,7 +465,7 @@ def _audit( audit.idDrug = drug_attributes.idDrug audit.idSegment = drug_attributes.idSegment audit.auditType = audit_type.value - audit.extra = _to_dict(drug_attributes) + audit.extra = to_dict(drug_attributes) audit.createdAt = datetime.today() audit.createdBy = user.id diff --git a/services/exams_service.py b/services/exams_service.py index 3a8cbbcb..a6b81dd0 100644 --- a/services/exams_service.py +++ b/services/exams_service.py @@ -41,96 +41,6 @@ def get_next_id(schema): return ([row[0] for row in result])[0] -def upsert_seg_exam(data: dict, id_segment: int, user: User): - if id_segment == None: - raise ValidationError( - "Parametros inválidos", - "errors.businessRules", - status.HTTP_400_BAD_REQUEST, - ) - - if not data_authorization_service.has_segment_authorization( - id_segment=id_segment, user=user - ): - raise ValidationError( - "Usuário não autorizado neste segmento", - "errors.businessRules", - status.HTTP_401_UNAUTHORIZED, - ) - - typeExam = data.get("type", None) - segExam = ( - db.session.query(SegmentExam) - .filter(SegmentExam.idSegment == id_segment) - .filter(SegmentExam.typeExam == typeExam) - .first() - ) - - newSegExam = False - if segExam is None: - newSegExam = True - segExam = SegmentExam() - segExam.idSegment = id_segment - segExam.typeExam = typeExam - - if "initials" in data.keys(): - segExam.initials = data.get("initials", None) - if "name" in data.keys(): - segExam.name = data.get("name", None) - if "min" in data.keys(): - segExam.min = data.get("min", None) - if "max" in data.keys(): - segExam.max = data.get("max", None) - if "ref" in data.keys(): - segExam.ref = data.get("ref", None) - if "order" in data.keys(): - segExam.order = data.get("order", None) - if "active" in data.keys(): - segExam.active = bool(data.get("active", False)) - - segExam.update = datetime.today() - segExam.user = user.id - - if newSegExam: - db.session.add(segExam) - - return segExam - - -def exams_reorder(exams, id_segment, user: User): - if not exams: - raise ValidationError( - "Parametros inválidos", - "errors.businessRules", - status.HTTP_400_BAD_REQUEST, - ) - - if not data_authorization_service.has_segment_authorization( - id_segment=id_segment, user=user - ): - raise ValidationError( - "Usuário não autorizado neste segmento", - "errors.businessRules", - status.HTTP_401_UNAUTHORIZED, - ) - - segExams = ( - SegmentExam.query.filter(SegmentExam.idSegment == id_segment) - .order_by(asc(SegmentExam.order)) - .all() - ) - - result = {} - for s in segExams: - if s.typeExam in exams: - s.order = exams.index(s.typeExam) - db.session.flush() - - result[s.typeExam] = s.order - - return result - - def get_textual_exams(admission_number: int = None, id_patient: int = None): admission_number_array = [] diff --git a/tests/conftest.py b/tests/conftest.py index a209f882..3e2fb756 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,6 +5,7 @@ from flask_jwt_extended import create_access_token from models.appendix import Memory from models.prescription import Prescription, PrescriptionAudit +import logging import sys @@ -19,6 +20,9 @@ session = DBSession() session.connection(execution_options={"schema_translate_map": {None: "demo"}}) +logging.basicConfig() +logging.getLogger("sqlalchemy.engine").setLevel(logging.ERROR) + with app.test_request_context(): access_token = create_access_token("1") diff --git a/tests/test_alerts.py b/tests/test_alerts.py new file mode 100644 index 00000000..d335a7ef --- /dev/null +++ b/tests/test_alerts.py @@ -0,0 +1,119 @@ +from datetime import datetime +from collections import namedtuple + +from conftest import * +from models.prescription import PrescriptionDrug, DrugAttributes, Drug +from services import alert_service + +MockRow = namedtuple( + "Mockrow", + "prescription_drug drug measure_unit frequency not_used score drug_attributes notes prevnotes status expire substance period_cpoe prescription_date measure_unit_convert_factor", +) + + +def _get_mock_row( + id_prescription_drug: int, dose: float, frequency: float, max_dose: float = None +): + d = Drug() + d.id = 1 + d.name = "Test2" + + pd = PrescriptionDrug() + pd.id = id_prescription_drug + pd.source = "Medicamentos" + pd.idDrug = 1 + pd.frequency = frequency + pd.doseconv = dose + + da = DrugAttributes() + da.idDrug = 1 + da.idSegment = 1 + da.maxDose = max_dose + + return MockRow( + pd, + d, + None, + None, + None, + None, + da, + None, + None, + None, + datetime.today(), + None, + 0, + datetime.today(), + 1, + ) + + +def test_dosemaxplus(): + """Alertas dosemaxplus: Testa presença do alerta tipo dosemaxplus""" + + drugs = [] + + drugs.append( + _get_mock_row(id_prescription_drug=61, dose=10, frequency=1, max_dose=10) + ) + drugs.append( + _get_mock_row(id_prescription_drug=62, dose=10, frequency=1, max_dose=10) + ) + + exams = {"age": 50, "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") + dosemax1 = alerts.get("alerts").get("61", []) + dosemax2 = alerts.get("alerts").get("62", []) + + assert len(dosemax1) == 1 + assert len(dosemax2) == 1 + + assert dosemax1[0].get("type", None) == "maxDosePlus" + assert dosemax2[0].get("type", None) == "maxDosePlus" + + assert dosemax1[0].get("level", None) == "high" + assert dosemax2[0].get("level", None) == "high" + + assert stats.get("maxDosePlus", 0) == 2 + + +def test_dosemax(): + """Alertas dosemaxplus: Testa presença do alerta tipo dosemax""" + drugs = [] + + drugs.append( + _get_mock_row(id_prescription_drug=61, dose=20, frequency=1, max_dose=10) + ) + + exams = {"age": 50, "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") + dosemax1 = alerts.get("alerts").get("61", []) + + assert len(dosemax1) == 1 + + assert dosemax1[0].get("type", None) == "maxDose" + + assert dosemax1[0].get("level", None) == "high" + + assert stats.get("maxDose", 0) == 1 diff --git a/tests/test_basic_api.py b/tests/test_basic_api.py index 2f6d8062..bc0abe23 100644 --- a/tests/test_basic_api.py +++ b/tests/test_basic_api.py @@ -152,10 +152,10 @@ def test_getDepartments(client): assert response.status_code == 200 -def test_getSegmentsExams(client): +def test_get_exam_types(client): """Teste get /segments/exams/types - Valida o status_code 200.""" - url = "/segments/exams/types" + url = "/admin/exam/types" access_token = get_access(client) diff --git a/tests/test_conciliation.py b/tests/test_conciliation.py index a0b89d92..cadc545e 100644 --- a/tests/test_conciliation.py +++ b/tests/test_conciliation.py @@ -1,16 +1,11 @@ -from flask.json import jsonify from conftest import * -from models.appendix import Department -from models.segment import Segment - -# from models.prescription import Prescription from models.prescription import Prescription def test_get_prescriptions_by_idPrescription_additional2(client): """Teste get /prescriptions/idPrescription - Compara response data com dados do banco e valida status_code 200 - Additional2 idPrescription data validation""" + Additional2 idPrescription data validation""" access_token = get_access(client) idPrescription = "9199" @@ -20,13 +15,12 @@ def test_get_prescriptions_by_idPrescription_additional2(client): ) data = json.loads(response.data)["data"] - assert data['concilia'] == 's' - assert len(data['prescription']) == 1 - assert data['prescription'][0]['drug'] == 'Medicamento do paciente' - assert len(data['solution']) == 0 - assert len(data['procedures']) == 0 - assert data['birthdate'] == "1941-02-05" - + assert data["concilia"] == "s" + assert len(data["prescription"]) == 1 + assert data["prescription"][0]["drug"] == "Medicamento do paciente" + assert len(data["solution"]) == 0 + assert len(data["procedures"]) == 0 + assert data["birthdate"] == "1941-02-05" def test_putPrescriprionsCheck(client): @@ -40,7 +34,7 @@ def test_putPrescriprionsCheck(client): response = client.post( url, data=json.dumps(data), headers=make_headers(access_token) ) - + prescription = ( session.query(Prescription) .filter(Prescription.id == PRESCRIPTION_ID) @@ -56,5 +50,3 @@ def test_putPrescriprionsCheck(client): assert response.status_code == 200 assert prescription assert prescriptionaudit - - \ No newline at end of file diff --git a/tests/test_segment.py b/tests/test_segment.py index e2e34796..6bc3faf6 100644 --- a/tests/test_segment.py +++ b/tests/test_segment.py @@ -25,13 +25,3 @@ def test_get_segments_by_idSegment(client): data = json.loads(response.data) # TODO: Add consulta ao banco de dados e comparar retorno (Compreender retorno para realizar comparação) assert response.status_code == 200 - - -def test_get_segments_exams_types(client): - """Teste get /segments/exams/types - Valida status_code 200""" - access_token = get_access(client) - - response = client.get("/segments/exams/types", headers=make_headers(access_token)) - data = json.loads(response.data) - # TODO: Add consulta ao banco de dados e comparar retorno (Compreender retorno para realizar comparação) - assert response.status_code == 200