コード例 #1
0
    def put(self, id):
        json_data = request.get_json()

        if 'counter_id' not in json_data:
            json_data['counter_id'] = counter_id

        if not json_data:
            return {'message': 'No input data received for updating citizen'}, 400

        csr = CSR.find_by_username(g.oidc_token_info['username'])
        citizen = Citizen.query.filter_by(citizen_id=id).first()
        my_print("==> PUT /citizens/" + str(citizen.citizen_id) + '/, Ticket: ' + str(citizen.ticket_number))

        try:
            citizen = self.citizen_schema.load(json_data, instance=citizen, partial=True).data

        except ValidationError as err:
            return {'message': err.messages}, 422

        db.session.add(citizen)
        db.session.commit()

        #  If this put request is the result of an appointment checkin, make a Snowplow call.
        if ('snowplow_addcitizen' in json_data) and (json_data['snowplow_addcitizen'] == True):
            SnowPlow.add_citizen(citizen, csr)

        result = self.citizen_schema.dump(citizen)
        socketio.emit('update_active_citizen', result.data, room=csr.office_id)

        return {'citizen': result.data,
                'errors': result.errors}, 200
コード例 #2
0
    def put(self, id):

        csr = CSR.find_by_username(g.oidc_token_info['username'])

        json_data = request.get_json()

        if not json_data:
            return {"message": "No input data received for updating an appointment"}

        appointment = Appointment.query.filter_by(appointment_id=id)\
                                       .filter_by(office_id=csr.office_id)\
                                       .first_or_404()

        appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True)

        if warning:
            logging.warning("WARNING: %s", warning)
            return {"message": warning}, 422

        db.session.add(appointment)
        db.session.commit()

        #   Make Snowplow call.
        schema = 'appointment_update'
        if "checked_in_time" in json_data:
            schema = 'appointment_checkin'
        SnowPlow.snowplow_appointment(None, csr, appointment, schema)

        result = self.appointment_schema.dump(appointment)

        return {"appointment": result.data,
                    "errors": result.errors}, 200
コード例 #3
0
    def post(self):

        json_data = request.get_json()

        csr = CSR.find_by_username(g.oidc_token_info['username'])
        if not csr:
            raise Exception('no user found with username: `{}`'.format(
                g.oidc_token_info['username']))

        try:
            citizen = self.citizen_schema.load(json_data).data
            citizen.office_id = csr.office_id
            citizen.start_time = datetime.now()

        except ValidationError as err:
            print(err)
            return {"message": err.messages}, 422

        citizen.cs_id = active_id
        citizen.service_count = 1
        db.session.add(citizen)
        db.session.commit()

        SnowPlow.add_citizen(citizen, csr)

        result = self.citizen_schema.dump(citizen)

        return {'citizen': result.data, 'errors': result.errors}, 201
コード例 #4
0
    def put(self, id):
        json_data = request.get_json()

        if not json_data:
            return {'message': 'No input data received for updating citizen'}, 400

        csr = CSR.find_by_username(g.oidc_token_info['username'])

        service_request = ServiceReq.query.filter_by(sr_id=id) \
                .join(ServiceReq.citizen, aliased=True) \
                .filter_by(office_id=csr.office_id).first_or_404()
        try:
            service_request = self.service_request_schema.load(json_data, instance=service_request, partial=True).data

        except ValidationError as err:
            return {'message': err.messages}, 422

        db.session.add(service_request)
        db.session.commit()

        SnowPlow.choose_service(service_request, csr, "chooseservice")

        result = self.service_request_schema.dump(service_request)
        citizen_result = self.citizen_schema.dump(service_request.citizen)
        socketio.emit('update_active_citizen', citizen_result.data, room=csr.office_id)

        return {'service_request': result.data,
                'errors': result.errors}, 200
コード例 #5
0
    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
コード例 #6
0
    def begin_service(self, csr, snowplow_event):
        active_period = self.get_active_period()

        if active_period.ps.ps_name in ["Being Served"]:
            raise TypeError(
                "You cannot begin serving a citizen that is already being served"
            )

        active_period.time_end = datetime.utcnow()
        # db.session.add(active_period)

        period_state_being_served = PeriodState.get_state_by_name(
            "Being Served")

        new_period = Period(sr_id=self.sr_id,
                            csr_id=csr.csr_id,
                            reception_csr_ind=csr.receptionist_ind,
                            ps_id=period_state_being_served.ps_id,
                            time_start=datetime.utcnow())

        self.periods.append(new_period)

        #  Calculate number of active periods, for Snowplow call.
        period_count = len(self.periods)
        SnowPlow.snowplow_event(self.citizen_id,
                                csr,
                                snowplow_event,
                                period_count=period_count,
                                current_sr_number=self.sr_number)
