class CitizenList(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @oidc.accept_token(require_token=True) def get(self): try: user = g.oidc_token_info['username'] has_role([Role.internal_user.value], g.oidc_token_info['realm_access']['roles'], user, "CitizenList GET /citizens/") 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'])) citizens = Citizen.query.filter_by(office_id=csr.office_id, cs_id=active_id) \ .order_by(Citizen.priority) \ .join(Citizen.service_reqs).all() result = self.citizens_schema.dump(citizens) return {'citizens': result.data, 'errors': result.errors}, 200 except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500 @oidc.accept_token(require_token=True) @has_any_role(roles=[Role.internal_user.value]) @api_call_with_retry def post(self): user = g.oidc_token_info['username'] has_role([Role.internal_user.value], g.oidc_token_info['realm_access']['roles'], user, "CitizenList POST /citizens/") 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
class CitizenGenericInvite(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @jwt.has_one_of_roles([Role.internal_user.value]) def post(self): y = 0 key = "DR->" + get_key() y = y + 1 csr = csr_find_by_user() lock = FileLock("lock/invite_citizen_{}.lock".format(csr.office_id)) with lock: active_citizen_state = citizen_state waiting_period_state = find_wait() citizen = None json_data = request.get_json() if json_data and 'counter_id' in json_data: counter_id = int(json_data.get('counter_id')) else: counter_id = int(csr.counter_id) citizen = find_citizen(counter_id,active_citizen_state, csr, waiting_period_state) # If no matching citizen with the same counter type, get next one if citizen is None: citizen = find_citizen2(active_citizen_state, csr, waiting_period_state) if citizen is None: return {"message": "There is no citizen to invite"}, 400 my_print("==> POST /citizens/invite/ Citizen: " + str(citizen.citizen_id) + ', Ticket: ' + citizen.ticket_number) db.session.refresh(citizen) active_service_request = find_active_sr(citizen) try: invite_active_sr(active_service_request,csr,citizen) except TypeError: return {"message": "Error inviting citizen. Please try again."}, 400 active_service_state = find_active_ss() active_service_request.sr_state_id = active_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office.office_name) 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.office_name) return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 200
class CitizenList(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @oidc.accept_token(require_token=True) def get(self): try: csr = CSR.find_by_username(g.oidc_token_info['username']) active_state = CitizenState.query.filter_by( cs_state_name="Active").first() citizens = Citizen.query.filter_by(office_id=csr.office_id, cs_id=active_state.cs_id) \ .order_by(Citizen.priority) \ .join(Citizen.service_reqs).all() result = self.citizens_schema.dump(citizens) return {'citizens': result.data, 'errors': result.errors}, 200 except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500 @oidc.accept_token(require_token=True) @api_call_with_retry 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
class CitizenPlaceOnHold(Resource): citizen_schema = CitizenSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) 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=id, office_id=csr.office_id) citizen = citizen.first() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/place_on_hold/, Ticket: ' + citizen.ticket_number) active_service_request = citizen.get_active_service_request() if active_service_request is None: return {"message": "Citizen has no active service requests"} active_service_request.place_on_hold(csr) pending_service_state = SRState.get_state_by_name("Active") active_service_request.sr_state_id = pending_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office.office_name) result = self.citizen_schema.dump(citizen) socketio.emit('update_active_citizen', result, room=csr.office.office_name) return {'citizen': result, 'errors': self.citizen_schema.validate(citizen)}, 200
class SmartBoradQDetails(Resource): citizen_schema = CitizenSchema() office_schema = OfficeSchema() walkinObj = WalkinDetail() processObj = SendLineReminderWalkin() @api_call_with_retry def get(self, id): try: # get office details from url id office = Office.query.filter_by(office_number=id).first() if not office: return {'message': 'office_number could not be found.'}, 400 booked_not_checkin = [] if (office.currently_waiting == 1): # office time zone local_timezone = self.walkinObj.get_my_office_timezone( office=office) # get all app from agenda panel result_in_book = self.walkinObj.get_all_app_from_agenda_panel( office=office) # processing agenda panel appointmnets: booked_not_checkin = self.walkinObj.process_agenda_panel( result_in_book, local_timezone) return {'booked_not_checkin': booked_not_checkin}, 200 return {} except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500
class SmartBoradQDetails(Resource): citizen_schema = CitizenSchema() office_schema = OfficeSchema() walkinObj = WalkinDetail() processObj = SendLineReminderWalkin() @api_call_with_retry def get(self, id): try: # get office details from url id office = Office.query.filter_by(office_number=id).first() if not office: return {'message': 'office_number could not be found.'}, 400 res_list = [] if (office.currently_waiting == 1): # result= all citizen in q result = self.walkinObj.get_all_citizen_in_q(office=office) # process result booked_check_app, walkin_app = self.processObj.process_all_citizen_in_q( result) # sorting-maintaing the order group res_list = tuple(booked_check_app + walkin_app) return {'citizen_in_q': res_list}, 200 return {} except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500
class CitizenFinishService(Resource): citizen_schema = CitizenSchema() clear_comments_flag = (os.getenv("THEQ_CLEAR_COMMENTS_FLAG", "True")).upper() == "TRUE" @oidc.accept_token(require_token=True) @has_any_role(roles=[Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.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 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
class CitizenPlaceOnHold(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @has_any_role(roles=[Role.internal_user.value]) @api_call_with_retry 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() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/place_on_hold/, Ticket: ' + citizen.ticket_number) active_service_request = citizen.get_active_service_request() if active_service_request is None: return {"message": "Citizen has no active service requests"} active_service_request.place_on_hold(csr) pending_service_state = SRState.get_state_by_name("Active") active_service_request.sr_state_id = pending_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class CitizenDetail(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) def get(self, id): try: csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() my_print("==> GET /citizens/" + str(citizen.citizen_id) + '/, Ticket: ' + citizen.ticket_number) result = self.citizen_schema.dump(citizen) return {'citizen': result.data, 'errors': result.errors} except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500 @oidc.accept_token(require_token=True) @api_call_with_retry def put(self, id): json_data = request.get_json() counter = Counter.query.filter(Counter.counter_name == "Counter")[0] if 'counter_id' not in json_data: json_data['counter_id'] = counter.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, office_id=csr.office_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
class CsrSelf(Resource): csr_schema = CSRSchema() citizen_schema = CitizenSchema(many=True) exam_schema = ExamSchema(many=True) exam_type_schema = ExamTypeSchema() @oidc.accept_token(require_token=True) def get(self): try: csr = CSR.find_by_username(g.oidc_token_info['username']) if not csr: return {'Message': 'User Not Found'}, 404 db.session.add(csr) active_sr_state = SRState.get_state_by_name("Active") today = datetime.now() active_citizens = Citizen.query \ .join(Citizen.service_reqs) \ .filter_by(sr_state_id=active_sr_state.sr_state_id) \ .join(ServiceReq.periods) \ .filter_by(csr_id=csr.csr_id) \ .filter(Period.time_end.is_(None)) individual_exams = Exam.query \ .filter_by(office_id=csr.office_id) \ .filter(Exam.exam_returned_date.is_(None), Exam.expiry_date <= today, Exam.deleted_date.is_(None)) \ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id) \ .filter(ExamType.group_exam_ind == 0).count() group_exams = Exam.query \ .filter_by(office_id=csr.office_id) \ .filter(Exam.deleted_date.is_(None)) \ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id) \ .filter(ExamType.group_exam_ind == 1) \ .join(Booking, Exam.booking_id == Booking.booking_id) \ .filter(Booking.invigilator_id.is_(None))\ .filter(Booking.sbc_staff_invigilated == 0).count() result = self.csr_schema.dump(csr) active_citizens = self.citizen_schema.dump(active_citizens) return { 'csr': result.data, 'individual_exams': individual_exams, 'group_exams': group_exams, 'active_citizens': active_citizens.data, 'errors': result.errors } except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500
class CitizenBeginService(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @has_any_role(roles=[Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) lock = FileLock("lock/begin_citizen_{}.lock".format(csr.office_id)) with lock: citizen = Citizen.query.filter_by(citizen_id=id).first() pending_service_state = SRState.get_state_by_name("Active") if citizen is None: print( "==> POST /citizen/<id>/begin_service/ error. No citizen with id " + str(id)) return {"message": "No citizen found with id " + str(id)} else: my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/begin_service/, Ticket: ' + citizen.ticket_number) active_service_request = citizen.get_active_service_request() if active_service_request is None: return {"message": "Citizen has no active service requests"} try: # Get Snowplow call. active_period = active_service_request.get_active_period() snowplow_event = "beginservice" if active_period.ps.ps_name == "On hold": snowplow_event = "invitefromhold" if active_period.ps.ps_name == "Ticket Creation": snowplow_event = "servecitizen" active_service_request.begin_service(csr, snowplow_event) except TypeError: return {"message": "Citizen has already been invited"}, 400 active_service_request.sr_state_id = pending_service_state.sr_state_id db.session.add(citizen) db.session.commit() if snowplow_event != "beginservice": socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class ServiceRequestActivate(Resource): citizen_schema = CitizenSchema() service_requests_schema = ServiceReqSchema(many=True) service_request_schema = ServiceReqSchema() @oidc.accept_token(require_token=True) @api_call_with_retry def post(self, id): 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() 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.now() ) 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.data, room=csr.office_id) result = self.service_request_schema.dump(service_request) return {'service_request': result.data, 'errors': result.errors}, 200
class CitizenSpecificInvite(Resource): citizen_schema = CitizenSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) lock = FileLock("lock/invite_citizen_{}.lock".format(csr.office_id)) with lock: citizen = db.session.query(Citizen).filter_by( citizen_id=id).first() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/invite/, Ticket: ' + citizen.ticket_number) active_service_state = SRState.get_state_by_name("Active") active_service_request = citizen.get_active_service_request() if active_service_request is None: return { "message": "Citizen has no active service requests" }, 400 try: active_service_request.invite(csr, invite_type="specific", sr_count=len( citizen.service_reqs)) except TypeError: return {"message": "Citizen has already been invited"}, 400 active_service_request.sr_state_id = active_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office.office_name) 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.office_name) return { 'citizen': result, 'errors': self.citizen_schema.validate(citizen) }, 200
class CitizenRemoveFromQueue(Resource): citizen_schema = CitizenSchema() appointment_schema = AppointmentSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() active_service_request = citizen.get_active_service_request() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/remove_from_queue, Ticket: ' + citizen.ticket_number) if active_service_request is None: return {"message": "Citizen has no active service requests"} appointment = Appointment.query.filter_by(citizen_id=id) \ .filter_by(office_id=csr.office_id) \ .filter(Appointment.checked_in_time.isnot(None)) \ .first_or_404() # This "un-check-in"s the appointment, returning it to calendar and removing from the queue. appointment.checked_in_time = None # Delete all "periods", FKs on service req active_service_request.remove_from_queue() # Delete the service req. db.session.delete(active_service_request) db.session.commit() # appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True) # if warning: # logging.warning("WARNING: %s", warning) # return {"message": warning}, 422 socketio.emit('update_customer_list', {}, room=csr.office_id) socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number) result = self.appointment_schema.dump(appointment) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_create', result) return { "appointment": result, "errors": self.appointment_schema.validate(appointment) }, 200
class AppointmentPost(Resource): appointment_schema = AppointmentSchema() citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @api_call_with_retry 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() 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
class CitizenAddToQueue(Resource): citizen_schema = CitizenSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() active_service_request = citizen.get_active_service_request() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/add_to_queue, Ticket: ' + citizen.ticket_number) if active_service_request is None: return {"message": "Citizen has no active service requests"} # Figure out what Snowplow call to make. Default is addtoqueue snowplow_call = "addtoqueue" if len(citizen.service_reqs) != 1 or len( active_service_request.periods) != 1: active_period = active_service_request.get_active_period() if active_period.ps.ps_name == "Invited": snowplow_call = "queuefromprep" elif active_period.ps.ps_name == "Being Served": snowplow_call = "returntoqueue" else: # TODO: Put in a Feedback Slack/Service now call here. return {"message": "Invalid citizen/period state. "} active_service_request.add_to_queue(csr, snowplow_call) pending_service_state = SRState.get_state_by_name("Pending") active_service_request.sr_state_id = pending_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class CitizenDetail(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) def get(self, id): try: csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() result = self.citizen_schema.dump(citizen) return {'citizen': result.data, 'errors': result.errors} except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500 @oidc.accept_token(require_token=True) @api_call_with_retry 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']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() 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() 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
class CitizenBeginService(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @api_call_with_retry def post(self, id): lock = FileLock("lock/begin_citizen.lock") with lock: csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first() pending_service_state = SRState.get_state_by_name("Active") active_service_request = citizen.get_active_service_request() if active_service_request is None: return {"message": "Citizen has no active service requests"} try: # Get Snowplow call. active_period = active_service_request.get_active_period() snowplow_event = "beginservice" if active_period.ps.ps_name == "On hold": snowplow_event = "invitefromhold" if active_period.ps.ps_name == "Ticket Creation": snowplow_event = "servecitizen" active_service_request.begin_service(csr, snowplow_event) except TypeError: return {"message": "Citizen has already been invited"}, 400 active_service_request.sr_state_id = pending_service_state.sr_state_id db.session.add(citizen) db.session.commit() if snowplow_event != "beginservice": socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class ServiceRequestsDetail(Resource): citizen_schema = CitizenSchema() service_requests_schema = ServiceReqSchema(many=True) service_request_schema = ServiceReqSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry 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.jwt_oidc_token_info['username']) service_request = ServiceReq.query.filter_by(sr_id=id) \ .join(ServiceReq.citizen, aliased=True).first_or_404() try: service_request = self.service_request_schema.load( json_data, instance=service_request, partial=True) 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, room=csr.office_id) return { 'service_request': result, 'errors': self.service_request_schema.validate(service_request) }, 200
class CitizenRemoveFromQueue(Resource): citizen_schema = CitizenSchema() appointment_schema = AppointmentSchema() @oidc.accept_token(require_token=True) @has_any_role(roles=[Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() active_service_request = citizen.get_active_service_request() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/remove_from_queue, Ticket: ' + citizen.ticket_number) if active_service_request is None: return {"message": "Citizen has no active service requests"} appointment = Appointment.query.filter_by(citizen_id=id) \ .filter_by(office_id=csr.office_id) \ .filter(Appointment.checked_in_time.isnot(None)) \ .first_or_404() # This "un-check-in"s the appointment, returning it to calendar and removing from the queue. appointment.checked_in_time = None db.session.commit() # ARC - Is below necessary? Think not. Causes issue when re-checking in a removed one. # It DOES remove from queue, but stops it from being re-added? # active_service_request.remove_from_queue() # appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True) # if warning: # logging.warning("WARNING: %s", warning) # return {"message": warning}, 422 result = self.appointment_schema.dump(appointment) return {"appointment": result.data, "errors": result.errors}, 200
class CitizenSpecificInvite(Resource): citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @api_call_with_retry def post(self, id): lock = FileLock("lock/invite_citizen.lock") with lock: csr = CSR.find_by_username(g.oidc_token_info['username']) citizen = db.session.query(Citizen).with_lockmode('update').filter_by(citizen_id=id).first() active_service_state = SRState.get_state_by_name("Active") active_service_request = citizen.get_active_service_request() if active_service_request is None: return {"message": "Citizen has no active service requests"}, 400 try: active_service_request.invite(csr, invite_type="specific", sr_count = len(citizen.service_reqs)) except TypeError: return {"message": "Citizen has already been invited"}, 400 active_service_request.sr_state_id = active_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class AppointmentPost(Resource): appointment_schema = AppointmentSchema() citizen_schema = CitizenSchema() @oidc.accept_token(require_token=True) @api_call_with_retry @has_any_role( roles=[Role.internal_user.value, Role.online_appointment_user.value]) 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
class CitizenGenericInvite(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @jwt.has_one_of_roles([Role.internal_user.value]) #@api_call_with_retry def post(self): #print("==> In Python /citizens/invitetest") y = 0 #for x in range(0, 25): key = "DR->" + get_key() #print("") y = y + 1 #print("DATETIME:", datetime.now(), "starting loop:", y, "==>Key : ", key) csr = csr_find_by_user() #print("DATETIME:", datetime.now(), "==>Key : ", key,"===>AFTER CALL TO csr_find_by_user:"******"lock/invite_citizen_{}.lock".format(csr.office_id)) with lock: #active_citizen_state = find_active() active_citizen_state = citizen_state #print("DATETIME:", datetime.now(), "==>Key : ", key, "===>AFTER CALL TO find_Active:", active_citizen_state) waiting_period_state = find_wait() #print("DATETIME:", datetime.now(), "==>Key : ", key, "===>AFTER CALL TO find_wait:", waiting_period_state) citizen = None json_data = request.get_json() if json_data and 'counter_id' in json_data: counter_id = int(json_data.get('counter_id')) else: counter_id = int(csr.counter_id) citizen = find_citizen(counter_id, active_citizen_state, csr, waiting_period_state) # If no matching citizen with the same counter type, get next one if citizen is None: citizen = find_citizen2(active_citizen_state, csr, waiting_period_state) if citizen is None: return {"message": "There is no citizen to invite"}, 400 my_print("==> POST /citizens/invite/ Citizen: " + str(citizen.citizen_id) + ', Ticket: ' + citizen.ticket_number) db.session.refresh(citizen) active_service_request = find_active_sr(citizen) #print("DATETIME:", datetime.now(), "==>Key : ", key, "===>AFTER CALL TO find_active_sr:", citizen) try: invite_active_sr(active_service_request, csr, citizen) #print("DATETIME:", datetime.now(), "==>Key : ", key, "===>AFTER CALL TO invite_active_sr:") except TypeError: return { "message": "Error inviting citizen. Please try again." }, 400 active_service_state = find_active_ss() #print("DATETIME:", datetime.now(), "==>Key : ", key, "===>AFTER CALL TO find_active_ss:", active_service_state) active_service_request.sr_state_id = active_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office.office_name) 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.office_name) #print("DATETIME:", datetime.now(), "end loop: ", y , "==>Key : ", key) return { 'citizen': result, 'errors': self.citizen_schema.validate(citizen) }, 200
class ServiceRequestsList(Resource): citizen_schema = CitizenSchema() service_request_schema = ServiceReqSchema() @oidc.accept_token(require_token=True) @api_call_with_retry 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
class CitizenList(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @jwt.requires_auth def get(self): try: user = g.jwt_oidc_token_info['username'] has_role([Role.internal_user.value], g.jwt_oidc_token_info['realm_access']['roles'], user, "CitizenList GET /citizens/") 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'])) citizens = Citizen.query \ .options(joinedload(Citizen.service_reqs, innerjoin=True).joinedload(ServiceReq.periods).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),raiseload(Citizen.office),raiseload(Citizen.counter),raiseload(Citizen.user)) \ .filter_by(office_id=csr.office_id, cs_id=active_id) \ .order_by(Citizen.priority) result = self.citizens_schema.dump(citizens) return { 'citizens': result, 'errors': self.citizens_schema.validate(citizens) }, 200 except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500 @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry 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
class CitizenGenericInvite(Resource): citizen_schema = CitizenSchema() citizens_schema = CitizenSchema(many=True) @oidc.accept_token(require_token=True) @api_call_with_retry def post(self): csr = CSR.find_by_username(g.oidc_token_info['username']) lock = FileLock("lock/invite_citizen_{}.lock".format(csr.office_id)) with lock: active_citizen_state = CitizenState.query.filter_by( cs_state_name='Active').first() waiting_period_state = PeriodState.get_state_by_name("Waiting") citizen = None json_data = request.get_json() if json_data and 'counter_id' in json_data: counter_id = int(json_data.get('counter_id')) else: counter_id = int(csr.counter_id) citizen = Citizen.query \ .filter_by(counter_id=counter_id, cs_id=active_citizen_state.cs_id, office_id=csr.office_id) \ .join(Citizen.service_reqs) \ .join(ServiceReq.periods) \ .filter_by(ps_id=waiting_period_state.ps_id) \ .filter(Period.time_end.is_(None)) \ .order_by(Citizen.priority, Citizen.citizen_id) \ .first() # If no matching citizen with the same counter type, get next one if citizen is None: citizen = Citizen.query \ .filter_by(cs_id=active_citizen_state.cs_id, office_id=csr.office_id) \ .join(Citizen.service_reqs) \ .join(ServiceReq.periods) \ .filter_by(ps_id=waiting_period_state.ps_id) \ .filter(Period.time_end.is_(None)) \ .order_by(Citizen.priority, Citizen.citizen_id) \ .first() if citizen is None: return {"message": "There is no citizen to invite"}, 400 my_print("==> POST /citizens/invite/ Citizen: " + str(citizen.citizen_id) + ', Ticket: ' + citizen.ticket_number) db.session.refresh(citizen) active_service_request = citizen.get_active_service_request() try: active_service_request.invite(csr, invite_type="generic", sr_count=len( citizen.service_reqs)) except TypeError: return { "message": "Error inviting citizen. Please try again." }, 400 active_service_state = SRState.get_state_by_name("Active") active_service_request.sr_state_id = active_service_state.sr_state_id db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office_id) 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
class CsrSelf(Resource): csr_schema = CSRSchema() citizen_schema = CitizenSchema(many=True) exam_schema = ExamSchema(many=True) exam_type_schema = ExamTypeSchema() timezone = pytz.timezone("US/Pacific") back_office_display = application.config['BACK_OFFICE_DISPLAY'] recurring_feature_flag = application.config['RECURRING_FEATURE_FLAG'] @oidc.accept_token(require_token=True) @api_call_with_retry def get(self): try: csr = CSR.find_by_username(g.oidc_token_info['username']) if not csr: return {'Message': 'User Not Found'}, 404 db.session.add(csr) active_sr_state = SRState.get_state_by_name("Active") today = datetime.now() start_date = self.timezone.localize(today) active_citizens = Citizen.query \ .join(Citizen.service_reqs) \ .filter_by(sr_state_id=active_sr_state.sr_state_id) \ .join(ServiceReq.periods) \ .filter_by(csr_id=csr.csr_id) \ .filter(Period.time_end.is_(None)) individual_exams = Exam.query \ .filter_by(office_id=csr.office_id) \ .filter(Exam.exam_returned_date.is_(None), Exam.expiry_date <= today, Exam.deleted_date.is_(None)) \ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id) \ .filter(ExamType.group_exam_ind == 0).count() individual_exams_past_schedule = Exam.query \ .join(Booking, Exam.booking_id == Booking.booking_id)\ .filter(Booking.start_time < start_date)\ .filter_by(office_id=csr.office_id) \ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id) \ .filter(ExamType.group_exam_ind == 0).count() group_exams = Exam.query \ .filter_by(office_id=csr.office_id) \ .filter(Exam.deleted_date.is_(None)) \ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id) \ .filter(ExamType.group_exam_ind == 1) \ .join(Booking, Exam.booking_id == Booking.booking_id) \ .filter(Booking.sbc_staff_invigilated == 0).count() #.filter(Booking.invigilator_id.is_(None))\ TODO: Update this plz group_attention = Exam.query \ .filter_by(office_id=csr.office_id)\ .filter(Exam.deleted_date.is_(None))\ .filter(Exam.exam_returned_date.is_(None))\ .join(ExamType, Exam.exam_type_id == ExamType.exam_type_id)\ .filter(ExamType.group_exam_ind == 1)\ .join(Booking, Exam.booking_id == Booking.booking_id)\ .filter(Booking.start_time < start_date).count() if group_attention > 0 and individual_exams > 0: group_attention += individual_exams result = self.csr_schema.dump(csr) active_citizens = self.citizen_schema.dump(active_citizens) return {'csr': result.data, 'individual_exams': individual_exams, 'individual_exams_past_schedule': individual_exams_past_schedule, 'group_exams': group_exams, 'group_individual_attention': group_attention, 'active_citizens': active_citizens.data, 'back_office_display': self.back_office_display, 'recurring_feature_flag': self.recurring_feature_flag, 'errors': result.errors} except exc.SQLAlchemyError as e: print(e) return {'message': 'API is down'}, 500
class AppointmentDraftPost(Resource): appointment_schema = AppointmentSchema() citizen_schema = CitizenSchema() # Un-authenticated call as it can happen before user has logged in def post(self): my_print("==> In AppointmentDraftPost, POST /appointments/draft") json_data = request.get_json() office_id = json_data.get('office_id') service_id = json_data.get('service_id') start_time = parse(json_data.get('start_time')) end_time = parse(json_data.get('end_time')) office = Office.find_by_id(office_id) service = Service.query.get(int(service_id)) if service_id else None # end_time can be null for CSRs when they click; whereas citizens know end-time. if not end_time: end_time = add_delta_to_time( start_time, minutes=office.appointment_duration, timezone=office.timezone.timezone_name) # Unauthenticated requests from citizens won't have name, so we set a fallback if (hasattr(g, 'jwt_oidc_token_info') and hasattr(g.jwt_oidc_token_info, 'username')): user = PublicUser.find_by_username( g.jwt_oidc_token_info['username']) citizen_name = user.display_name else: citizen_name = 'Draft' # Delete all expired drafts before checking availability Appointment.delete_expired_drafts() csr = None if (hasattr(g, 'jwt_oidc_token_info')): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) # CSRs are not limited by drafts, can always see other CSRs drafts # This mitigates two CSRs in office creating at same time for same meeting # Ensure there's no race condition when submitting a draft if not csr and 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 # Set draft specific data json_data['is_draft'] = True json_data['citizen_name'] = citizen_name warning = self.appointment_schema.validate(json_data) appointment = self.appointment_schema.load(json_data) if warning: logging.warning("WARNING: %s", warning) return {"message": warning}, 422 db.session.add(appointment) db.session.commit() result = self.appointment_schema.dump(appointment) if not application.config['DISABLE_AUTO_REFRESH']: socketio.emit('appointment_create', result) return {"appointment": result, "warning": warning}, 201
class ServiceRequestsList(Resource): citizen_schema = CitizenSchema() service_request_schema = ServiceReqSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry 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.get(service_request.citizen_id) 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 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 = 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_id) result = self.service_request_schema.dump(service_request) return { 'service_request': result, 'errors': self.service_request_schema.validate(service_request) }, 201
class CitizenAddToQueue(Resource): citizen_schema = CitizenSchema() @jwt.has_one_of_roles([Role.internal_user.value]) @api_call_with_retry def post(self, id): csr = CSR.find_by_username(g.jwt_oidc_token_info['username']) citizen = Citizen.query.filter_by(citizen_id=id).first() active_service_request = citizen.get_active_service_request() my_print("==> POST /citizens/" + str(citizen.citizen_id) + '/add_to_queue, Ticket: ' + citizen.ticket_number) if active_service_request is None: return {"message": "Citizen has no active service requests"} # Figure out what Snowplow call to make. Default is addtoqueue snowplow_call = "addtoqueue" if len(citizen.service_reqs) != 1 or len( active_service_request.periods) != 1: active_period = active_service_request.get_active_period() if active_period.ps.ps_name == "Invited": snowplow_call = "queuefromprep" elif active_period.ps.ps_name == "Being Served": snowplow_call = "returntoqueue" else: # TODO: Put in a Feedback Slack/Service now call here. return {"message": "Invalid citizen/period state. "} active_service_request.add_to_queue(csr, snowplow_call) pending_service_state = SRState.get_state_by_name("Pending") active_service_request.sr_state_id = pending_service_state.sr_state_id # send walkin spot confirmation try: if (citizen.notification_phone or citizen.notification_email ) and not (citizen.reminder_flag) and not ( citizen.notification_sent_time): update_table = False try: appointment_portal_url = application.config.get( 'APPOINTMENT_PORTAL_URL', '') # Dynamic URL creations url = '' if appointment_portal_url and citizen.walkin_unique_id: if appointment_portal_url.endswith('/'): appointment_portal_url = appointment_portal_url[: -1] url = "{}/{}/{}".format(appointment_portal_url, 'walk-in-Q', citizen.walkin_unique_id) # email email_sent = False if citizen.notification_email: officeObj = Office.find_by_id(citizen.office_id) print( 'Sending email for walk in spot confirmations to') email_sent = get_walkin_spot_confirmation_email_contents( citizen, url, officeObj) # SMS sms_sent = False if citizen.notification_phone: sms_sent = send_walkin_spot_confirmation_sms( citizen, url, request.headers['Authorization'].replace( 'Bearer ', '')) if email_sent: status = send_email( request.headers['Authorization'].replace( 'Bearer ', ''), *email_sent) update_table = True if sms_sent: update_table = True except Exception as exc: pprint(f'Error on token generation - {exc}') update_table = False if update_table: citizen.reminder_flag = 0 citizen.notification_sent_time = datetime.utcnow() except Exception as err: logging.error('{}'.format(str(err))) pprint(err) db.session.add(citizen) db.session.commit() socketio.emit('update_customer_list', {}, room=csr.office.office_name) 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.office_name) return { 'citizen': result, 'errors': self.citizen_schema.validate(citizen) }, 200