def put(self, id): json_data = request.get_json() if 'counter_id' not in json_data: json_data['counter_id'] = counter_id if not json_data: return {'message': 'No input data received for updating citizen'}, 400 csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() my_print("==> PUT /citizens/" + str(citizen.citizen_id) + '/, Ticket: ' + str(citizen.ticket_number)) try: citizen = self.citizen_schema.load(json_data, instance=citizen, partial=True).data except ValidationError as err: return {'message': err.messages}, 422 db.session.add(citizen) db.session.commit() # If this put request is the result of an appointment checkin, make a Snowplow call. if ('snowplow_addcitizen' in json_data) and (json_data['snowplow_addcitizen'] == True): SnowPlow.add_citizen(citizen, csr) result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', result.data, room=csr.office_id) return {'citizen': result.data, 'errors': result.errors}, 200
def put(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) json_data = request.get_json() if not json_data: return {"message": "No input data received for updating an appointment"} appointment = Appointment.query.filter_by(appointment_id=id)\ .filter_by(office_id=csr.office_id)\ .first_or_404() appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 db.session.add(appointment) db.session.commit() # Make Snowplow call. schema = 'appointment_update' if "checked_in_time" in json_data: schema = 'appointment_checkin' SnowPlow.snowplow_appointment(None, csr, appointment, schema) result = self.appointment_schema.dump(appointment) return {"appointment": result.data, "errors": result.errors}, 200
def post(self): json_data = request.get_json() csr = CSR.find_by_username(g.oidc_token_info['username']) if not csr: raise Exception('no user found with username: `{}`'.format( g.oidc_token_info['username'])) try: citizen = self.citizen_schema.load(json_data).data citizen.office_id = csr.office_id citizen.start_time = datetime.now() except ValidationError as err: print(err) return {"message": err.messages}, 422 citizen.cs_id = active_id citizen.service_count = 1 db.session.add(citizen) db.session.commit() SnowPlow.add_citizen(citizen, csr) result = self.citizen_schema.dump(citizen) return {'citizen': result.data, 'errors': result.errors}, 201
def put(self, id): json_data = request.get_json() if not json_data: return {'message': 'No input data received for updating citizen'}, 400 csr = CSR.find_by_username(g.oidc_token_info['username']) service_request = ServiceReq.query.filter_by(sr_id=id) \ .join(ServiceReq.citizen, aliased=True) \ .filter_by(office_id=csr.office_id).first_or_404() try: service_request = self.service_request_schema.load(json_data, instance=service_request, partial=True).data except ValidationError as err: return {'message': err.messages}, 422 db.session.add(service_request) db.session.commit() SnowPlow.choose_service(service_request, csr, "chooseservice") result = self.service_request_schema.dump(service_request) citizen_result = self.citizen_schema.dump(service_request.citizen) socketio.emit('update_active_citizen', citizen_result.data, room=csr.office_id) return {'service_request': result.data, 'errors': result.errors}, 200
def post(self): json_data = request.get_json() csr = CSR.find_by_username(g.oidc_token_info['username']) try: citizen = self.citizen_schema.load(json_data).data citizen.office_id = csr.office_id citizen.start_time = datetime.now() except ValidationError as err: print(err) return {"message": err.messages}, 422 citizen_state = CitizenState.query.filter_by( cs_state_name="Active").first() citizen.cs_id = citizen_state.cs_id citizen.service_count = 1 db.session.add(citizen) db.session.commit() SnowPlow.add_citizen(citizen, csr) result = self.citizen_schema.dump(citizen) return {'citizen': result.data, 'errors': result.errors}, 201
def begin_service(self, csr, snowplow_event): active_period = self.get_active_period() if active_period.ps.ps_name in ["Being Served"]: raise TypeError( "You cannot begin serving a citizen that is already being served" ) active_period.time_end = datetime.utcnow() # db.session.add(active_period) period_state_being_served = PeriodState.get_state_by_name( "Being Served") new_period = Period(sr_id=self.sr_id, csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_being_served.ps_id, time_start=datetime.utcnow()) self.periods.append(new_period) # Calculate number of active periods, for Snowplow call. period_count = len(self.periods) SnowPlow.snowplow_event(self.citizen_id, csr, snowplow_event, period_count=period_count, current_sr_number=self.sr_number)
def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/finish_service/, Ticket: ' + citizen.ticket_number) active_service_request = citizen.get_active_service_request() inaccurate = request.args.get('inaccurate') if active_service_request is None: return {"message": "Citizen has no active service requests"} # If citizen here overnight, or inaccurate time flag set, update accurate time flag. if citizen.start_time.date() != datetime.now().date( ) or inaccurate == 'true': citizen.accurate_time_ind = 0 SnowPlow.snowplow_event( citizen.citizen_id, csr, "finish", quantity=active_service_request.quantity, current_sr_number=active_service_request.sr_number) active_sr_id = active_service_request.sr_id active_service_request.finish_service(csr, self.clear_comments_flag) citizen_state = CitizenState.query.filter_by( cs_state_name="Received Services").first() citizen.cs_id = citizen_state.cs_id pending_service_state = SRState.get_state_by_name("Complete") active_service_request.sr_state_id = pending_service_state.sr_state_id # remove walkin unique id when service is finished citizen.walkin_unique_id = None db.session.add(citizen) db.session.commit() # Loop to stop all services in the service stopped state (which are all except the active service) if len(citizen.service_reqs) != 1: for sr in citizen.service_reqs: if sr.sr_id != active_sr_id: SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped", quantity=sr.quantity, current_sr_number=sr.sr_number) socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number) result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', result, room=csr.office_id) return { 'citizen': result, 'errors': self.citizen_schema.validate(citizen) }, 200
def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) service_request = ServiceReq.query.filter_by(sr_id=id) \ .join(ServiceReq.citizen, aliased=True) \ .filter_by(office_id=csr.office_id).first_or_404() active_service_state = SRState.get_state_by_name("Active") complete_service_state = SRState.get_state_by_name("Complete") # Find the currently active service_request and close it current_sr_number = 0 for req in service_request.citizen.service_reqs: if req.sr_state_id == active_service_state.sr_state_id: req.sr_state_id = complete_service_state.sr_state_id req.finish_service(csr, clear_comments=False) current_sr_number = req.sr_number db.session.add(req) # Then set the requested service to active service_request.sr_state_id = active_service_state.sr_state_id period_state_being_served = PeriodState.get_state_by_name( "Being Served") new_period = Period(sr_id=service_request.sr_id, csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_being_served.ps_id, time_start=datetime.utcnow()) db.session.add(new_period) db.session.add(service_request) db.session.commit() # To make service active, stop current service, restart previous service. SnowPlow.snowplow_event(service_request.citizen.citizen_id, csr, "stopservice", current_sr_number=current_sr_number) SnowPlow.snowplow_event(service_request.citizen.citizen_id, csr, "restartservice", current_sr_number=service_request.sr_number) citizen_result = self.citizen_schema.dump(service_request.citizen) socketio.emit('update_active_citizen', citizen_result, room=csr.office_id) result = self.service_request_schema.dump(service_request) return { 'service_request': result, 'errors': self.service_request_schema.validate(service_request) }, 200
def post(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() sr_state = SRState.get_state_by_name("Complete") # Create parameters for and make snowplow call. Default is no service request, CSR pressed cancel. quantity = 0 sr_number = 1 active_sr = 0 status = "service-creation" if len(citizen.service_reqs) != 0: active_service_request = citizen.get_active_service_request() quantity = active_service_request.quantity sr_number = active_service_request.sr_number active_sr = active_service_request.sr_id active_period = active_service_request.get_active_period() if active_period.ps.ps_name == "Invited": status = "at-prep" else: status = "being-served" SnowPlow.snowplow_event(citizen.citizen_id, csr, ("left/" + status), quantity = quantity, current_sr_number= sr_number) for service_request in citizen.service_reqs: service_request.sr_state_id = sr_state.sr_state_id for p in service_request.periods: if p.time_end is None: p.time_end = datetime.now() # Make snowplow calls to finish any stopped services if service_request.sr_id != active_sr: SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped", quantity = service_request.quantity, current_sr_number= service_request.sr_number) citizen.cs = CitizenState.query.filter_by(cs_state_name='Left before receiving services').first() if self.clear_comments_flag: citizen.citizen_comments = None if citizen.start_time.date() != datetime.now().date(): citizen.accurate_time_ind = 0 db.session.add(citizen) db.session.commit() socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number) result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', result.data, room=csr.office_id) return {'citizen': result.data, 'errors': result.errors}, 200
def post(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() active_service_request = citizen.get_active_service_request() inaccurate = request.args.get('inaccurate') if active_service_request is None: return {"message": "Citizen has no active service requests"} SnowPlow.snowplow_event( citizen.citizen_id, csr, "finish", quantity=active_service_request.quantity, current_sr_number=active_service_request.sr_number) active_sr_id = active_service_request.sr_id active_service_request.finish_service(csr, self.clear_comments_flag) citizen_state = CitizenState.query.filter_by( cs_state_name="Received Services").first() citizen.cs_id = citizen_state.cs_id pending_service_state = SRState.get_state_by_name("Complete") active_service_request.sr_state_id = pending_service_state.sr_state_id if citizen.start_time.date() != datetime.now().date( ) or inaccurate == 'true': citizen.accurate_time_ind = 0 db.session.add(citizen) db.session.commit() # Loop to stop all services in the service stopped state (which are all except the active service) if len(citizen.service_reqs) != 1: for sr in citizen.service_reqs: if sr.sr_id != active_sr_id: SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped", quantity=sr.quantity, current_sr_number=sr.sr_number) socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number) result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', result.data, room=csr.office_id) return {'citizen': result.data, 'errors': result.errors}, 200
def delete(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) appointment = Appointment.query.filter_by(appointment_id=id)\ .filter_by(office_id=csr.office_id)\ .first_or_404() SnowPlow.snowplow_appointment(None, csr, appointment, 'appointment_delete') db.session.delete(appointment) db.session.commit() return {}, 204
def delete(self, id): appointment = Appointment.query.filter_by(appointment_id=id) \ .first_or_404() csr = None if is_public_user() else CSR.find_by_username( g.oidc_token_info['username']) user: PublicUser = PublicUser.find_by_username( g.oidc_token_info['username']) if is_public_user() else None if is_public_user(): # Check if it's a public user citizen = Citizen.find_citizen_by_id(appointment.citizen_id) if not citizen or citizen.citizen_id != appointment.citizen_id: abort(403) # Must call this prior to deleting from DB, so cannot # combine with repeated is_draft check below if not appointment.is_draft: SnowPlow.snowplow_appointment(None, csr, appointment, 'appointment_delete') db.session.delete(appointment) db.session.commit() if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_delete', id) # Do not log snowplow events or send emails if it's a draft. if not appointment.is_draft: # If the appointment is public user's and if staff deletes it send email if csr: office = Office.find_by_id(appointment.office_id) # Send blackout email try: pprint('Sending email for appointment cancellation') send_email( generate_ches_token(), *get_cancel_email_contents(appointment, user, office, office.timezone)) except Exception as exc: pprint(f'Error on token generation - {exc}') return {}, 204
def add_to_queue(self, csr, snowplow_event): active_period = self.get_active_period() active_period.time_end = datetime.now() #db.session.add(active_period) period_state_waiting = PeriodState.get_state_by_name("Waiting") new_period = Period( sr_id=self.sr_id, csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_waiting.ps_id, time_start=datetime.now() ) self.periods.append(new_period) SnowPlow.snowplow_event(self.citizen_id, csr, snowplow_event, current_sr_number=self.sr_number)
def place_on_hold(self, csr): active_period = self.get_active_period() active_period.time_end = datetime.now() # db.session.add(active_period) period_state_on_hold = PeriodState.get_state_by_name("On hold") new_period = Period( sr_id=self.sr_id, csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_on_hold.ps_id, time_start=datetime.now() ) self.periods.append(new_period) SnowPlow.snowplow_event(self.citizen_id, csr, "hold", current_sr_number = self.sr_number)
def invite(self, csr, invite_type, sr_count=1): active_period = self.get_active_period() if active_period.ps.ps_name in ["Invited", "Being Served", "On hold"]: raise TypeError( "You cannot invite a citizen that has already been invited") # If a generic invite type, event is either invitecitizen or returninvite. if invite_type == "generic": # If only one SR, one period, an invitecitizen call, from First Time in Line state. if sr_count == 1 and len(self.periods) == 2: snowplow_event = "invitecitizen" # Otherwise from the Back in Line state. else: snowplow_event = "returninvite" # A specific invite type. Event is invitefromlist, returnfromlist or invitefromhold else: # If only one SR, one period, an invitefromlist call, from First Time in Line state. if sr_count == 1 and len(self.periods) == 2: snowplow_event = "invitefromlist" # Either from back in line or hold state. else: if active_period.ps.ps_name == "Waiting": snowplow_event = "returnfromlist" else: snowplow_event = "invitefromhold" active_period.time_end = datetime.utcnow() # db.session.add(active_period) period_state_invite = PeriodState.get_state_by_name("Invited") new_period = Period(sr_id=self.sr_id, csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_invite.ps_id, time_start=datetime.utcnow()) self.periods.append(new_period) SnowPlow.snowplow_event(self.citizen_id, csr, snowplow_event, current_sr_number=self.sr_number)
def post(self): csr = CSR.find_by_username(g.oidc_token_info['username']) # Create a citizen for later use. citizen = self.citizen_schema.load({}).data citizen.office_id = csr.office_id citizen.qt_xn_citizen_ind = 0; citizen_state = CitizenState.query.filter_by(cs_state_name="Appointment booked").first() citizen.cs_id = citizen_state.cs_id citizen.start_time = datetime.now() citizen.service_count = 1 db.session.add(citizen) db.session.commit() json_data = request.get_json() if not json_data: return {"message": "No input data received for creating an appointment"}, 400 appointment, warning = self.appointment_schema.load(json_data) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 if appointment.office_id == csr.office_id: appointment.citizen_id = citizen.citizen_id db.session.add(appointment) db.session.commit() SnowPlow.snowplow_appointment(citizen, csr, appointment, 'appointment_create') result = self.appointment_schema.dump(appointment) return {"appointment": result.data, "errors": result.errors}, 201 else: return {"The Appointment Office ID and CSR Office ID do not match!"}, 403
def delete(self, id): appointment = Appointment.query.filter_by(appointment_id=id)\ .first_or_404() csr = None if is_public_user() else CSR.find_by_username( g.oidc_token_info['username']) user: PublicUser = PublicUser.find_by_username( g.oidc_token_info['username']) if is_public_user() else None if is_public_user(): # Check if it's a public user citizen = Citizen.find_citizen_by_id(appointment.citizen_id) if not citizen or citizen.citizen_id != appointment.citizen_id: abort(403) SnowPlow.snowplow_appointment(None, csr, appointment, 'appointment_delete') db.session.delete(appointment) db.session.commit() # If the appointment is public user's and if staff deletes it send email if csr: office = Office.find_by_id(appointment.office_id) # Send blackout email @copy_current_request_context def async_email(subject, email, sender, body): send_email(subject, email, sender, body) thread = Thread(target=async_email, args=get_cancel_email_contents( appointment, user, office, office.timezone)) thread.daemon = True thread.start() return {}, 204
def post(self): user = g.jwt_oidc_token_info['username'] has_role([Role.internal_user.value], g.jwt_oidc_token_info['realm_access']['roles'], user, "CitizenList POST /citizens/") json_data = request.get_json() csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) if not csr: raise Exception('no user found with username: `{}`'.format( g.jwt_oidc_token_info['username'])) try: citizen = self.citizen_schema.load(json_data) citizen.office_id = csr.office_id citizen.start_time = datetime.utcnow() except ValidationError as err: print(err) return {"message": err.messages}, 422 citizen.cs_id = active_id citizen.service_count = 1 db.session.add(citizen) db.session.commit() SnowPlow.add_citizen(citizen, csr) result = self.citizen_schema.dump(citizen) return { 'citizen': result, 'errors': self.citizen_schema.validate(citizen) }, 201
def post(self): json_data = request.get_json() if not json_data: return {"message": "No input data received for creating service request"}, 400 csr = CSR.find_by_username(g.oidc_token_info['username']) try: service_request = self.service_request_schema.load(json_data['service_request']).data except ValidationError as err: return {"message": err.messages}, 422 except KeyError as err: print (err) return {"message": str(err)} active_sr_state = SRState.get_state_by_name("Active") complete_sr_state = SRState.get_state_by_name("Complete") citizen_state = CitizenState.query.filter_by(cs_state_name="Active").first() citizen = Citizen.query.get(service_request.citizen_id) service = Service.query.get(service_request.service_id) if citizen is None: return {"message": "No matching citizen found for citizen_id"}, 400 if service is None: return {"message": "No matching service found for service_id"}, 400 # Find the currently active service_request and close it (if it exists) current_sr_number = 0 for req in citizen.service_reqs: if req.sr_state_id == active_sr_state.sr_state_id: req.sr_state_id = complete_sr_state.sr_state_id req.finish_service(csr, clear_comments=False) current_sr_number = req.sr_number db.session.add(req) service_request.sr_state_id = active_sr_state.sr_state_id # Only add ticket creation period and ticket number if it's their first service_request if len(citizen.service_reqs) == 0: period_state_ticket_creation = PeriodState.get_state_by_name("Ticket Creation") ticket_create_period = Period( csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_ticket_creation.ps_id, time_start=citizen.get_service_start_time(), time_end=datetime.now() ) service_request.periods.append(ticket_create_period) # Move start_time back 6 hours to account for DST and UTC offsets # It's only important that the number carries over _around_ midnight offset_start_time = citizen.start_time - timedelta(hours=6) service_count = ServiceReq.query \ .join(ServiceReq.citizen, aliased=True) \ .filter(Citizen.start_time >= offset_start_time.strftime("%Y-%m-%d")) \ .filter_by(office_id=csr.office_id) \ .join(ServiceReq.service, aliased=True) \ .filter_by(prefix=service.prefix) \ .count() citizen.ticket_number = service.prefix + str(service_count) else: period_state_being_served = PeriodState.get_state_by_name("Being Served") ticket_create_period = Period( csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_being_served.ps_id, time_start=datetime.now() ) service_request.periods.append(ticket_create_period) citizen.cs_id = citizen_state.cs_id # If first service, just choose it. If additional service, more work needed. if len(citizen.service_reqs) == 0: snowplow_event = "chooseservice" else: snowplow_event = "additionalservice" service_request.sr_number = len(citizen.service_reqs) + 1 db.session.add(service_request) db.session.add(citizen) db.session.commit() # If first service, just need a choose service call. if snowplow_event == "chooseservice": SnowPlow.choose_service(service_request, csr, "chooseservice") # If not first service, need stop service, choose service, and additional service calls. else: SnowPlow.snowplow_event(citizen.citizen_id, csr, "stopservice", current_sr_number=current_sr_number) SnowPlow.choose_service(service_request, csr, "chooseservice") SnowPlow.snowplow_event(citizen.citizen_id, csr, "additionalservice", current_sr_number=service_request.sr_number) citizen_result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', citizen_result.data, room=csr.office_id) result = self.service_request_schema.dump(service_request) return {'service_request': result.data, 'errors': result.errors}, 201
def post(self): try: json_data = request.get_json() except Exception as error: return {"message": str(error)}, 401 csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) service_request, message, code = get_service_request( self, json_data, csr) if (service_request is None): return {"message": message}, code active_sr_state = SRState.get_state_by_name("Active") complete_sr_state = SRState.get_state_by_name("Complete") citizen = None try: citizen = Citizen.query \ .options(joinedload(Citizen.service_reqs).joinedload(ServiceReq.periods).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),joinedload(Citizen.office),raiseload(Citizen.user)) \ .filter_by(citizen_id=service_request.citizen_id) citizen = citizen.first() except: print("==> An exception getting citizen info") print(" --> CSR: " + csr.username) print(" --> json_data: " + json.dumps(json_data['service_request'])) if citizen is None: return {"message": "No matching citizen found for citizen_id"}, 400 service, message, code = get_service(service_request, json_data, csr) if (service is None): return {"message": message}, code # Find the currently active service_request and close it (if it exists) current_sr_number = 0 for req in citizen.service_reqs: if req.sr_state_id == active_sr_state.sr_state_id: req.sr_state_id = complete_sr_state.sr_state_id req.finish_service(csr, clear_comments=False) current_sr_number = req.sr_number db.session.add(req) service_request.sr_state_id = active_sr_state.sr_state_id # Only add ticket creation period and ticket number if it's their first service_request if len(citizen.service_reqs) == 0: period_state_ticket_creation = PeriodState.get_state_by_name( "Ticket Creation") ticket_create_period = Period( csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_ticket_creation.ps_id, time_start=citizen.get_service_start_time(), time_end=datetime.now()) service_request.periods.append(ticket_create_period) # Move start_time back 11 hours to account for DST and UTC offsets # tickets up till 10pm will not affect ticket numbering on next day offset_start_time = citizen.start_time - timedelta(hours=11) service_count = ServiceReq.query \ .join(ServiceReq.citizen, aliased=True) \ .filter(Citizen.start_time >= offset_start_time.strftime("%Y-%m-%d")) \ .filter_by(office_id=csr.office_id) \ .join(ServiceReq.service, aliased=True) \ .filter_by(prefix=service.prefix) \ .count() citizen.ticket_number = service.prefix + str(service_count) else: period_state_being_served = PeriodState.get_state_by_name( "Being Served") ticket_create_period = Period( csr_id=csr.csr_id, reception_csr_ind=csr.receptionist_ind, ps_id=period_state_being_served.ps_id, time_start=datetime.now()) service_request.periods.append(ticket_create_period) citizen.cs_id = active_id # If first service, just choose it. If additional service, more work needed. if len(citizen.service_reqs) == 0: snowplow_event = "chooseservice" else: snowplow_event = "additionalservice" service_request.sr_number = len(citizen.service_reqs) + 1 db.session.add(service_request) db.session.add(citizen) db.session.commit() # If first service, just need a choose service call. if snowplow_event == "chooseservice": SnowPlow.choose_service(service_request, csr, "chooseservice") # If not first service, need stop service, choose service, and additional service calls. else: SnowPlow.snowplow_event(citizen.citizen_id, csr, "stopservice", current_sr_number=current_sr_number) SnowPlow.choose_service(service_request, csr, "chooseservice") SnowPlow.snowplow_event( citizen.citizen_id, csr, "additionalservice", current_sr_number=service_request.sr_number) citizen_result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', citizen_result, room=csr.office.office_name) result = self.service_request_schema.dump(service_request) return { 'service_request': result, 'errors': self.service_request_schema.validate(service_request) }, 201
def put(self, id): json_data = request.get_json() csr = None user = None if not json_data: return { "message": "No input data received for updating an appointment" } is_public_user_appt = is_public_user() # Should delete draft appointment, and free up slot, before booking. # Clear up a draft if one was previously created by user reserving this time. if json_data.get('appointment_draft_id'): draft_id_to_delete = int(json_data['appointment_draft_id']) Appointment.delete_draft([draft_id_to_delete]) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_delete', draft_id_to_delete) if is_public_user_appt: office_id = json_data.get('office_id') office = Office.find_by_id(office_id) # user = PublicUser.find_by_username(g.oidc_token_info['username']) # citizen = Citizen.find_citizen_by_username(g.oidc_token_info['username'], office_id) # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id( office_id=office_id, user_name=g.oidc_token_info['username'], start_time=json_data.get('start_time'), timezone=office.timezone.timezone_name, appointment_id=id) if appointments and len( appointments) >= office.max_person_appointment_per_day: return { "code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appoinments reached" }, 400 # Check for race condition start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) service_id = json_data.get('service_id') service = Service.query.get(int(service_id)) if not AvailabilityService.has_available_slots( office=office, start_time=start_time, end_time=end_time, service=service): return { "code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to scheduling conflict. Please pick another time." }, 400 else: csr = CSR.find_by_username(g.oidc_token_info['username']) office_id = csr.office_id office = Office.find_by_id(office_id) appointment = Appointment.query.filter_by(appointment_id=id) \ .filter_by(office_id=office_id) \ .first_or_404() # If appointment is not made by same user, throw error if is_public_user_appt: citizen = Citizen.find_citizen_by_id(appointment.citizen_id) user = PublicUser.find_by_username(g.oidc_token_info['username']) # Should just match based on appointment_id and other info. Can't have proper auth yet. if citizen.user_id != user.user_id: abort(403) appointment, warning = self.appointment_schema.load( json_data, instance=appointment, partial=True) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 db.session.add(appointment) db.session.commit() # Send confirmation email try: pprint('Sending email for appointment update') send_email( generate_ches_token(), *get_confirmation_email_contents(appointment, office, office.timezone, user)) except Exception as exc: pprint(f'Error on token generation - {exc}') # Make Snowplow call. schema = 'appointment_update' if "checked_in_time" in json_data: schema = 'appointment_checkin' if not appointment.is_draft: SnowPlow.snowplow_appointment(None, csr, appointment, schema) result = self.appointment_schema.dump(appointment) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_update', result.data) return {"appointment": result.data, "errors": result.errors}, 200
def post(self): my_print("==> In AppointmentPost, POST /appointments/") json_data = request.get_json() if not json_data: return { "message": "No input data received for creating an appointment" }, 400 # Should delete draft appointment, and free up slot, before booking. # Clear up a draft if one was previously created by user reserving this time. if json_data.get('appointment_draft_id'): draft_id_to_delete = int(json_data['appointment_draft_id']) Appointment.delete_draft([draft_id_to_delete]) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_delete', draft_id_to_delete) is_blackout_appt = json_data.get('blackout_flag', 'N') == 'Y' csr = None user = None office = None # Create a citizen for later use. citizen = self.citizen_schema.load({}).data # Check if the appointment is created by public user. Can't depend on the IDP as BCeID is used by other users as well is_public_user_appointment = is_public_user() if is_public_user_appointment: office_id = json_data.get('office_id') service_id = json_data.get('service_id') user = PublicUser.find_by_username(g.oidc_token_info['username']) # Add values for contact info and notes json_data['contact_information'] = user.email telephone = f'. Phone: {user.telephone}' if user.telephone else '' json_data['comments'] = json_data.get('comments', '') + telephone citizen.user_id = user.user_id citizen.citizen_name = user.display_name office = Office.find_by_id(office_id) service = Service.query.get(int(service_id)) # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id( office_id=office_id, user_name=g.oidc_token_info['username'], start_time=json_data.get('start_time'), timezone=office.timezone.timezone_name) if appointments and len( appointments) >= office.max_person_appointment_per_day: return { "code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appointments reached" }, 400 # Check for race condition start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) if not AvailabilityService.has_available_slots( office=office, start_time=start_time, end_time=end_time, service=service): return { "code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to scheduling conflict. Please pick another time." }, 400 else: csr = CSR.find_by_username(g.oidc_token_info['username']) office_id = csr.office_id office = Office.find_by_id(office_id) citizen.office_id = office_id citizen.qt_xn_citizen_ind = 0 citizen_state = CitizenState.query.filter_by( cs_state_name="Appointment booked").first() citizen.cs_id = citizen_state.cs_id citizen.start_time = datetime.now() citizen.service_count = 1 db.session.add(citizen) db.session.commit() appointment, warning = self.appointment_schema.load(json_data) if is_public_user_appointment: appointment.citizen_name = user.display_name appointment.online_flag = True if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 if appointment.office_id == office_id: appointment.citizen_id = citizen.citizen_id db.session.add(appointment) db.session.commit() # Generate CHES token try: ches_token = generate_ches_token() except Exception as exc: pprint(f'Error on token generation - {exc}') # If staff user is creating a blackout event then send email to all of the citizens with appointments for that period if is_blackout_appt: appointment_ids_to_delete = [] appointments_for_the_day = Appointment.get_appointment_conflicts( office_id, json_data.get('start_time'), json_data.get('end_time')) for (cancelled_appointment, office, timezone, user) in appointments_for_the_day: if cancelled_appointment.appointment_id != appointment.appointment_id and not cancelled_appointment.checked_in_time: appointment_ids_to_delete.append( cancelled_appointment.appointment_id) # Send blackout email try: pprint( 'Sending email for appointment cancellation due to blackout' ) send_email( ches_token, *get_blackout_email_contents( appointment, cancelled_appointment, office, timezone, user)) except Exception as exc: pprint(f'Error on email sending - {exc}') # Delete appointments if len(appointment_ids_to_delete) > 0: Appointment.delete_appointments(appointment_ids_to_delete) else: # Send confirmation email try: pprint('Sending email for appointment confirmation') send_email( ches_token, *get_confirmation_email_contents( appointment, office, office.timezone, user)) except Exception as exc: pprint(f'Error on email sending - {exc}') SnowPlow.snowplow_appointment(citizen, csr, appointment, 'appointment_create') result = self.appointment_schema.dump(appointment) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_create', result.data) return {"appointment": result.data, "errors": result.errors}, 201 else: return { "The Appointment Office ID and CSR Office ID do not match!" }, 403
def put(self, id): json_data = request.get_json() csr = None user = None if not json_data: return {"message": "No input data received for updating an appointment"} is_public_user_appt = is_public_user() if is_public_user_appt: office_id = json_data.get('office_id') office = Office.find_by_id(office_id) # user = PublicUser.find_by_username(g.oidc_token_info['username']) # citizen = Citizen.find_citizen_by_username(g.oidc_token_info['username'], office_id) # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id(office_id=office_id, user_name=g.oidc_token_info['username'], start_time=json_data.get('start_time'), timezone=office.timezone.timezone_name, appointment_id=id) if appointments and len(appointments) >= office.max_person_appointment_per_day: return {"code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appoinments reached"}, 400 # Check for race condition start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time): return {"code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to conflict in time"}, 400 else: csr = CSR.find_by_username(g.oidc_token_info['username']) office_id = csr.office_id office = Office.find_by_id(office_id) appointment = Appointment.query.filter_by(appointment_id=id) \ .filter_by(office_id=office_id) \ .first_or_404() # If appointment is not made by same user, throw error if is_public_user_appt: citizen = Citizen.find_citizen_by_id(appointment.citizen_id) user = PublicUser.find_by_username(g.oidc_token_info['username']) if citizen.user_id != user.user_id: abort(403) appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 db.session.add(appointment) db.session.commit() # Send confirmation email @copy_current_request_context def async_email(subject, email, sender, body): send_email(subject, email, sender, body) thread = Thread(target=async_email, args=get_confirmation_email_contents(appointment, office, office.timezone, user)) thread.daemon = True thread.start() # Make Snowplow call. schema = 'appointment_update' if "checked_in_time" in json_data: schema = 'appointment_checkin' SnowPlow.snowplow_appointment(None, csr, appointment, schema) result = self.appointment_schema.dump(appointment) return {"appointment": result.data, "errors": result.errors}, 200
def post(self, id): my_print("++> POST API call time before csr = statement: " + str(datetime.now())) csr = CSR.find_by_username(g.oidc_token_info['username']) my_print(" ++> Time before citizen = statement: " + str(datetime.now())) citizen = Citizen.query.filter_by(citizen_id=id).first() my_print(" ++> Time before citizen ID statement: " + str(datetime.now())) citizen_id_string = self.get_citizen_string(citizen) citizen_ticket = self.get_ticket_string(citizen) my_print(" ++> Time before citizen ticket statement: " + str(datetime.now())) my_print(" ++> POST /citizens/" + citizen_id_string + '/citizen_left/, Ticket: ' + citizen_ticket) my_print(" ++> Time before sr_state statement: " + str(datetime.now())) sr_state = SRState.get_state_by_name("Complete") # Create parameters for and make snowplow call. Default is no service request, CSR pressed cancel. quantity = 0 sr_number = 1 active_sr = 0 status = "service-creation" if len(citizen.service_reqs) != 0: active_service_request = citizen.get_active_service_request() quantity = active_service_request.quantity sr_number = active_service_request.sr_number active_sr = active_service_request.sr_id active_period = active_service_request.get_active_period() if active_period.ps.ps_name == "Invited": status = "at-prep" else: status = "being-served" my_print(" ++> Time before Snowplow call: " + str(datetime.now())) SnowPlow.snowplow_event(citizen.citizen_id, csr, ("left/" + status), quantity = quantity, current_sr_number= sr_number) my_print(" ++> Time before closing non-active service requests: " + str(datetime.now())) for service_request in citizen.service_reqs: service_request.sr_state_id = sr_state.sr_state_id for p in service_request.periods: if p.time_end is None: p.time_end = datetime.now() # Make snowplow calls to finish any stopped services if service_request.sr_id != active_sr: SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped", quantity = service_request.quantity, current_sr_number= service_request.sr_number) my_print(" ++> Time before updating citizen state: " + str(datetime.now())) citizen.cs = CitizenState.query.filter_by(cs_state_name='Left before receiving services').first() if self.clear_comments_flag: citizen.citizen_comments = None if citizen.start_time.date() != datetime.now().date(): citizen.accurate_time_ind = 0 my_print(" ++> Time before updating citizen database: " + str(datetime.now())) db.session.add(citizen) my_print(" ++> Time before database commit: " + str(datetime.now())) db.session.commit() my_print(" ++> Time before socket io invited call: " + str(datetime.now())) socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number) my_print(" ++> Time before creating the result: " + str(datetime.now())) result = self.citizen_schema.dump(citizen) my_print(" ++> Time before socket io update call: " + str(datetime.now())) socketio.emit('update_active_citizen', result.data, room=csr.office_id) my_print(" ++> Time before return result call: " + str(datetime.now())) return {'citizen': result.data, 'errors': result.errors}, 200
def post(self): json_data = request.get_json() if not json_data: return {"message": "No input data received for creating an appointment"}, 400 is_blackout_appt = json_data.get('blackout_flag', 'N') == 'Y' csr = None user = None office = None # Create a citizen for later use. citizen = self.citizen_schema.load({}).data # Check if the appointment is created by public user. Can't depend on the IDP as BCeID is used by other users as well is_public_user_appointment = is_public_user() if is_public_user_appointment: office_id = json_data.get('office_id') user = PublicUser.find_by_username(g.oidc_token_info['username']) # Add values for contact info and notes json_data['contact_information'] = user.email telephone = f'. Phone: {user.telephone}' if user.telephone else '' json_data['comments'] = json_data.get('comments', '') + telephone citizen.user_id = user.user_id citizen.citizen_name = user.display_name office = Office.find_by_id(office_id) # Validate if the same user has other appointments for same day at same office appointments = Appointment.find_by_username_and_office_id(office_id=office_id, user_name=g.oidc_token_info['username'], start_time=json_data.get('start_time'), timezone=office.timezone.timezone_name) if appointments and len(appointments) >= office.max_person_appointment_per_day: return {"code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appointments reached"}, 400 # Check for race condition start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time): return {"code": "CONFLICT_APPOINTMENT", "message": "Cannot create appointment due to conflict in time"}, 400 else: csr = CSR.find_by_username(g.oidc_token_info['username']) office_id = csr.office_id office = Office.find_by_id(office_id) citizen.office_id = office_id citizen.qt_xn_citizen_ind = 0 citizen_state = CitizenState.query.filter_by(cs_state_name="Appointment booked").first() citizen.cs_id = citizen_state.cs_id citizen.start_time = datetime.now() citizen.service_count = 1 db.session.add(citizen) db.session.commit() appointment, warning = self.appointment_schema.load(json_data) if is_public_user_appointment: appointment.citizen_name = user.display_name appointment.online_flag = True if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 if appointment.office_id == office_id: appointment.citizen_id = citizen.citizen_id db.session.add(appointment) db.session.commit() # If staff user is creating a blackout event then send email to all of the citizens with appointments for that period if is_blackout_appt: appointment_ids_to_delete = [] appointments_for_the_day = Appointment.get_appointment_conflicts(office_id, json_data.get('start_time'), json_data.get('end_time')) for (cancelled_appointment, office, timezone, user) in appointments_for_the_day: if cancelled_appointment.appointment_id != appointment.appointment_id and not cancelled_appointment.checked_in_time: appointment_ids_to_delete.append(cancelled_appointment.appointment_id) # Send blackout email @copy_current_request_context def async_email(subject, email, sender, body): return send_email(subject, email, sender, body) thread = Thread(target=async_email, args=get_blackout_email_contents(appointment, cancelled_appointment, office, timezone, user)) thread.daemon = True thread.start() # Delete appointments if len(appointment_ids_to_delete) > 0: Appointment.delete_appointments(appointment_ids_to_delete) else: # Send confirmation email @copy_current_request_context def async_email(subject, email, sender, body): send_email(subject, email, sender, body) thread = Thread(target=async_email, args=get_confirmation_email_contents(appointment, office, office.timezone, user)) thread.daemon = True thread.start() SnowPlow.snowplow_appointment(citizen, csr, appointment, 'appointment_create') result = self.appointment_schema.dump(appointment) return {"appointment": result.data, "errors": result.errors}, 201 else: return {"The Appointment Office ID and CSR Office ID do not match!"}, 403
def put(self, id): json_data = request.get_json() if 'counter_id' not in json_data: json_data['counter_id'] = counter_id if not json_data: return {'message': 'No input data received for updating citizen'}, 400 csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() my_print("==> PUT /citizens/" + str(citizen.citizen_id) + '/, Ticket: ' + str(citizen.ticket_number)) if not ((json_data.get('is_first_reminder', False) or json_data.get('is_second_reminder', False))): try: citizen = self.citizen_schema.load(json_data, instance=citizen, partial=True) except ValidationError as err: return {'message': err.messages}, 422 else: try: office_obj = Office.find_by_id(citizen.office_id) if (citizen.notification_phone): sms_sent = False # code/function call to send sms notification, sms_sent = send_walkin_reminder_sms(citizen, office_obj, request.headers['Authorization'].replace('Bearer ', '')) if (json_data.get('is_first_reminder', False)): if (sms_sent): citizen.reminder_flag = 1 citizen.notification_sent_time = datetime.utcnow() if (json_data.get('is_second_reminder', False)): if (sms_sent): citizen.reminder_flag = 2 citizen.notification_sent_time = datetime.utcnow() if (citizen.notification_email): # code/function call to send first email notification, email_sent = False email_sent = get_walkin_reminder_email_contents(citizen, office_obj) if email_sent: send_email(request.headers['Authorization'].replace('Bearer ', ''), *email_sent) if (json_data.get('is_first_reminder', False)): if email_sent: citizen.reminder_flag = 1 citizen.notification_sent_time = datetime.utcnow() if (json_data.get('is_second_reminder', False)): if email_sent: citizen.reminder_flag = 2 citizen.notification_sent_time = datetime.utcnow() except ValidationError as err: return {'message': err.messages}, 422 db.session.add(citizen) db.session.commit() # If this put request is the result of an appointment checkin, make a Snowplow call. if ('snowplow_addcitizen' in json_data) and (json_data['snowplow_addcitizen'] == True): SnowPlow.add_citizen(citizen, csr) result = self.citizen_schema.dump(citizen) citizen = Citizen.query.filter_by(citizen_id=citizen.citizen_id).first() socketio.emit('update_active_citizen', result, room=csr.office.office_name) return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 200