コード例 #7
0
    def post(self, id):
        csr = CSR.find_by_username(g.jwt_oidc_token_info['username'])
        citizen = Citizen.query.filter_by(citizen_id=id).first()
        my_print("==> POST /citizens/" + str(citizen.citizen_id) +
                 '/finish_service/, Ticket: ' + citizen.ticket_number)
        active_service_request = citizen.get_active_service_request()
        inaccurate = request.args.get('inaccurate')

        if active_service_request is None:
            return {"message": "Citizen has no active service requests"}

        #  If citizen here overnight, or inaccurate time flag set, update accurate time flag.
        if citizen.start_time.date() != datetime.now().date(
        ) or inaccurate == 'true':
            citizen.accurate_time_ind = 0

        SnowPlow.snowplow_event(
            citizen.citizen_id,
            csr,
            "finish",
            quantity=active_service_request.quantity,
            current_sr_number=active_service_request.sr_number)

        active_sr_id = active_service_request.sr_id
        active_service_request.finish_service(csr, self.clear_comments_flag)
        citizen_state = CitizenState.query.filter_by(
            cs_state_name="Received Services").first()
        citizen.cs_id = citizen_state.cs_id

        pending_service_state = SRState.get_state_by_name("Complete")
        active_service_request.sr_state_id = pending_service_state.sr_state_id

        # remove walkin unique id when service is finished
        citizen.walkin_unique_id = None

        db.session.add(citizen)
        db.session.commit()

        #  Loop to stop all services in the service stopped state (which are all except the active service)
        if len(citizen.service_reqs) != 1:
            for sr in citizen.service_reqs:
                if sr.sr_id != active_sr_id:
                    SnowPlow.snowplow_event(citizen.citizen_id,
                                            csr,
                                            "finishstopped",
                                            quantity=sr.quantity,
                                            current_sr_number=sr.sr_number)

        socketio.emit('citizen_invited', {},
                      room='sb-%s' % csr.office.office_number)
        result = self.citizen_schema.dump(citizen)
        socketio.emit('update_active_citizen', result, room=csr.office_id)

        return {
            'citizen': result,
            'errors': self.citizen_schema.validate(citizen)
        }, 200
コード例 #8
0
    def post(self, id):
        csr = CSR.find_by_username(g.jwt_oidc_token_info['username'])

        service_request = ServiceReq.query.filter_by(sr_id=id) \
            .join(ServiceReq.citizen, aliased=True) \
            .filter_by(office_id=csr.office_id).first_or_404()

        active_service_state = SRState.get_state_by_name("Active")
        complete_service_state = SRState.get_state_by_name("Complete")

        # Find the currently active service_request and close it
        current_sr_number = 0
        for req in service_request.citizen.service_reqs:
            if req.sr_state_id == active_service_state.sr_state_id:
                req.sr_state_id = complete_service_state.sr_state_id
                req.finish_service(csr, clear_comments=False)
                current_sr_number = req.sr_number
                db.session.add(req)

        # Then set the requested service to active
        service_request.sr_state_id = active_service_state.sr_state_id

        period_state_being_served = PeriodState.get_state_by_name(
            "Being Served")

        new_period = Period(sr_id=service_request.sr_id,
                            csr_id=csr.csr_id,
                            reception_csr_ind=csr.receptionist_ind,
                            ps_id=period_state_being_served.ps_id,
                            time_start=datetime.utcnow())

        db.session.add(new_period)
        db.session.add(service_request)

        db.session.commit()

        #  To make service active, stop current service, restart previous service.
        SnowPlow.snowplow_event(service_request.citizen.citizen_id,
                                csr,
                                "stopservice",
                                current_sr_number=current_sr_number)
        SnowPlow.snowplow_event(service_request.citizen.citizen_id,
                                csr,
                                "restartservice",
                                current_sr_number=service_request.sr_number)

        citizen_result = self.citizen_schema.dump(service_request.citizen)
        socketio.emit('update_active_citizen',
                      citizen_result,
                      room=csr.office_id)
        result = self.service_request_schema.dump(service_request)

        return {
            'service_request': result,
            'errors': self.service_request_schema.validate(service_request)
        }, 200
コード例 #9
0
    def post(self, id):

        csr = CSR.find_by_username(g.oidc_token_info['username'])
        citizen = Citizen.query.filter_by(citizen_id=id, office_id=csr.office_id).first()
        sr_state = SRState.get_state_by_name("Complete")

        #  Create parameters for and make snowplow call.  Default is no service request, CSR pressed cancel.
        quantity = 0
        sr_number = 1
        active_sr = 0
        status = "service-creation"
        if len(citizen.service_reqs) != 0:
            active_service_request = citizen.get_active_service_request()
            quantity = active_service_request.quantity
            sr_number = active_service_request.sr_number
            active_sr = active_service_request.sr_id
            active_period = active_service_request.get_active_period()
            if active_period.ps.ps_name == "Invited":
                status = "at-prep"
            else:
                status = "being-served"

        SnowPlow.snowplow_event(citizen.citizen_id, csr, ("left/" + status),
                                quantity = quantity, current_sr_number= sr_number)

        for service_request in citizen.service_reqs:

            service_request.sr_state_id = sr_state.sr_state_id

            for p in service_request.periods:
                if p.time_end is None:
                    p.time_end = datetime.now()

            #  Make snowplow calls to finish any stopped services
            if service_request.sr_id != active_sr:
                SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped",
                                        quantity = service_request.quantity,
                                        current_sr_number= service_request.sr_number)

        citizen.cs = CitizenState.query.filter_by(cs_state_name='Left before receiving services').first()
        if self.clear_comments_flag:
            citizen.citizen_comments = None

        if citizen.start_time.date() != datetime.now().date():
            citizen.accurate_time_ind = 0

        db.session.add(citizen)
        db.session.commit()

        socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number)
        result = self.citizen_schema.dump(citizen)
        socketio.emit('update_active_citizen', result.data, room=csr.office_id)

        return {'citizen': result.data,
                'errors': result.errors}, 200
