Example #1
0
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
Example #3
0
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
Example #5
0
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
Example #6
0
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
Example #9
0
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
Example #10
0
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
Example #12
0
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
Example #13
0
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
Example #14
0
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
Example #15
0
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
Example #16
0
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
Example #18
0
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
Example #22
0
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
Example #23
0
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
Example #25
0
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
Example #26
0
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
Example #27
0
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
Example #30
0
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