def testRegeneratePatient(self): folder_name = os.path.join(os.path.dirname(__file__), "data", "chw") patient = export.import_patient_json_file(os.path.join(folder_name, "patient.json")) self.assertEqual(0, len(patient.encounters)) with open(os.path.join(folder_name, "non_life_threatening_referral.xml"), "r") as f: formbody = f.read() formdoc = post_xform_to_couch(formbody) new_form_workflow(formdoc, SENDER_PHONE, None) patient = CPatient.get(patient.get_id) self.assertEqual(1, len(patient.encounters)) self.assertTrue(reprocess(patient.get_id)) patient = CPatient.get(patient.get_id) self.assertEqual(1, len(patient.encounters))
def reprocess(patient_id): """ Reprocess a patient's data from xforms, by playing them back in the order they are found. Returns true if successfully regenerated, otherwise false. """ # you can't call the loader because the loader calls this patient = CPatient.get(patient_id) # first create a backup in case anything goes wrong backup_id = CPatient.copy(patient) try: # have to change types, otherwise we get conflicts with our cases backup = CPatient.get(backup_id) backup.doc_type = "PatientBackup" backup.save() # reload the original and blank out encounters/cases/version patient = CPatient.get(patient_id) patient.encounters = [] patient.cases = [] # don't blank out pregnancies. we need them in order to preserve ids # patient.pregnancies = [] patient.backup_id = backup_id patient.app_version = None # blanking out the version marks it "upgraded" to the current version patient.save() for form in patient.unique_xforms(): add_form_to_patient(patient_id, form) # save to stick the new version on so we know we've upgraded this form if form.requires_upgrade(): form.save() # only send the updated signal when all the dust has settled patient_updated.send(sender="reprocessing", patient_id=patient_id) get_db().delete_doc(backup_id) return True except Exception, e: logging.exception("problem regenerating patient case data (patient: %s)" % patient_id) current_rev = get_db().get_rev(patient_id) patient = get_db().get(backup_id) patient["_rev"] = current_rev patient["_id"] = patient_id patient["doc_type"] = "CPatient" get_db().save_doc(patient) get_db().delete_doc(backup_id) return False
def pi_details(request): year = int(request.GET["year"]) month = int(request.GET["month"]) clinic = request.GET["clinic"] report_slug = request.GET["report"] col_slug = request.GET["col"] results = get_db().view(const.get_view_name(report_slug), reduce=False, key=[year, month -1, clinic, col_slug], include_docs=True) forms = [] for row in results: num, denom = row["value"] # only count forms for now, and make sure they have a patient id # and contributed to the report denominator if row["doc"]["doc_type"] == "CXFormInstance" and denom > 0: form = CXFormInstance.wrap(row["doc"]) try: form.patient_id = form.xpath("case/patient_id") form.bhoma_patient_id = CPatient.get(form.patient_id).formatted_id except ResourceNotFound: form.patient = form.patient_id = form.bhoma_patient_id = None form.num = num form.denom = denom form.good = num == denom forms.append(form) elif row["doc"]["doc_type"] == "PregnancyReportRecord" and denom > 0: # for the pregnancy PI force the aggregated pregnancy docs # to look like forms preg = PregnancyReportRecord.wrap(row["doc"]) try: preg.bhoma_patient_id = CPatient.get(preg.patient_id).formatted_id except ResourceNotFound: form.patient = form.patient_id = form.bhoma_patient_id = None preg.num = num preg.denom = denom preg.good = num == denom preg.encounter_date = preg.first_visit_date forms.append(preg) title = "PI Details - %s: %s (%s, %s)" % (const.get_name(report_slug), const.get_display_name(report_slug, col_slug), datetime(year, month, 1).strftime("%B %Y"), clinic_display_name(clinic)) return render_to_response(request, "reports/pi_details.html", {"report": {"name": title}, "forms": forms})
def get_patient(patient_id): """ Loads a patient from the database. If any conflicts are detected this will run through all the xforms and regenerate the patient, deleting all other revisions. """ resolve_conflicts(patient_id) return CPatient.get(patient_id)
def patient_case(request, patient_id, case_id): pat = CPatient.get(patient_id) found_case = None for case in pat.cases: if case.get_id == case_id: found_case = case break return render_to_response(request, "case/single_case.html", {"patient": pat, "case": found_case, "options": TouchscreenOptions.default()})
def patient_excel(request): # we have to make sure to update any patients without export tags # before redirecting to the export view. for pat in CPatient.view("patient/missing_export_tag", include_docs=True): try: pat.save() except ResourceConflict: # workaround potential conflicts by trying twice pat = CPatient.get(pat.get_id) pat.save() return HttpResponseRedirect("%s?export_tag=CPatient&filename=Patients" % reverse("export_data_async"))
def export_patient(request, patient_id): patient = CPatient.get(patient_id) count = 1 form_filenames = [] for form in patient.xforms(): form_filenames.append(export.get_form_filename(count, form)) count += 1 return render_to_response(request, "patient/export_instructions.html", {"patient": patient, "zip_filename": export.get_zip_filename(patient), "foldername": export.get_zip_filename(patient).split(".")[0], "pat_filename": export.get_patient_filename(patient), "forms": form_filenames})
def export_patient_download(request, patient_id): """ Export a patient's forms to a zip file. """ # this may not perform with huge amounts of data, but for a single # patient should be fine patient = CPatient.get(patient_id) temp_file = export.export_patient(patient) data = temp_file.read() response = HttpResponse(data, content_type='application/zip') response['Content-Disposition'] = 'attachment; filename=%s' % export.get_zip_filename(patient) response['Content-Length'] = len(data) return response
def testUpdate(self): patient = random_person() case = bootstrap_case_from_xml(self, "create_update.xml") patient.cases=[case,] patient.save() # make sure we can get it back from our shared view case_back = CommCareCase.get_with_patient("case/all_and_patient", case.case_id) self.assertEqual(case.case_id, case_back.case_id) self.assertEqual(patient.first_name, case_back.patient.first_name) self.assertEqual(patient.last_name, case_back.patient.last_name) self.assertEqual(patient.get_id, case_back.patient.get_id) self.assertEqual(1, len(patient.cases)) self.assertEqual(case._id, patient.cases[0]._id) # update case = bootstrap_case_from_xml(self, "update.xml", case.case_id) self.assertEqual(patient.get_id, case.patient.get_id) case.save() patient = CPatient.get(patient.get_id) self.assertEqual(1, len(patient.cases)) case_in_patient = patient.cases[0] self.assertEqual(case._id, case_in_patient._id) self.assertEqual(False, case_in_patient.closed) self.assertEqual(3, len(case_in_patient.actions)) new_update_action = case_in_patient.actions[2] self.assertEqual(const.CASE_ACTION_UPDATE, new_update_action["action_type"]) # some properties didn't change self.assertEqual("123", str(case["someotherprop"])) # but some should have self.assertEqual("abcd", case_in_patient["someprop"]) self.assertEqual("abcd", new_update_action["someprop"]) # and there are new ones self.assertEqual("efgh", case_in_patient["somenewprop"]) self.assertEqual("efgh", new_update_action["somenewprop"]) # we also changed everything originally in the case self.assertEqual("a_new_type", case_in_patient.type) self.assertEqual("a_new_type", new_update_action["type"]) self.assertEqual("a new name", case_in_patient.name) self.assertEqual("a new name", new_update_action["name"]) self.assertEqual(UPDATE_DATE, case_in_patient.opened_on) self.assertEqual(UPDATE_DATE, new_update_action["opened_on"]) # case should have a new modified date self.assertEqual(MODIFY_DATE, case.modified_on)
def insert_zscores(sender, form, **kwargs): """For Under5, hook up zscore calculated""" from bhoma.apps.zscore.models import Zscore from bhoma.apps.patient.models import CPatient patient_id = form.xpath("case/patient_id") if not patient_id or not get_db().doc_exist(patient_id): return patient = CPatient.get(patient_id) if form["#type"] == "underfive" and patient.age_in_months <= 60: form.zscore_calc_good = [] try: zscore = Zscore.objects.get(gender=patient.gender, age=patient.age_in_months) except Zscore.DoesNotExist: # how is this possible? zscore = None if zscore and form.xpath("nutrition/weight_for_age") and form.xpath("vitals/weight"): def calculate_zscore(l_value,m_value,s_value,x_value): #for L != 0, Z = (((X/M)^L)-1)/(L*S) eval_power = pow((float(x_value) / m_value),l_value) return ((eval_power - 1) / (l_value * s_value)) def compare_sd(zscore_value): if zscore_value >= 0: return "0" elif 0 > zscore_value and zscore_value >= -2: return "-2" elif -2 > zscore_value and zscore_value >= -3: return "-3" elif -3 > zscore_value: return "below -3" zscore_num = calculate_zscore(zscore.l_value, zscore.m_value, zscore.s_value, form["vitals"]["weight"]) sd_num = compare_sd(zscore_num) if form.xpath("nutrition/weight_for_age") == sd_num: form.zscore_calc_good = "true" else: form.zscore_calc_good = "false" else: form.zscore_calc_good = "unable_to_calc" form.save()
def update_pregnancy_report_data(sender, patient_id, **kwargs): """ Update pregnancies of a patient. """ from bhoma.apps.reports.calc.pregnancy import PregnancyReportData from bhoma.apps.reports.models import PregnancyReportRecord from bhoma.apps.patient.models import CPatient patient = CPatient.get(patient_id) # manually remove old pregnancies, since all pregnancy data is dynamically generated for old_preg in PregnancyReportRecord.view("reports/pregnancies_for_patient", key=patient_id, include_docs=True).all(): old_preg.delete() for preg in patient.pregnancies: preg_report_data = PregnancyReportData(patient, preg) couch_pregnancy = preg_report_data.to_couch_object() couch_pregnancy.save()
def close_ltfu_cases(sender, patient_id, **kwargs): """ Checks if any open cases in the patient are passed the LTFU date and if they are closes them with status lost to followup. """ from bhoma.apps.patient.models import CPatient from bhoma.apps.case.bhomacaselogic.ltfu import close_as_lost patient = CPatient.get(patient_id) save_pat = False for case in patient.cases: today = datetime.utcnow().date() if not case.closed and case.ltfu_date and case.ltfu_date < today: close_as_lost(case) save_pat = True if save_pat: patient.save()
def new_form_received(patient_id, form): """ A new form was received for a patient. This usually just adds the form to the patient object, but will fully reprocess the patient data if the form is from the past, so that previously-entered but later-occurring changes can be applied to the data """ patient = CPatient.get(patient_id) encounter_date = Encounter.get_visit_date(form) full_reprocess = False for encounter in patient.encounters: if encounter.visit_date > encounter_date: full_reprocess = True break if full_reprocess: reprocess(patient_id) else: add_form_to_patient(patient_id, form)
def update_patient_deceased_status(sender, patient_id, **kwargs): """ Check if any of the cases are resolved with type death and if so, mark the patient as deceased. """ from bhoma.apps.patient.models import CPatient patient = CPatient.get(patient_id) if not patient.is_deceased: for case in patient.cases: if case.outcome == Outcome.PATIENT_DIED: patient.handle_died() patient.save() else: save_pat = False for case in patient.cases: if not case.closed: case.manual_close(Outcome.PATIENT_DIED, datetime.utcnow()) save_pat = True if save_pat: patient.save()
def testLifeThreatening(self): folder_name = os.path.join(os.path.dirname(__file__), "data", "chw") patient = export.import_patient_json_file(os.path.join(folder_name, "patient.json")) self.assertEqual(0, len(patient.encounters)) self.assertEqual(0, len(patient.cases)) add_form_with_date_offset\ (None, os.path.join(folder_name, "life_threatening_referral.xml"), days_from_today=0) patient = CPatient.get(patient.get_id) self.assertEqual(1, len(patient.encounters)) [case] = patient.cases [ccase] = case.commcare_cases self.assertFalse(ccase.closed) visit_date = datetime.utcnow().date() self.assertEqual("referral_no_show", ccase.followup_type) self.assertFalse(ccase.closed) self.assertEqual(visit_date + timedelta(days=3), ccase.start_date) self.assertEqual(visit_date + timedelta(days=3), ccase.activation_date) self.assertEqual(visit_date + timedelta(days=3), ccase.due_date) self.assertEqual(visit_date + timedelta(days=42), case.ltfu_date) self.assertEqual("new_clinic_referral|fever headache", ccase.name)
def add_form_to_patient(patient_id, form): """ Adds a clinic form to a patient, including all processing necessary. """ patient = CPatient.get(patient_id) new_encounter = Encounter.from_xform(form) patient.encounters.append(new_encounter) encounter_info = ENCOUNTERS_BY_XMLNS.get(form.namespace) if not encounter_info: raise Exception("Attempt to add unknown form type: %s to patient %s!" % \ (form.namespace, patient_id)) if encounter_info.classification == CLASSIFICATION_CLINIC: case = get_or_update_bhoma_case(form, new_encounter) if case: patient.cases.append(case) if is_pregnancy_encounter(new_encounter): update_pregnancies(patient, new_encounter) if is_delivery_encounter(new_encounter): update_deliveries(patient, new_encounter) elif encounter_info.classification == CLASSIFICATION_PHONE: # process phone form process_phone_form(patient, new_encounter) else: logging.error("Unknown classification %s for encounter: %s" % \ (encounter_info.classification, form.get_id)) # finally close any previous cases we had open, according # to the complicated rules close_previous_cases_from_new_form(patient, form, new_encounter) patient.save()
def new_encounter(request, patient_id, encounter_slug): """A new encounter for a patient""" encounter_info = CLINIC_ENCOUNTERS[encounter_slug] def callback(xform, doc): if doc: new_form_workflow(doc, SENDER_CLINIC, patient_id) return HttpResponseRedirect(reverse("single_patient", args=(patient_id,))) patient = CPatient.get(patient_id) xform = encounter_info.get_xform() # TODO: generalize this better preloader_tags = {"case": {"patient_id" : patient_id, "age_years" : str(patient.age) if patient.age != None else '', "dob": patient.birthdate.strftime('%Y-%m-%d') if patient.birthdate else '', "gender" : patient.gender, "bhoma_case_id" : "<uid>", "case_id" : "<uid>"}, "meta": {"clinic_id": settings.BHOMA_CLINIC_ID, "user_id": request.user.get_profile()._id, "username": request.user.username}} return xforms_views.play(request, xform.id, callback, preloader_tags)