コード例 #10
0
    def post(self, id):
        csr = CSR.find_by_username(g.oidc_token_info['username'])
        citizen = Citizen.query.filter_by(citizen_id=id,
                                          office_id=csr.office_id).first()
        active_service_request = citizen.get_active_service_request()
        inaccurate = request.args.get('inaccurate')

        if active_service_request is None:
            return {"message": "Citizen has no active service requests"}

        SnowPlow.snowplow_event(
            citizen.citizen_id,
            csr,
            "finish",
            quantity=active_service_request.quantity,
            current_sr_number=active_service_request.sr_number)

        active_sr_id = active_service_request.sr_id
        active_service_request.finish_service(csr, self.clear_comments_flag)
        citizen_state = CitizenState.query.filter_by(
            cs_state_name="Received Services").first()
        citizen.cs_id = citizen_state.cs_id

        pending_service_state = SRState.get_state_by_name("Complete")
        active_service_request.sr_state_id = pending_service_state.sr_state_id

        if citizen.start_time.date() != datetime.now().date(
        ) or inaccurate == 'true':
            citizen.accurate_time_ind = 0

        db.session.add(citizen)
        db.session.commit()

        #  Loop to stop all services in the service stopped state (which are all except the active service)
        if len(citizen.service_reqs) != 1:
            for sr in citizen.service_reqs:
                if sr.sr_id != active_sr_id:
                    SnowPlow.snowplow_event(citizen.citizen_id,
                                            csr,
                                            "finishstopped",
                                            quantity=sr.quantity,
                                            current_sr_number=sr.sr_number)

        socketio.emit('citizen_invited', {},
                      room='sb-%s' % csr.office.office_number)
        result = self.citizen_schema.dump(citizen)
        socketio.emit('update_active_citizen', result.data, room=csr.office_id)

        return {'citizen': result.data, 'errors': result.errors}, 200
コード例 #11
0
    def delete(self, id):

        csr = CSR.find_by_username(g.oidc_token_info['username'])

        appointment = Appointment.query.filter_by(appointment_id=id)\
                                       .filter_by(office_id=csr.office_id)\
                                       .first_or_404()

        SnowPlow.snowplow_appointment(None, csr, appointment,
                                      'appointment_delete')

        db.session.delete(appointment)
        db.session.commit()

        return {}, 204
コード例 #12
0
    def delete(self, id):

        appointment = Appointment.query.filter_by(appointment_id=id) \
            .first_or_404()

        csr = None if is_public_user() else CSR.find_by_username(
            g.oidc_token_info['username'])

        user: PublicUser = PublicUser.find_by_username(
            g.oidc_token_info['username']) if is_public_user() else None
        if is_public_user():
            # Check if it's a public user
            citizen = Citizen.find_citizen_by_id(appointment.citizen_id)
            if not citizen or citizen.citizen_id != appointment.citizen_id:
                abort(403)

        # Must call this prior to deleting from DB, so cannot
        # combine with repeated is_draft check below
        if not appointment.is_draft:
            SnowPlow.snowplow_appointment(None, csr, appointment,
                                          'appointment_delete')

        db.session.delete(appointment)
        db.session.commit()

        if not application.config['DISABLE_AUTO_REFRESH']:
            socketio.emit('appointment_delete', id)

        # Do not log snowplow events or send emails if it's a draft.
        if not appointment.is_draft:

            # If the appointment is public user's and if staff deletes it send email
            if csr:
                office = Office.find_by_id(appointment.office_id)

                # Send blackout email
                try:
                    pprint('Sending email for appointment cancellation')
                    send_email(
                        generate_ches_token(),
                        *get_cancel_email_contents(appointment, user, office,
                                                   office.timezone))
                except Exception as exc:
                    pprint(f'Error on token generation - {exc}')

        return {}, 204
コード例 #13
0
    def add_to_queue(self, csr, snowplow_event):

        active_period = self.get_active_period()
        active_period.time_end = datetime.now()
        #db.session.add(active_period)

        period_state_waiting = PeriodState.get_state_by_name("Waiting")

        new_period = Period(
            sr_id=self.sr_id,
            csr_id=csr.csr_id,
            reception_csr_ind=csr.receptionist_ind,
            ps_id=period_state_waiting.ps_id,
            time_start=datetime.now()
        )
        self.periods.append(new_period)

        SnowPlow.snowplow_event(self.citizen_id, csr, snowplow_event, current_sr_number=self.sr_number)
