class TestAppointmentEndpoint(TestCase): def setUp(self): self.endpoint = AppointmentEndpoint() def test_list(self): # will raise an exception if you don't provide with params that has date or datetime key with self.assertRaises(Exception): self.endpoint.list() # Returns an iterator self.assertEqual(type(self.endpoint.list(params={'date': None})), GeneratorType) # request should fail, and raises a value error when trying to do response.json() with self.assertRaises(APIException): next(self.endpoint.list(params={'date': None}))
def load_appointments(self, doctor_timezone): """ Load all of today's appointments for the logged in doctor """ access_token = self.get_token() api = AppointmentEndpoint(access_token) today = datetime.datetime.now() today = today.strftime('%Y-%m-%d') appointments = api.list(date=today) for a in appointments: patient = self.get_patient(a['patient']) patient_name = patient['first_name'] + ' ' + patient['last_name'] check_in_time = None seen_time = None print(a['status']) if a['status'] in ['In Session', 'Complete']: seen_time = datetime.datetime.now() elif a['status'] in ['Arrived', 'Checked In', 'In Room']: check_in_time = datetime.datetime.now() print('CHECKED IN') appointment = Appointment( drchrono_id=a['id'], doctor=a['doctor'], patient_name=patient_name, patient_id=a['patient'], scheduled_time=datetime.datetime.strptime( a['scheduled_time'], '%Y-%m-%dT%H:%M:%S'), duration=a['duration'], status=a['status'], check_in_time=check_in_time, seen_time=seen_time) appointment.save()
def update_appointments(self, doctor_id): access_token = self.get_token() api = AppointmentEndpoint(access_token) today = date.today() params = {} params['doctor'] = doctor_id lst = list(api.list(params=params, date=today)) for item in lst: Appointment.objects.update_or_create(defaults={ 'doctor': Doctor.objects.get(id=doctor_id), 'duration': item['duration'], 'exam_room': item['exam_room'], 'office': item['office'], 'patient': Patient.objects.get(id=item['patient']), 'scheduled_time': item['scheduled_time'], 'status': item['status'] }, id=item['id'])
def get_context_data(self, **kwargs): kwargs = super(DoctorWelcome, self).get_context_data(**kwargs) access_token = get_token() dt_api = DoctorEndpoint(access_token) ap_api = AppointmentEndpoint(access_token) pt_api = PatientSummaryEndpoint(access_token) doctor = next(dt_api.list()) appointments = list(ap_api.list(date=dt.date.today())) app_list = [] for a in appointments: patient_info = pt_api.fetch(id=a["patient"]) a["patient_info"] = patient_info a['start_time'] = dt.datetime.strptime(a["scheduled_time"], "%Y-%m-%dT%H:%M:%S") a['end_time'] = a["start_time"]\ + dt.timedelta(minutes=int(a["duration"])) updated_at = dt.datetime.strptime(a['updated_at'], "%Y-%m-%dT%H:%M:%S") wait_time = dt.datetime.now() - updated_at a["wait_time"] = round(wait_time.total_seconds() / 60) app_list.append(a) confirmed = filter(lambda x: x["status"] == "", app_list) current = filter(lambda x: x["status"] == "In Session", app_list) arrived = filter(lambda x: x["status"] == "Arrived", app_list) complete = filter(lambda x: x["status"] == "Complete", app_list) kwargs['doctor'] = doctor kwargs['confirmed'] = confirmed kwargs['current'] = current kwargs['arrived'] = arrived kwargs['complete'] = complete return kwargs
def appointments_list(self, params=None, date=None, start=None, end=None, **kwargs): """ Obtains Appointments list https://app.drchrono.com/api-docs/#operation/appointments_list Args: params (dict, optional): cursor : string (Cursor): The pagination cursor value. date : string (date) date_range : string (date_range) doctor : integer (doctor) office : integer (office) page_size: integer (Page size): Number of results to return per page. patient : integer (patient) since : string (since) status : string (status) date (string, optional): Date start (string, optional): Start end (string, optional): End Returns: list: appointments list """ api = AppointmentEndpoint(self.access_token) g = api.list(params, date, start, end, **kwargs) return self.__get_list(g)
def get_appointments(self, doctor): """ Return all appointments for today TODO: Performing local db sync on page load slows performance, even if by a non-negligible amount Write cron/celery task to periodically sync so we can remove the API call from here """ access_token = self.get_token() api = AppointmentEndpoint(access_token) apts_api = api.list(params={"verbose": True, "doctor": doctor['id']}, date=self.today_date) for apt in apts_api: try: # TODO: Update status locally if value from API result differs apt_db = Appointment.objects.get(id=apt['id']) # Check if status has changed and update if needed # if apt_db.status != apt['status']: # apt_db.status = apt['status'] # apt_db.save() except Appointment.DoesNotExist: Appointment.create_from_api(apt, access_token) # Sort appointments by status, in order of time_spent_waiting yesterday = datetime.today() - timedelta(days=1) apts_db = Appointment.objects\ .filter(scheduled_time__gt=yesterday)\ .exclude(status=Appointment.IN_SESSION)\ .order_by(Appointment.ORDER_HIERARCHY) return apts_db
def clean(self): try: oauth_provider = UserSocialAuth.objects.get(provider='drchrono') access_token = oauth_provider.extra_data['access_token'] except UserSocialAuth.DoesNotExist: raise forms.ValidationError( "We had a problem authenticating with the drchrono API.") full_name = f"{self.cleaned_data.get('first_name')} {self.cleaned_data.get('last_name')}" appointments_client = AppointmentEndpoint(access_token) patient_client = PatientEndpoint(access_token) self.cleaned_data['appointment_id'] = None self.cleaned_data['patient_id'] = None # get a list of patients, make a list of their full names to search through patients = list(patient_client.list()) patients_full_names = [ f"{patient.get('first_name')} {patient.get('last_name')}" for patient in list(patient_client.list()) ] patient_found = full_name in patients_full_names if not patient_found: raise forms.ValidationError( "Couldn't find a patient matching your name.") patient = patients[patients_full_names.index(full_name)] self.cleaned_data['patient_id'] = patient.get('id') # okay, we found them. do they have an appt. ? today = timezone.now() today_str = today.strftime('%m-%d-%y') appointments = list( appointments_client.list( {'patient': self.cleaned_data.get('patient_id')}, start=today_str, end=today_str)) patient_has_appointment_today = len(appointments) > 0 if not patient_has_appointment_today: raise forms.ValidationError( "Couldn't find an appointment for you today.") # if they have any appointments set their status to Arrived for appointment in appointments: self.cleaned_data['appointment_id'] = appointment.get('id') import random if random.randrange(20) % 4: appointments_client.update(self.cleaned_data['appointment_id'], {'status': 'Arrived'}) # create a Visit object visit, created = Visit.objects.get_or_create( appointment_id=self.cleaned_data['appointment_id'], patient_id=self.cleaned_data['patient_id']) # if there was already a Visit object and it is set to Arrived, they already checked in! if not created and visit.status == 'Arrived': raise forms.ValidationError( "You already checked in for your appointment today.")
def fetch_appointment_list(self): """ Use the token we have stored in the DB to make an API request and get appointment list. """ # We can create an instance of an endpoint resource class, and use it to fetch details access_token = self.get_token() api = AppointmentEndpoint(access_token) # Grab all appointments from one date return api.list(date='2020-02-06')
def make_api_request(self): """ Use the token we have stored in the DB to make an API request and get doctor details. If this succeeds, we've proved that the OAuth setup is working """ # We can create an instance of an endpoint resource class, and use it to fetch details access_token = self.get_token() api = AppointmentEndpoint(access_token) # Grab the first doctor from the list; normally this would be the whole practice group, but your hackathon # account probably only has one doctor in it. return (api.list(date="2019-10-22"))
def get_appointments(self, date=None, start=None, end=None): """ Get appointments in certain date or range But in this Hackathon, only TODAY's appointments are pulled. Using ID as key """ access_token = self.get_token() api = AppointmentEndpoint(access_token) for appointment in list(api.list(None, date, start, end)): self.appointments[appointment['id']] = api.fetch( appointment['id'], 'verbose=true') return self.appointments
def make_appt_api_request(access_token): appt_api = AppointmentEndpoint(access_token) for appt in appt_api.list(date=datetime.now()): obj, _ = Appointment.objects.update_or_create( pk=appt["id"], defaults={ "doctor": Doctor.objects.get(id=appt["doctor"]), "patient": Patient.objects.get(id=appt["patient"]), "status": appt["status"], "start_time": appt["scheduled_time"], "duration": appt["duration"] })
def apt_db_save(self, token): apt_api = AppointmentEndpoint(token) # Sometimes, this api cannot return all the appointments from start time to end time. for p in apt_api.list(start='2019-11-2', end='2019-11-10'): p_start_time = datetime.datetime.strptime(p[u'scheduled_time'], '%Y-%m-%dT%H:%M:%S') p_end_time = p_start_time + datetime.timedelta( minutes=int(p[u'duration'])) appointment = models.Appointment(apt_id=p[u'id'], doctor_id=p[u'doctor'], patient_id=p[u'patient'], \ scheduled_time=p[u'scheduled_time'], duration=p[u'duration'], end_time=p_end_time,\ status=p[u'status'], reason=p[u'reason']) appointment.save() return
def list_today(request): """ List Today's Appointments """ api_appt = AppointmentEndpoint(ChronoOauth.get_token()) current_date = datetime.now().strftime("%Y-%m-%d") try: appointments_data = api_appt.list(date=current_date) appointments_list = list(appointments_data) except Exception, e: print(e) return JsonResponse({ 'error' : 'D))0000MNN Could not fetch Appointments List.' })
def manage_exam_room(request): if request.method == 'POST': oauth_provider = UserSocialAuth.objects.get(provider='drchrono') access_token = oauth_provider.extra_data['access_token'] api = AppointmentEndpoint(access_token) for p in api.list(start='2019-10-25', end='2019-10-26'): appointment = models.Appointment(apt_id=p[u'id'], doctor_id=p[u'doctor'], patient_id=p[u'patient'], \ scheduled_time=p[u'scheduled_time'], duration=p[u'duration'], \ status=p[u'status'], reason=p[u'reason'], exam_room_id=p[u'exam_room']) appointment.save() else: pass appointment_db = models.Appointment.objects.all() return render(request, 'patient_checkin.html', {})
def list_today_status(request): """ List Today's Appointment Statuses only. Even though the API gets the entire Appointment Data, Down the road, there will be an opportunity to get partial data. """ api_appt = AppointmentEndpoint(ChronoOauth.get_token()) current_date = datetime.now().strftime("%Y-%m-%d") try: appointments_data = api_appt.list(date=current_date) appointments_list = list(appointments_data) except Exception, e: print(e) return JsonResponse( { 'error' : 'D))0000MNN Could not fetch Appointments List.' })
def get_context_data(self, **kwargs): kwargs = super(PatientAppointmentListView, self).get_context_data(**kwargs) patient_id = self.kwargs['id'] access_token = get_token() ap_api = AppointmentEndpoint(access_token) pt_api = PatientSummaryEndpoint(access_token) ap_params = {"patient": patient_id} patient_info = pt_api.fetch(id=patient_id) appointments = list(ap_api.list(params=ap_params, date=dt.date.today())) for a in appointments: a['start_time'] = dt.datetime.strptime(a["scheduled_time"], "%Y-%m-%dT%H:%M:%S") a['end_time'] = a["start_time"]\ + dt.timedelta(minutes=int(a["duration"])) kwargs["patient_info"] = patient_info kwargs["appointments"] = appointments return kwargs
def getpatientappointments(request, id): patientid = id access_token = get_token() api = AppointmentEndpoint(access_token) params = {} params['patient'] = patientid params['date'] = str(datetime.today().year) + '-' + str( datetime.today().month) + '-' + str(datetime.today().day) params['date'] = '2019-11-01' appointments = api.list(params=params) appointmentList = [] for a in appointments: appointmentList.append(a) response = {} response['appointments'] = appointmentList return JsonResponse(response)
def get_all_appointments(self, first_name=None, last_name=None, date_of_birth=None, doctor_id=None): """ :param : either {first_name: last_name: date_of_birth: } or {doctor_id: } :return: all appointments iterator """ api = AppointmentEndpoint(self.access_token) # should be the current date. current_time = self.get_current_time() date = current_time.strftime("%Y-%m-%d") params = {} if doctor_id is not None: params = {'date': date, 'doctor': doctor_id} elif first_name is not None and last_name is not None and date_of_birth is not None: patient_details = self.get_patient(first_name=first_name, last_name=last_name, date_of_birth=date_of_birth) patient_id = patient_details["id"] params = {'date': date, "patient": patient_id} else: raise Exception("Must provide first name, last name and date_of_birth OR doctor id") return api.list(params)
def getAppointments(self): """ Use the token we have stored in the DB to make an API request and get doctor details. If this succeeds, we've proved that the OAuth setup is working """ # We can create an instance of an endpoint resource class, and use it to fetch details access_token = self.get_token() api = AppointmentEndpoint(access_token) date = datetime.strptime(self.request.GET.get('date'), '%Y-%m-%d')\ if self.request.GET.get('date') else None start = datetime.strptime(self.request.GET.get('start'), '%Y-%m-%d')\ if self.request.GET.get('start') else None end = datetime.strptime(self.request.GET.get('end'), '%Y-%m-%d')\ if self.request.GET.get('end') else None appointments = api.list(date=date, start=start, end=end) appointmentsList = [] for a in appointments: appointmentsList.append(a) return appointmentsList
def form_valid(self, form): """ update patient info. if patient already has an appointment, mark status as arrived else create an appointment and mark status as arrived """ print('\n \n form VALID !!') todays_date = date.today() access_token = utility.get_token() patient_api = PatientEndpoint(access_token) # update patient demographics update_resp = patient_api.update(id=str(self.kwargs['patient_id']), data=form.cleaned_data) # check if this patient has an appointment appnt_api = AppointmentEndpoint(access_token) appnt_list = list( appnt_api.list( date=todays_date, params={'patient': self.kwargs['patient_id']}, )) print() print('appnt_list={}'.format(appnt_list)) # if appnt doesnt exist create one if appnt_list == []: new_appt = appnt_api.create(data={ 'date': todays_date, 'patient': self.kwargs['patient_id'] }) print() print('new_appt={}'.format(new_appt)) return super(PatientUpdateProfile, self).form_valid(form)
def get_appointments_on_date(self, on_date): access_token = utility.get_token() appnt_api = AppointmentEndpoint(access_token) return appnt_api.list(date=on_date)
def fetch_appointment(self, today=None): if not today: today = "2019-10-25" todays_date = date.today().strftime("%Y-%m-%d") api = AppointmentEndpoint(self.get_token()) return api.list(date=today)
def get_appointments_on_date(self, date): auth_token = get_token() appointments = AppointmentEndpoint(auth_token) return appointments.list(date=date)
def verify_patient_has_appointment(params): """ Verify that a patient has a confirmed appointment with the doctor before asking for their Demographics information. parameters = { 'patient_id', 'first_name', 'last_name', 'appointment_id', 'override_late' # Will not exclude this appointment from } """ current_date = datetime.now().strftime("%Y-%m-%d") # Check if the patient actually exists. patient_api = PatientEndpoint(ChronoOauth.get_token()) patient = None if 'patient_id' in params: patient = patient_api.fetch(id=params['patient_id']) elif 'first_name' in params and 'last_name' in params: patient = PatientService.find_patient_by_name( params['first_name'], params['last_name']) if not patient: return {'error': 'Patient not found in the Doctor\'s database.'} api_appt = AppointmentEndpoint(ChronoOauth.get_token()) if 'appointment_id' in params and params['appointment_id']: """ Check a patient into a specific appointment. """ target_appointment = api_appt.fetch(id=params['appointment_id']) else: """ Grab a list of the nearest available Appointment. """ current_appointments = api_appt.list(date=current_date) current_appointments_list = list(current_appointments) # Determine whether a patient can be seen by the doctor. # The api automatically sorts the schedules by the time. #TODO: Do not yet factor Early Checkin or Late Arrivals. Just deal with a single status. target_appointment = None for appointment in current_appointments_list: # If a patient has multiple schedules throughout the day, # that patient cannot be 'checked_in' twice. if appointment['patient'] == patient['id'] and \ appointment['status'] in settings.DRCHRONO_VALID_SEEABLE_PATIENTS: break if appointment['patient'] == patient['id'] and \ appointment['status'] in settings.DRCHRONO_VALID_APPOINTMENTS: # No time to debug this extra functionality... #if 'override_late' not in params or not params['override_late']: # # If this appointment time is later than the Doctors Practice's # # Late limit, then skip this appointment. # scheduled_time = datetime.strptime (appointment['scheduled_time'], "%Y-%m-%dT%H:%M:%S") # schedule_epoch = (scheduled_time - datetime(1970, 1, 1)).total_seconds() # # late_time = time.time() - schedule_epoch # if late_time > settings.PATIENT_LATE_CUTOFF_TIME*60: # print('wut? {} {}'.format(late_time, settings.PATIENT_LATE_CUTOFF_TIME)) # continue target_appointment = appointment break elif appointment['patient'] == patient['id']: pass if not target_appointment: return { 'error' : 'Were sorry, it appears you have either: not confirmed an appointment, '+\ 'have already checked in, are Late (over {} Minutes), rescheduled, etc'.\ format(settings.PATIENT_LATE_CUTOFF_TIME) } return target_appointment
def get_context_data(self, **kwargs): """ :param kwargs: :return: """ kwargs = super(DoctorWelcome, self).get_context_data(**kwargs) # look for oauth user, so we can use it's access_token oauth_provider = get_object_or_404(UserSocialAuth, provider='drchrono') # check if token is about to expire, and refresh it if so if self.is_access_token_expired( oauth_provider.extra_data['access_token']): oauth_provider.refresh_token(load_strategy()) access_token = oauth_provider.extra_data['access_token'] patient_client = PatientEndpoint(access_token) appointments_client = AppointmentEndpoint(access_token) # information about the doctor kwargs['doctor'] = next(DoctorEndpoint(access_token).list()) # list of patients patients = list(patient_client.list()) # list of today's appointments today_str = timezone.now().strftime('%m-%d-%y') todays_appointments = list( appointments_client.list({}, start=today_str, end=today_str)) for appointment in todays_appointments: patient = [ patient for patient in patients if patient.get('id') == appointment.get('patient') ][0] appointment['first_name'] = patient.get('first_name') appointment['last_name'] = patient.get('last_name') kwargs['appointments'] = todays_appointments # fetch information about patients who have checked in visits = Visit.objects.filter(status='Arrived', arrival_time__isnull=False, start_time__isnull=True).all() for visit in visits: visit.wait_since_arrived = visit.get_wait_duration().seconds patient = [ patient for patient in patients if patient.get('id') == visit.patient_id ][0] visit.first_name = patient.get('first_name') visit.last_name = patient.get('last_name') kwargs['arrived'] = visits # fetch information about our current appointment current_appointment = Visit.objects.filter( status='In Session', arrival_time__isnull=False, start_time__isnull=False).first() if current_appointment: kwargs['current_appointment'] = current_appointment current_appointment.visit_duration = current_appointment.get_visit_duration( ).seconds patient = [ patient for patient in patients if patient.get('id') == current_appointment.patient_id ][0] current_appointment.first_name = patient.get('first_name') current_appointment.last_name = patient.get('last_name') current_appointment.date_of_birth = patient.get('date_of_birth') current_appointment.date_of_last_appointment = patient.get( 'date_of_last_appointment') current_appointment.race = patient.get('race') current_appointment.gender = patient.get('gender') current_appointment.ethnicity = patient.get('ethnicity') # create list of past visit, and use it to generate average wait and visit duration past_visits = Visit.objects.filter(status="Finished", arrival_time__isnull=False, start_time__isnull=False).all() if len(past_visits) > 0: avg_wait_time = sum( [(visit.start_time - visit.arrival_time).seconds for visit in past_visits]) / len(past_visits) kwargs['avg_wait_duration'] = math.ceil(avg_wait_time) avg_visit_duration = sum( [(visit.end_time - visit.start_time).seconds for visit in past_visits]) / len(past_visits) kwargs['avg_visit_duration'] = math.ceil(avg_visit_duration) kwargs['avg_wait_duration'] = "You have no arrivals! - 0" kwargs['avg_visit_duration'] = "You have no visits! - 0" else: kwargs['avg_wait_duration'] = "You have no arrivals! - 0" kwargs['avg_visit_duration'] = "You have no visits! - 0" # creating altair visualization # create a df with list of times and durations visit_data = [{ 'start_time': visit.arrival_time, 'arrival_time': visit.start_time, 'wait_duration': visit.get_visit_duration().seconds, 'visit_duration': visit.get_wait_duration().seconds, } for visit in past_visits] visit_data_df = pd.DataFrame(visit_data) # https://altair-viz.github.io/user_guide/interactions.html#selections-building-blocks-of-interactions brush = alt.selection_interval(encodings=['x']) chart = alt.Chart(visit_data_df).mark_bar().properties( width=300, height=150).add_selection(brush) # combine two charts one with wait duration and one with visit duration kwargs['chart'] = alt.hconcat( chart.encode(x=alt.X( 'start_time:T', axis=alt.Axis(title='Time the visit started')), y=alt.Y('wait_duration:Q', axis=alt.Axis(title='Wait Duration')), color=alt.condition(brush, alt.value('black'), alt.value('lightgray'))), chart.encode( x=alt.X('start_time:T', axis=alt.Axis(title='Time the visit started')), y=alt.Y('visit_duration:Q', axis=alt.Axis(title='Visit Duration')), color=alt.condition( brush, alt.value('black'), alt.value('lightgray')))).resolve_scale(y='shared') return kwargs