コード例 #14
0
    def place_on_hold(self, csr):
        active_period = self.get_active_period()
        active_period.time_end = datetime.now()
        # db.session.add(active_period)

        period_state_on_hold = PeriodState.get_state_by_name("On hold")

        new_period = Period(
            sr_id=self.sr_id,
            csr_id=csr.csr_id,
            reception_csr_ind=csr.receptionist_ind,
            ps_id=period_state_on_hold.ps_id,
            time_start=datetime.now()
        )

        self.periods.append(new_period)

        SnowPlow.snowplow_event(self.citizen_id, csr, "hold", current_sr_number = self.sr_number)
コード例 #15
0
    def invite(self, csr, invite_type, sr_count=1):
        active_period = self.get_active_period()
        if active_period.ps.ps_name in ["Invited", "Being Served", "On hold"]:
            raise TypeError(
                "You cannot invite a citizen that has already been invited")

        #  If a generic invite type, event is either invitecitizen or returninvite.
        if invite_type == "generic":
            #  If only one SR, one period, an invitecitizen call, from First Time in Line state.
            if sr_count == 1 and len(self.periods) == 2:
                snowplow_event = "invitecitizen"
            #  Otherwise from the Back in Line state.
            else:
                snowplow_event = "returninvite"

        #  A specific invite type.  Event is invitefromlist, returnfromlist or invitefromhold
        else:
            #  If only one SR, one period, an invitefromlist call, from First Time in Line state.
            if sr_count == 1 and len(self.periods) == 2:
                snowplow_event = "invitefromlist"
            #  Either from back in line or hold state.
            else:
                if active_period.ps.ps_name == "Waiting":
                    snowplow_event = "returnfromlist"
                else:
                    snowplow_event = "invitefromhold"

        active_period.time_end = datetime.utcnow()
        # db.session.add(active_period)

        period_state_invite = PeriodState.get_state_by_name("Invited")

        new_period = Period(sr_id=self.sr_id,
                            csr_id=csr.csr_id,
                            reception_csr_ind=csr.receptionist_ind,
                            ps_id=period_state_invite.ps_id,
                            time_start=datetime.utcnow())

        self.periods.append(new_period)

        SnowPlow.snowplow_event(self.citizen_id,
                                csr,
                                snowplow_event,
                                current_sr_number=self.sr_number)
コード例 #16
0
    def post(self):

        csr = CSR.find_by_username(g.oidc_token_info['username'])

        #  Create a citizen for later use.
        citizen = self.citizen_schema.load({}).data
        citizen.office_id = csr.office_id
        citizen.qt_xn_citizen_ind = 0;
        citizen_state = CitizenState.query.filter_by(cs_state_name="Appointment booked").first()
        citizen.cs_id = citizen_state.cs_id
        citizen.start_time = datetime.now()
        citizen.service_count = 1
        db.session.add(citizen)
        db.session.commit()

        json_data = request.get_json()

        if not json_data:
            return {"message": "No input data received for creating an appointment"}, 400

        appointment, warning = self.appointment_schema.load(json_data)

        if warning:
            logging.warning("WARNING: %s", warning)
            return {"message": warning}, 422

        if appointment.office_id == csr.office_id:
            appointment.citizen_id = citizen.citizen_id
            db.session.add(appointment)
            db.session.commit()

            SnowPlow.snowplow_appointment(citizen, csr, appointment, 'appointment_create')

            result = self.appointment_schema.dump(appointment)

            return {"appointment": result.data,
                    "errors": result.errors}, 201

        else:
            return {"The Appointment Office ID and CSR Office ID do not match!"}, 403
コード例 #17
0
    def delete(self, id):

        appointment = Appointment.query.filter_by(appointment_id=id)\
                                       .first_or_404()

        csr = None if is_public_user() else CSR.find_by_username(
            g.oidc_token_info['username'])

        user: PublicUser = PublicUser.find_by_username(
            g.oidc_token_info['username']) if is_public_user() else None
        if is_public_user():
            # Check if it's a public user
            citizen = Citizen.find_citizen_by_id(appointment.citizen_id)
            if not citizen or citizen.citizen_id != appointment.citizen_id:
                abort(403)

        SnowPlow.snowplow_appointment(None, csr, appointment,
                                      'appointment_delete')

        db.session.delete(appointment)
        db.session.commit()

        # If the appointment is public user's and if staff deletes it send email
        if csr:
            office = Office.find_by_id(appointment.office_id)

            # Send blackout email
            @copy_current_request_context
            def async_email(subject, email, sender, body):
                send_email(subject, email, sender, body)

            thread = Thread(target=async_email,
                            args=get_cancel_email_contents(
                                appointment, user, office, office.timezone))
            thread.daemon = True
            thread.start()

        return {}, 204
コード例 #18
0
    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
コード例 #19
0
    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
コード例 #20
0
    def post(self):
        try:
            json_data = request.get_json()
        except Exception as error:
            return {"message": str(error)}, 401

        csr = CSR.find_by_username(g.jwt_oidc_token_info['username'])
        service_request, message, code = get_service_request(
            self, json_data, csr)
        if (service_request is None):
            return {"message": message}, code

        active_sr_state = SRState.get_state_by_name("Active")
        complete_sr_state = SRState.get_state_by_name("Complete")
        citizen = None
        try:

            citizen = Citizen.query \
                .options(joinedload(Citizen.service_reqs).joinedload(ServiceReq.periods).options(raiseload(Period.sr),joinedload(Period.csr).raiseload('*')),joinedload(Citizen.office),raiseload(Citizen.user)) \
                .filter_by(citizen_id=service_request.citizen_id)

            citizen = citizen.first()

        except:
            print("==> An exception getting citizen info")
            print("    --> CSR:       " + csr.username)
            print("    --> json_data: " +
                  json.dumps(json_data['service_request']))

        if citizen is None:
            return {"message": "No matching citizen found for citizen_id"}, 400

        service, message, code = get_service(service_request, json_data, csr)
        if (service is None):
            return {"message": message}, code

        # Find the currently active service_request and close it (if it exists)
        current_sr_number = 0
        for req in citizen.service_reqs:
            if req.sr_state_id == active_sr_state.sr_state_id:
                req.sr_state_id = complete_sr_state.sr_state_id
                req.finish_service(csr, clear_comments=False)
                current_sr_number = req.sr_number
                db.session.add(req)

        service_request.sr_state_id = active_sr_state.sr_state_id

        # Only add ticket creation period and ticket number if it's their first service_request
        if len(citizen.service_reqs) == 0:
            period_state_ticket_creation = PeriodState.get_state_by_name(
                "Ticket Creation")

            ticket_create_period = Period(
                csr_id=csr.csr_id,
                reception_csr_ind=csr.receptionist_ind,
                ps_id=period_state_ticket_creation.ps_id,
                time_start=citizen.get_service_start_time(),
                time_end=datetime.now())
            service_request.periods.append(ticket_create_period)

            # Move start_time back 11 hours to account for DST and UTC offsets
            # tickets up till 10pm will not affect ticket numbering on next day
            offset_start_time = citizen.start_time - timedelta(hours=11)

            service_count = ServiceReq.query \
                    .join(ServiceReq.citizen, aliased=True) \
                    .filter(Citizen.start_time >= offset_start_time.strftime("%Y-%m-%d")) \
                    .filter_by(office_id=csr.office_id) \
                    .join(ServiceReq.service, aliased=True) \
                    .filter_by(prefix=service.prefix) \
                    .count()

            citizen.ticket_number = service.prefix + str(service_count)
        else:
            period_state_being_served = PeriodState.get_state_by_name(
                "Being Served")

            ticket_create_period = Period(
                csr_id=csr.csr_id,
                reception_csr_ind=csr.receptionist_ind,
                ps_id=period_state_being_served.ps_id,
                time_start=datetime.now())
            service_request.periods.append(ticket_create_period)

        citizen.cs_id = active_id

        #  If first service, just choose it.  If additional service, more work needed.
        if len(citizen.service_reqs) == 0:
            snowplow_event = "chooseservice"
        else:
            snowplow_event = "additionalservice"

        service_request.sr_number = len(citizen.service_reqs) + 1

        db.session.add(service_request)
        db.session.add(citizen)
        db.session.commit()

        #  If first service, just need a choose service call.
        if snowplow_event == "chooseservice":
            SnowPlow.choose_service(service_request, csr, "chooseservice")

        #  If not first service, need stop service, choose service, and additional service calls.
        else:
            SnowPlow.snowplow_event(citizen.citizen_id,
                                    csr,
                                    "stopservice",
                                    current_sr_number=current_sr_number)
            SnowPlow.choose_service(service_request, csr, "chooseservice")
            SnowPlow.snowplow_event(
                citizen.citizen_id,
                csr,
                "additionalservice",
                current_sr_number=service_request.sr_number)

        citizen_result = self.citizen_schema.dump(citizen)
        socketio.emit('update_active_citizen',
                      citizen_result,
                      room=csr.office.office_name)
        result = self.service_request_schema.dump(service_request)

        return {
            'service_request': result,
            'errors': self.service_request_schema.validate(service_request)
        }, 201
コード例 #21
0
    def put(self, id):
        json_data = request.get_json()
        csr = None
        user = None

        if not json_data:
            return {
                "message": "No input data received for updating an appointment"
            }
        is_public_user_appt = is_public_user()

        # Should delete draft appointment, and free up slot, before booking.
        # Clear up a draft if one was previously created by user reserving this time.
        if json_data.get('appointment_draft_id'):
            draft_id_to_delete = int(json_data['appointment_draft_id'])
            Appointment.delete_draft([draft_id_to_delete])
            if not application.config['DISABLE_AUTO_REFRESH']:
                socketio.emit('appointment_delete', draft_id_to_delete)

        if is_public_user_appt:
            office_id = json_data.get('office_id')
            office = Office.find_by_id(office_id)
            # user = PublicUser.find_by_username(g.oidc_token_info['username'])
            # citizen = Citizen.find_citizen_by_username(g.oidc_token_info['username'], office_id)
            # Validate if the same user has other appointments for same day at same office
            appointments = Appointment.find_by_username_and_office_id(
                office_id=office_id,
                user_name=g.oidc_token_info['username'],
                start_time=json_data.get('start_time'),
                timezone=office.timezone.timezone_name,
                appointment_id=id)
            if appointments and len(
                    appointments) >= office.max_person_appointment_per_day:
                return {
                    "code": "MAX_NO_OF_APPOINTMENTS_REACHED",
                    "message": "Maximum number of appoinments reached"
                }, 400

            # Check for race condition
            start_time = parse(json_data.get('start_time'))
            end_time = parse(json_data.get('end_time'))
            service_id = json_data.get('service_id')
            service = Service.query.get(int(service_id))
            if not AvailabilityService.has_available_slots(
                    office=office,
                    start_time=start_time,
                    end_time=end_time,
                    service=service):
                return {
                    "code":
                    "CONFLICT_APPOINTMENT",
                    "message":
                    "Cannot create appointment due to scheduling conflict.  Please pick another time."
                }, 400

        else:
            csr = CSR.find_by_username(g.oidc_token_info['username'])
            office_id = csr.office_id
            office = Office.find_by_id(office_id)

        appointment = Appointment.query.filter_by(appointment_id=id) \
            .filter_by(office_id=office_id) \
            .first_or_404()

        # If appointment is not made by same user, throw error
        if is_public_user_appt:
            citizen = Citizen.find_citizen_by_id(appointment.citizen_id)
            user = PublicUser.find_by_username(g.oidc_token_info['username'])
            # Should just match based on appointment_id and other info.  Can't have proper auth yet.
            if citizen.user_id != user.user_id:
                abort(403)

        appointment, warning = self.appointment_schema.load(
            json_data, instance=appointment, partial=True)

        if warning:
            logging.warning("WARNING: %s", warning)
            return {"message": warning}, 422

        db.session.add(appointment)
        db.session.commit()

        # Send confirmation email
        try:
            pprint('Sending email for appointment update')
            send_email(
                generate_ches_token(),
                *get_confirmation_email_contents(appointment, office,
                                                 office.timezone, user))
        except Exception as exc:
            pprint(f'Error on token generation - {exc}')

        #   Make Snowplow call.
        schema = 'appointment_update'
        if "checked_in_time" in json_data:
            schema = 'appointment_checkin'

        if not appointment.is_draft:
            SnowPlow.snowplow_appointment(None, csr, appointment, schema)

        result = self.appointment_schema.dump(appointment)

        if not application.config['DISABLE_AUTO_REFRESH']:
            socketio.emit('appointment_update', result.data)

        return {"appointment": result.data, "errors": result.errors}, 200
コード例 #22
0
    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
コード例 #23
0
    def put(self, id):
        json_data = request.get_json()
        csr = None
        user = None

        if not json_data:
            return {"message": "No input data received for updating an appointment"}
        is_public_user_appt = is_public_user()
        if is_public_user_appt:
            office_id = json_data.get('office_id')
            office = Office.find_by_id(office_id)
            # user = PublicUser.find_by_username(g.oidc_token_info['username'])
            # citizen = Citizen.find_citizen_by_username(g.oidc_token_info['username'], office_id)
            # Validate if the same user has other appointments for same day at same office
            appointments = Appointment.find_by_username_and_office_id(office_id=office_id,
                                                                      user_name=g.oidc_token_info['username'],
                                                                      start_time=json_data.get('start_time'),
                                                                      timezone=office.timezone.timezone_name,
                                                                      appointment_id=id)
            if appointments and len(appointments) >= office.max_person_appointment_per_day:
                return {"code": "MAX_NO_OF_APPOINTMENTS_REACHED",
                        "message": "Maximum number of appoinments reached"}, 400

            # Check for race condition
            start_time = parse(json_data.get('start_time'))
            end_time = parse(json_data.get('end_time'))
            if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time):
                return {"code": "CONFLICT_APPOINTMENT",
                        "message": "Cannot create appointment due to conflict in time"}, 400

        else:
            csr = CSR.find_by_username(g.oidc_token_info['username'])
            office_id = csr.office_id
            office = Office.find_by_id(office_id)

        appointment = Appointment.query.filter_by(appointment_id=id) \
            .filter_by(office_id=office_id) \
            .first_or_404()

        # If appointment is not made by same user, throw error
        if is_public_user_appt:
            citizen = Citizen.find_citizen_by_id(appointment.citizen_id)
            user = PublicUser.find_by_username(g.oidc_token_info['username'])
            if citizen.user_id != user.user_id:
                abort(403)

        appointment, warning = self.appointment_schema.load(json_data, instance=appointment, partial=True)

        if warning:
            logging.warning("WARNING: %s", warning)
            return {"message": warning}, 422

        db.session.add(appointment)
        db.session.commit()

        # Send confirmation email
        @copy_current_request_context
        def async_email(subject, email, sender, body):
            send_email(subject, email, sender, body)

        thread = Thread(target=async_email, args=get_confirmation_email_contents(appointment, office, office.timezone, user))
        thread.daemon = True
        thread.start()

        #   Make Snowplow call.
        schema = 'appointment_update'
        if "checked_in_time" in json_data:
            schema = 'appointment_checkin'

        SnowPlow.snowplow_appointment(None, csr, appointment, schema)

        result = self.appointment_schema.dump(appointment)

        return {"appointment": result.data,
                "errors": result.errors}, 200
コード例 #24
0
    def post(self, id):

        my_print("++> POST API call time before csr = statement: " + str(datetime.now()))
        csr = CSR.find_by_username(g.oidc_token_info['username'])
        my_print("    ++> Time before citizen = statement: " + str(datetime.now()))
        citizen = Citizen.query.filter_by(citizen_id=id).first()

        my_print("    ++> Time before citizen ID statement: " + str(datetime.now()))
        citizen_id_string = self.get_citizen_string(citizen)
        citizen_ticket = self.get_ticket_string(citizen)

        my_print("    ++> Time before citizen ticket statement: " + str(datetime.now()))
        my_print("    ++> POST /citizens/" + citizen_id_string + '/citizen_left/, Ticket: ' + citizen_ticket)
        my_print("    ++> Time before sr_state statement: " + str(datetime.now()))
        sr_state = SRState.get_state_by_name("Complete")

        #  Create parameters for and make snowplow call.  Default is no service request, CSR pressed cancel.
        quantity = 0
        sr_number = 1
        active_sr = 0
        status = "service-creation"
        if len(citizen.service_reqs) != 0:
            active_service_request = citizen.get_active_service_request()
            quantity = active_service_request.quantity
            sr_number = active_service_request.sr_number
            active_sr = active_service_request.sr_id
            active_period = active_service_request.get_active_period()
            if active_period.ps.ps_name == "Invited":
                status = "at-prep"
            else:
                status = "being-served"

        my_print("    ++> Time before Snowplow call: " + str(datetime.now()))
        SnowPlow.snowplow_event(citizen.citizen_id, csr, ("left/" + status),
                                quantity = quantity, current_sr_number= sr_number)

        my_print("    ++> Time before closing non-active service requests: " + str(datetime.now()))
        for service_request in citizen.service_reqs:

            service_request.sr_state_id = sr_state.sr_state_id

            for p in service_request.periods:
                if p.time_end is None:
                    p.time_end = datetime.now()

            #  Make snowplow calls to finish any stopped services
            if service_request.sr_id != active_sr:
                SnowPlow.snowplow_event(citizen.citizen_id, csr, "finishstopped",
                                        quantity = service_request.quantity,
                                        current_sr_number= service_request.sr_number)

        my_print("    ++> Time before updating citizen state: " + str(datetime.now()))
        citizen.cs = CitizenState.query.filter_by(cs_state_name='Left before receiving services').first()
        if self.clear_comments_flag:
            citizen.citizen_comments = None

        if citizen.start_time.date() != datetime.now().date():
            citizen.accurate_time_ind = 0

        my_print("    ++> Time before updating citizen database: " + str(datetime.now()))
        db.session.add(citizen)
        my_print("    ++> Time before database commit: " + str(datetime.now()))
        db.session.commit()

        my_print("    ++> Time before socket io invited call: " + str(datetime.now()))
        socketio.emit('citizen_invited', {}, room='sb-%s' % csr.office.office_number)
        my_print("    ++> Time before creating the result: " + str(datetime.now()))
        result = self.citizen_schema.dump(citizen)
        my_print("    ++> Time before socket io update call: " + str(datetime.now()))
        socketio.emit('update_active_citizen', result.data, room=csr.office_id)

        my_print("    ++> Time before return result call: " + str(datetime.now()))
        return {'citizen': result.data,
                'errors': result.errors}, 200
コード例 #25
0
    def post(self):
        json_data = request.get_json()
        if not json_data:
            return {"message": "No input data received for creating an appointment"}, 400

        is_blackout_appt = json_data.get('blackout_flag', 'N') == 'Y'
        csr = None
        user = None
        office = None

        #  Create a citizen for later use.
        citizen = self.citizen_schema.load({}).data

        # Check if the appointment is created by public user. Can't depend on the IDP as BCeID is used by other users as well
        is_public_user_appointment = is_public_user()
        if is_public_user_appointment:
            office_id = json_data.get('office_id')
            user = PublicUser.find_by_username(g.oidc_token_info['username'])
            # Add values for contact info and notes
            json_data['contact_information'] = user.email
            telephone = f'. Phone: {user.telephone}' if user.telephone else ''
            json_data['comments'] = json_data.get('comments', '') + telephone

            citizen.user_id = user.user_id
            citizen.citizen_name = user.display_name

            office = Office.find_by_id(office_id)
            # Validate if the same user has other appointments for same day at same office
            appointments = Appointment.find_by_username_and_office_id(office_id=office_id,
                                                                      user_name=g.oidc_token_info['username'],
                                                                      start_time=json_data.get('start_time'),
                                                                      timezone=office.timezone.timezone_name)
            if appointments and len(appointments) >= office.max_person_appointment_per_day:
                return {"code": "MAX_NO_OF_APPOINTMENTS_REACHED", "message": "Maximum number of appointments reached"}, 400

            # Check for race condition
            start_time = parse(json_data.get('start_time'))
            end_time = parse(json_data.get('end_time'))
            if not AvailabilityService.has_available_slots(office=office, start_time=start_time, end_time=end_time):
                return {"code": "CONFLICT_APPOINTMENT",
                        "message": "Cannot create appointment due to conflict in time"}, 400

        else:
            csr = CSR.find_by_username(g.oidc_token_info['username'])
            office_id = csr.office_id
            office = Office.find_by_id(office_id)

        citizen.office_id = office_id
        citizen.qt_xn_citizen_ind = 0
        citizen_state = CitizenState.query.filter_by(cs_state_name="Appointment booked").first()
        citizen.cs_id = citizen_state.cs_id
        citizen.start_time = datetime.now()
        citizen.service_count = 1

        db.session.add(citizen)
        db.session.commit()

        appointment, warning = self.appointment_schema.load(json_data)
        if is_public_user_appointment:
            appointment.citizen_name = user.display_name
            appointment.online_flag = True

        if warning:
            logging.warning("WARNING: %s", warning)
            return {"message": warning}, 422

        if appointment.office_id == office_id:
            appointment.citizen_id = citizen.citizen_id
            db.session.add(appointment)
            db.session.commit()

            # If staff user is creating a blackout event then send email to all of the citizens with appointments for that period
            if is_blackout_appt:
                appointment_ids_to_delete = []
                appointments_for_the_day = Appointment.get_appointment_conflicts(office_id, json_data.get('start_time'),
                                                                                 json_data.get('end_time'))
                for (cancelled_appointment, office, timezone, user) in appointments_for_the_day:
                    if cancelled_appointment.appointment_id != appointment.appointment_id and not cancelled_appointment.checked_in_time:
                        appointment_ids_to_delete.append(cancelled_appointment.appointment_id)

                        # Send blackout email
                        @copy_current_request_context
                        def async_email(subject, email, sender, body):
                            return send_email(subject, email, sender, body)

                        thread = Thread(target=async_email, args=get_blackout_email_contents(appointment, cancelled_appointment, office, timezone, user))
                        thread.daemon = True
                        thread.start()

                # Delete appointments
                if len(appointment_ids_to_delete) > 0:
                    Appointment.delete_appointments(appointment_ids_to_delete)

            else:
                # Send confirmation email
                @copy_current_request_context
                def async_email(subject, email, sender, body):
                    send_email(subject, email, sender, body)

                thread = Thread(target=async_email, args=get_confirmation_email_contents(appointment, office, office.timezone, user))
                thread.daemon = True
                thread.start()

            SnowPlow.snowplow_appointment(citizen, csr, appointment, 'appointment_create')

            result = self.appointment_schema.dump(appointment)

            return {"appointment": result.data,
                    "errors": result.errors}, 201

        else:
            return {"The Appointment Office ID and CSR Office ID do not match!"}, 403
コード例 #26
0
    def put(self, id):
        json_data = request.get_json()

        if 'counter_id' not in json_data:
            json_data['counter_id'] = counter_id

        if not json_data:
            return {'message': 'No input data received for updating citizen'}, 400

        csr = CSR.find_by_username(g.jwt_oidc_token_info['username'])
        citizen = Citizen.query.filter_by(citizen_id=id).first()
        my_print("==> PUT /citizens/" + str(citizen.citizen_id) + '/, Ticket: ' + str(citizen.ticket_number))
        if not ((json_data.get('is_first_reminder', False) or json_data.get('is_second_reminder', False))):
            try:
                citizen = self.citizen_schema.load(json_data, instance=citizen, partial=True)
            except ValidationError as err:
                return {'message': err.messages}, 422
        else:
            try:
                office_obj = Office.find_by_id(citizen.office_id)
                if (citizen.notification_phone):
                    sms_sent = False
                    # code/function call to send sms notification,
                    sms_sent = send_walkin_reminder_sms(citizen, office_obj, request.headers['Authorization'].replace('Bearer ', ''))
                    if (json_data.get('is_first_reminder', False)):
                        if (sms_sent):
                            citizen.reminder_flag = 1
                            citizen.notification_sent_time = datetime.utcnow()
                    if (json_data.get('is_second_reminder', False)):
                        if (sms_sent):
                            citizen.reminder_flag = 2
                            citizen.notification_sent_time = datetime.utcnow()
                if (citizen.notification_email):
                    # code/function call to send first email notification,
                    email_sent = False
                    email_sent = get_walkin_reminder_email_contents(citizen, office_obj)
                    if email_sent:
                        send_email(request.headers['Authorization'].replace('Bearer ', ''), *email_sent)
                    if (json_data.get('is_first_reminder', False)):
                        if email_sent:
                            citizen.reminder_flag = 1
                            citizen.notification_sent_time = datetime.utcnow()
                    if (json_data.get('is_second_reminder', False)):
                        if email_sent:
                            citizen.reminder_flag = 2
                            citizen.notification_sent_time = datetime.utcnow()
                    
            except ValidationError as err:
                return {'message': err.messages}, 422

        db.session.add(citizen)
        db.session.commit()

        #  If this put request is the result of an appointment checkin, make a Snowplow call.
        if ('snowplow_addcitizen' in json_data) and (json_data['snowplow_addcitizen'] == True):
            SnowPlow.add_citizen(citizen, csr)

        result = self.citizen_schema.dump(citizen)
        citizen = Citizen.query.filter_by(citizen_id=citizen.citizen_id).first()
        socketio.emit('update_active_citizen', result, room=csr.office.office_name)

        return {'citizen': result,
                'errors': self.citizen_schema.validate(citizen)}, 200