class MessageCenterView(FlaskView):
    route_base = 'message-center'

    def __init__(self):
        self.base = MessageCenterController()
        self.wcc = WritingCenterController()

    @route('/')
    def index(self):
        self.wcc.check_roles_and_route(['Administrator'])
        users = self.base.get_active_users()
        users = sorted(users, key=lambda i: i.lastName)
        roles = self.base.get_roles()
        roles = sorted(roles, key=lambda i: i.id)
        return render_template('message_center/send-email.html', **locals())
    
    @route('/send', methods=['POST'])
    def send(self):
        self.wcc.check_roles_and_route(['Administrator'])
        data = request.form
        # grab the group(s) from the form, use the group id to get the emails of all the people in the group(s)
        # make sure there are no duplicates in the email list
        # need to check that all the stuff is actually filled in, if its not, we need to fill it with an empty value
        subject = data.get('subject')
        message = data.get('message')
        groups = data.getlist('recipients')
        cc_ids = data.getlist('cc')
        bcc_ids = data.getlist('bcc')

        recipients = self.base.get_cc(cc_ids)
        bcc = self.base.get_bcc(groups, bcc_ids)

        if self.base.send_message(subject, message, recipients, bcc, False):
            self.wcc.set_alert('success', 'Email sent successfully!')
        else:
            self.wcc.set_alert('danger', 'Email failed to send.')
        return redirect(url_for('MessageCenterView:index'))
 def __init__(self):
     self.sc = SchedulesController()
     self.wcc = WritingCenterController()
     self.mcc = MessageCenterController()
class SchedulesView(FlaskView):
    route_base = '/schedules/'

    def __init__(self):
        self.sc = SchedulesController()
        self.wcc = WritingCenterController()
        self.mcc = MessageCenterController()

    @route("/create-schedule")
    def create_time_slot(self):
        self.wcc.check_roles_and_route(['Administrator'])
        schedules = self.sc.get_all_schedules()
        return render_template("schedules/create_time_slot.html", **locals())

    @route('/manage-tutor-schedules')
    def manage_tutor_schedules(self):
        self.wcc.check_roles_and_route(['Administrator'])
        schedules = self.sc.get_active_schedules()
        tutors = self.sc.get_active_tutors()
        time_setting = self.sc.get_time_setting()[0]
        return render_template('schedules/manage_tutor_schedules.html',
                               **locals())

    @route('view-tutor-schedules')
    def view_tutor_schedules(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])
        schedules = self.sc.get_active_schedules()
        tutors = self.sc.get_active_tutors()
        time_setting = self.sc.get_time_setting()[0]
        return render_template('schedules/view_tutor_schedule.html',
                               **locals())

    @route('/create', methods=['POST'])
    def create_new_time_slot(self):
        self.wcc.check_roles_and_route(['Administrator'])
        form = request.form

        start_time = form.get('start-time')
        start_time = datetime.strftime(datetime.strptime(start_time, '%H:%M'),
                                       '%H:%M:%S')

        end_time = form.get('end-time')
        end_time = datetime.strftime(datetime.strptime(end_time, '%H:%M'),
                                     '%H:%M:%S')

        is_active = int(form.get('active'))

        created = self.sc.create_time_slot(start_time, end_time, is_active)

        if created:
            self.wcc.set_alert('success', 'Schedule Created Successfully!')
        else:
            self.wcc.set_alert('danger', 'Schedule already exists!')
        return redirect(url_for('SchedulesView:create_time_slot'))

    @route('deactivate-time-slots', methods=['POST'])
    def deactivate_time_slots(self):
        self.wcc.check_roles_and_route(['Administrator'])
        form = request.form
        json_schedule_ids = form.get('jsonScheduleIds')
        schedule_ids = json.loads(json_schedule_ids)
        try:
            for schedule_id in schedule_ids:
                self.sc.deactivate_time_slot(schedule_id)
            self.wcc.set_alert('success',
                               'Time slot(s) deactivated successfully!')
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to deactivate time slot(s).')
        return 'done'  # Return doesn't matter: success or failure take you to the same page. Only the alert changes.

    @route('/add-tutors-to-shifts', methods=['POST'])
    def add_tutors_to_shifts(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form

        start_date = form.get('start-date')
        end_date = form.get('end-date')
        if not start_date or not end_date:
            self.wcc.set_alert('danger',
                               'You must set a start date AND an end date!')
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))
        # Formats the date strings into date objects
        start = datetime.strptime(start_date, '%a %b %d %Y').date()
        end = datetime.strptime(end_date, '%a %b %d %Y').date()
        if start > end:
            self.wcc.set_alert(
                'danger',
                'Start date cannot be further in the future than the end date!'
            )
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))
        multilingual = int(form.get('multilingual'))
        drop_in = int(form.get('drop-in'))
        virtual = int(form.get('virtual'))
        tutors = form.getlist('tutors')
        days = form.getlist('days')
        time_slots = form.getlist('time-slots')

        if tutors[0] == 'Select All Tutors':
            tutors = []
            for tutor in self.sc.get_active_tutors():
                tutors.append(tutor.id)
        success = self.sc.create_tutor_shifts(start, end, multilingual,
                                              drop_in, virtual, tutors, days,
                                              time_slots)
        if not success:
            self.wcc.set_alert(
                'warning',
                'The shifts failed to be scheduled! One or more of the selected day of week never occurs.'
            )
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))
        if success == 'warning':
            self.wcc.set_alert(
                'warning', 'One or more of the shifts failed to be scheduled.')
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))
        self.wcc.set_alert(
            'success', 'Successfully added the tutor(s) to the time slot(s).')
        return redirect(url_for('SchedulesView:manage_tutor_schedules'))

    @route('/show-schedule', methods=['POST'])
    def show_tutor_schedule(self):
        self.wcc.check_roles_and_route(['Administrator', 'Tutor'])
        names = json.loads(request.data).get('tutors')
        if 'view-all' in names:
            tutors = self.sc.get_active_tutors()
            names = []
            for tutor in tutors:
                names.append(str(tutor.id))
        all_tutor_appts = self.sc.get_tutor_appointments(names)
        appointments = []
        # Formats the times to match the fullcalendar desired format
        for tutor_appts in all_tutor_appts:
            for appointment in tutor_appts:
                if appointment.actualStart and appointment.actualEnd:
                    start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                        appointment.actualStart.year,
                        appointment.actualStart.strftime('%m'),
                        appointment.actualStart.strftime('%d'),
                        appointment.actualStart.strftime('%H'),
                        appointment.actualStart.strftime('%M'),
                        appointment.actualStart.strftime('%S'))
                    end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                        appointment.actualEnd.year,
                        appointment.actualEnd.strftime('%m'),
                        appointment.actualEnd.strftime('%d'),
                        appointment.actualEnd.strftime('%H'),
                        appointment.actualEnd.strftime('%M'),
                        appointment.actualEnd.strftime('%S'))
                elif appointment.scheduledStart and appointment.scheduledEnd:
                    start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                        appointment.scheduledStart.year,
                        appointment.scheduledStart.strftime('%m'),
                        appointment.scheduledStart.strftime('%d'),
                        appointment.scheduledStart.strftime('%H'),
                        appointment.scheduledStart.strftime('%M'),
                        appointment.scheduledStart.strftime('%S'))
                    end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                        appointment.scheduledEnd.year,
                        appointment.scheduledEnd.strftime('%m'),
                        appointment.scheduledEnd.strftime('%d'),
                        appointment.scheduledEnd.strftime('%H'),
                        appointment.scheduledEnd.strftime('%M'),
                        appointment.scheduledEnd.strftime('%S'))
                else:
                    start_time = None
                    end_time = None

                tutor = self.sc.get_user_by_id(appointment.tutor_id)
                appointments.append({
                    'id':
                    appointment.id,
                    'studentId':
                    appointment.student_id,
                    'tutorName':
                    '{0} {1}'.format(tutor.firstName, tutor.lastName),
                    'startTime':
                    start_time,
                    'endTime':
                    end_time,
                    'multilingual':
                    appointment.multilingual,
                    'virtual':
                    appointment.online,
                    'dropIn':
                    appointment.dropIn,
                    'sub':
                    appointment.sub
                })

        return jsonify(appointments)

    @route('delete-confirmation', methods=['POST'])
    def confirm_delete(self):
        self.wcc.check_roles_and_route(['Administrator'])
        # Post method that displays a confirmation before appointments within a given range for selected tutors are
        # deleted to make sure the person knows what they are doing
        start_date = str(json.loads(request.data).get('startDate'))
        end_date = str(json.loads(request.data).get('endDate'))
        start = datetime.strptime(start_date, '%a %b %d %Y').date()
        end = datetime.strptime(end_date, '%a %b %d %Y').date()
        tutor_ids = json.loads(request.data).get('tutors')
        names = []
        if start > end:
            invalid_date = True
        if 'view-all' in tutor_ids:
            tutors = self.sc.get_active_tutors()
            for tutor in tutors:
                user = self.sc.get_user_by_id(tutor.id)
                name = '{0} {1}'.format(user.firstName, user.lastName)
                names.append(name)
        else:
            for tutor_id in tutor_ids:
                user = self.sc.get_user_by_id(tutor_id)
                if user:
                    name = '{0} {1}'.format(user.firstName, user.lastName)
                    names.append(name)

        return render_template('schedules/delete_confirmation.html',
                               **locals())

    @route('delete-appointment', methods=['POST'])
    def delete_appointment(self):
        self.wcc.check_roles_and_route(['Administrator'])
        appt_id = str(json.loads(request.data).get('appt_id'))
        deleted = self.sc.delete_appointment(appt_id)
        if deleted:
            if deleted == 'sub':
                return deleted
            else:
                return appt_id
        else:
            self.wcc.set_alert('danger', 'Failed to delete appointment!')
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))

    @route('confirm-delete', methods=['post'])
    def confirm_delete_appointment(self):
        self.wcc.check_roles_and_route(['Administrator'])

        appt_id = str(json.loads(request.data).get('appt_id'))
        deleted = self.sc.confirm_delete_appointment(appt_id)
        if deleted:
            # TODO: probably should send an email here
            return appt_id
        else:
            self.wcc.set_alert('danger', 'Failed to delete appointment!')
            return redirect(url_for('SchedulesView:manage_tutor_schedules'))

    @route('delete-tutor-shifts', methods=['POST'])
    def delete_tutors_from_shifts(self):
        self.wcc.check_roles_and_route(['Administrator'])
        # Post method to delete appointments which selected tutors are running in a given date range
        start_date = str(json.loads(request.data).get('startDate'))
        end_date = str(json.loads(request.data).get('endDate'))
        start = datetime.strptime(start_date, '%a %b %d %Y').date()
        end = datetime.strptime(end_date, '%a %b %d %Y').date()

        # If start > end that means start is further into the future than end in so we should the confirmation html
        # again to tell them to fix that
        if start > end:
            invalid_date = True
            return render_template('schedules/delete_confirmation.html',
                                   **locals())

        # If we get past that check, then we delete the appointment(s) and show the substitution table
        tutor_ids = json.loads(request.data).get('tutors')
        if 'view-all' in tutor_ids:
            tutors = self.sc.get_active_tutors()
            tutor_ids = []
            for ids in tutors:
                tutor_ids.append(str(ids.id))
        sub_appts = self.sc.delete_tutor_shifts(tutor_ids, start, end)
        if sub_appts == 'none':
            return '<h3>All appointments in the selected range were deleted successfully!</h3>'
        if sub_appts:
            return render_template('schedules/sub_table.html',
                                   **locals(),
                                   id_to_user=self.sc.get_user_by_id)
        return '<h3>There weren\'t any appointments within the date range selected!</h3>'

    @route('request-sub', methods=['POST'])
    def request_sub(self):
        self.wcc.check_roles_and_route(['Administrator'])
        # Post method to request a sub for a given appointment or subs for all given appointments
        appt_id = json.loads(request.data).get('apptID')
        appt_id_list = json.loads(request.data).get('apptIDList')
        if appt_id == 'all':
            worked = self.sc.sub_all(appt_id_list)
            if not worked:
                self.wcc.set_alert(
                    'danger',
                    'Failed to request a substitute for all appointments.')
        else:
            worked = self.sc.request_substitute(appt_id)
            if not worked:
                self.wcc.set_alert(
                    'danger',
                    'Failed to request a substitute for appointment id {0}.'.
                    format(appt_id))

        return 'Substitute Requested Successfully'

    @route('get-appointments', methods=['GET'])
    def get_users_appointments(self):
        self.wcc.check_roles_and_route(['Student', 'Tutor', 'Administrator'])

        if flask_session['USERNAME'] in [
                'Student', 'Tutor', 'Administrator', 'Observer'
        ]:
            return ''

        appts = self.sc.get_all_user_appointments(flask_session['USERNAME'])
        appointments = []
        # Formats the times to match the fullcalendar desired format
        for appointment in appts:
            if appointment.actualStart and appointment.actualEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.actualStart.year,
                    appointment.actualStart.strftime('%m'),
                    appointment.actualStart.strftime('%d'),
                    appointment.actualStart.strftime('%H'),
                    appointment.actualStart.strftime('%M'),
                    appointment.actualStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.actualEnd.year,
                    appointment.actualEnd.strftime('%m'),
                    appointment.actualEnd.strftime('%d'),
                    appointment.actualEnd.strftime('%H'),
                    appointment.actualEnd.strftime('%M'),
                    appointment.actualEnd.strftime('%S'))
            elif appointment.scheduledStart and appointment.scheduledEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.scheduledStart.year,
                    appointment.scheduledStart.strftime('%m'),
                    appointment.scheduledStart.strftime('%d'),
                    appointment.scheduledStart.strftime('%H'),
                    appointment.scheduledStart.strftime('%M'),
                    appointment.scheduledStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.scheduledEnd.year,
                    appointment.scheduledEnd.strftime('%m'),
                    appointment.scheduledEnd.strftime('%d'),
                    appointment.scheduledEnd.strftime('%H'),
                    appointment.scheduledEnd.strftime('%M'),
                    appointment.scheduledEnd.strftime('%S'))
            else:
                start_time = None
                end_time = None
            tutor = self.sc.get_user_by_id(appointment.tutor_id)
            appointments.append({
                'id':
                appointment.id,
                'studentId':
                appointment.student_id,
                'tutorName':
                '{0} {1}'.format(tutor.firstName, tutor.lastName),
                'startTime':
                start_time,
                'endTime':
                end_time,
                'multilingual':
                appointment.multilingual,
                'virtual':
                appointment.online,
                'dropIn':
                appointment.dropIn,
                'sub':
                appointment.sub
            })

        return jsonify(appointments)

    def get_sub_appointments(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])
        appts = self.sc.get_sub_appointments()
        appointments = []
        # Formats the times to match the fullcalendar desired format
        for appointment in appts:
            if appointment.actualStart and appointment.actualEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.actualStart.year,
                    appointment.actualStart.strftime('%m'),
                    appointment.actualStart.strftime('%d'),
                    appointment.actualStart.strftime('%H'),
                    appointment.actualStart.strftime('%M'),
                    appointment.actualStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.actualEnd.year,
                    appointment.actualEnd.strftime('%m'),
                    appointment.actualEnd.strftime('%d'),
                    appointment.actualEnd.strftime('%H'),
                    appointment.actualEnd.strftime('%M'),
                    appointment.actualEnd.strftime('%S'))
            elif appointment.scheduledStart and appointment.scheduledEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.scheduledStart.year,
                    appointment.scheduledStart.strftime('%m'),
                    appointment.scheduledStart.strftime('%d'),
                    appointment.scheduledStart.strftime('%H'),
                    appointment.scheduledStart.strftime('%M'),
                    appointment.scheduledStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(
                    appointment.scheduledEnd.year,
                    appointment.scheduledEnd.strftime('%m'),
                    appointment.scheduledEnd.strftime('%d'),
                    appointment.scheduledEnd.strftime('%H'),
                    appointment.scheduledEnd.strftime('%M'),
                    appointment.scheduledEnd.strftime('%S'))
            else:
                start_time = None
                end_time = None
            tutor = self.sc.get_user_by_id(appointment.tutor_id)
            appointments.append({
                'id':
                appointment.id,
                'studentId':
                appointment.student_id,
                'tutorName':
                '{0} {1}'.format(tutor.firstName, tutor.lastName),
                'startTime':
                start_time,
                'endTime':
                end_time,
                'multilingual':
                appointment.multilingual,
                'virtual':
                appointment.online,
                'dropIn':
                appointment.dropIn,
                'sub':
                appointment.sub
            })

        return jsonify(appointments)

    @route('pickup-shift', methods=['POST'])
    def pickup_shift(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])

        if flask_session['USERNAME'] in [
                'Administrator', 'Observer', 'Tutor', 'Student'
        ]:
            self.wcc.set_alert(
                'danger', 'You cannot pick up a shift while acting as a role.')
        else:

            appointment_id = str(json.loads(request.data).get('appt_id'))
            appt = self.sc.get_one_appointment(appointment_id)
            self.mcc.substitute_request_filled(appointment_id)
            # TODO MAYBE EMAIL STUDENT ABOUT TUTOR CHANGE IF APPLICABLE?
            picked_up = self.sc.pickup_shift(appointment_id,
                                             flask_session['USERNAME'])
            if picked_up:
                self.wcc.set_alert('success',
                                   'Successfully picked up the shift!')
            else:
                self.wcc.set_alert('danger', 'Failed to pick up the shift.')

        return 'finished'

    @route('request-subtitute', methods=['POST'])
    def request_substitute(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])
        appointment_id = str(json.loads(request.data).get('appt_id'))
        appt = self.sc.get_one_appointment(appointment_id)
        success = self.sc.request_substitute(appointment_id)
        if success:
            self.mcc.request_substitute(appointment_id)
            self.wcc.set_alert('success',
                               'Successfully requested a substitute!')
        else:
            self.wcc.set_alert('danger', 'Error! Substitute not requested.')
        return 'finished'
class StatisticsView(FlaskView):
    def __init__(self):
        self.sc = StatisticsController()
        self.wcc = WritingCenterController()

    @route('/stats')
    def stats(self):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])
        # Use the default start and end dates to get the first tables of data
        start = flask_session['DATE-SELECTOR-START']
        end = flask_session['DATE-SELECTOR-END']
        value = flask_session['DATE-SELECTOR-VALUE']

        appointments, walk_in_appts, no_show_appts = self.get_statistics_data(start, end, value)

        return render_template('statistics/statistics.html', **locals())

    @route('/hours-worked')
    def hours_worked(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])
        return render_template('statistics/hours_worked.html', **locals())

    @route('/get-hours', methods=['POST'])
    def get_hours_worked(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])

        if flask_session['USERNAME'] in ['Student', 'Tutor', 'Administrator', 'Observer']:
            return "No user selected"

        start = str(json.loads(request.data).get('start'))
        end = str(json.loads(request.data).get('end'))
        start = datetime.strptime(start, '%a %b %d %Y')
        end = datetime.strptime(end, '%a %b %d %Y')
        appointments, time = self.sc.get_appt_hours(start, end, flask_session['USERNAME'])

        user = self.sc.get_user_by_username(flask_session['USERNAME'])
        start = start.strftime('%B %d %Y')
        end = end.strftime('%B %d %Y')
        return render_template('statistics/hours_worked_table.html', **locals(), id_to_user=self.sc.get_user_by_id)

    @route('/handle-stats-change', methods=['POST'])
    def handle_stats_change(self):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])
        start = str(json.loads(request.data).get('startDate'))
        end = str(json.loads(request.data).get('endDate'))
        start = datetime.strptime(start, '%a %b %d %Y')
        end = datetime.strptime(end, '%a %b %d %Y')
        value = str(json.loads(request.data).get('value'))
        stat_id = str(json.loads(request.data).get('id'))
        # We store what 'page' we are on last so check which 'page' we were last on and apply those tables to it
        if stat_id == 'busyness' or flask_session['STATISTICS-PAGE'] == 'busyness' and stat_id != 'course-busyness':

            appointments, walk_in_appts, no_show_appts, busiest_day, busiest_tod, busiest_week, busiest_tutors \
                = self.get_statistics_data(start, end, value, stat_id)
        elif stat_id == 'course-busyness' or flask_session['STATISTICS-PAGE'] == 'course-busyness':
            appointments, walk_in_appts, no_show_appts, courses = self.get_statistics_data(start, end, value, stat_id)
        else:
            # If we aren't on the busyness or course-busyness page, then we are on the homepage
            appointments, walk_in_appts, no_show_appts, = self.get_statistics_data(start, end, value, stat_id)
        # print(flask_session['DATE-SELECTOR-START'])
        return render_template('statistics/statistics_tables.html', **locals())

    def get_statistics_data(self, start, end, value, stat_id=''):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])
        # Set stored values
        flask_session['DATE-SELECTOR-START'] = start
        flask_session['DATE-SELECTOR-END'] = end
        flask_session['DATE-SELECTOR-VALUE'] = value
        # If stat_id is date-appt-change that means that either the date or type of appointment we are viewing has
        # changed so we must check which 'page' we are on by looking at what is stored in the session
        if stat_id == 'date-appt-change':
            stat_id = flask_session['STATISTICS-PAGE']
        else:
            # Else we are changing which 'page' we are saving that we are on
            flask_session['STATISTICS-PAGE'] = stat_id
        # Gets some basic appointment data
        appointments = self.sc.get_appointments(start, end, value)
        walk_in_appts = self.sc.get_walk_in_appointments(start, end, value)
        no_show_appts = self.sc.get_no_show_appointments(start, end, value)
        appts_list = []
        appts_list.extend(appointments)
        appts_list.extend(walk_in_appts)
        # If stat_id == busyness then we are only looking at busyness statistics so only return them to save time
        if stat_id == 'busyness':
            # Used to get the busiest week(s)
            busiest_week = {}
            beginning_of_week = start
            mid_week = start
            # Assume that we are starting on a Sunday
            in_mid_week = False
            # If we aren't starting on a Sunday then we are somewhere in the middle of the week
            if start.weekday() != 6:
                in_mid_week = True
            while start < end:
                # If our iterator, start, is Sunday, we can just keep moving a week forward in time to get our date
                # range
                if start.weekday() == 6:
                    # If we started in the middle of the week, than our first date range is going to be different than
                    # the rest of our date ranges so we have some custom logic for it
                    if in_mid_week:
                        in_mid_week = False
                        beginning_of_week = beginning_of_week.replace(hour=0, minute=0, second=0)
                        mid_week = mid_week.replace(hour=23, minute=59, second=59)
                        week_str = '{0} - {1}'.format(beginning_of_week.strftime('%m/%d/%Y'), mid_week.strftime('%m/%d/%Y'))
                        busiest_week.update({
                            week_str: {
                                'start': beginning_of_week,
                                'end': mid_week,
                                'count': 0
                            }
                        })
                    # Update values
                    beginning_of_week = start
                    start += timedelta(weeks=1)  # Add a week for next session
                    end_of_week = start - timedelta(days=1)
                    beginning_of_week = beginning_of_week.replace(hour=0, minute=0, second=0)
                    end_of_week = end_of_week.replace(hour=23, minute=59, second=59)
                    # If end_of_week is less than our end date, then we are still iterating through the dates so we can
                    # create our current week date range using the beginning_of_week and end_of_week variables
                    if end_of_week < end:
                        week_str = '{0} - {1}'.format(beginning_of_week.strftime('%m/%d/%Y'),
                                                      end_of_week.strftime('%m/%d/%Y'))
                        busiest_week.update({
                            week_str: {
                                'start': beginning_of_week,
                                'end': end_of_week,
                                'count': 0
                            }
                        })
                    else:
                        # If end is greater or equal to end_of_week, then we are stopping at some point midweek, so we
                        # have this custom logic to create the final week date range
                        end = end.replace(hour=23, minute=59, second=59)
                        week_str = '{0} - {1}'.format(beginning_of_week.strftime('%m/%d/%Y'), end.strftime('%m/%d/%Y'))
                        busiest_week.update({
                            week_str: {
                                'start': beginning_of_week,
                                'end': end,
                                'count': 0
                            }
                        })
                else:
                    # Moves us to the next day until we are on a Sunday
                    mid_week = start
                    # If the next day is equal to the end of the date range, then we haven't encountered a Sunday and
                    # thus we should just show our week as the first start value and the end value. Once start is
                    # updated below, we will break out of the while loop
                    if start + timedelta(days=1) == end:
                        beginning_of_week = beginning_of_week.replace(hour=0, minute=0, second=0)
                        end = end.replace(hour=23, minute=59, second=59)
                        week_str = '{0} - {1}'.format(beginning_of_week.strftime('%m/%d/%Y'), end.strftime('%m/%d/%Y'))
                        busiest_week.update({
                            week_str: {
                                'start': beginning_of_week,
                                'end': end,
                                'count': 0
                            }
                        })
                    # else we will increase the day by 1 to keep searching for a Sunday
                    start += timedelta(days=1)  # Adds a day until we are on sunday
            busiest_day = {}
            busiest_tod = {}
            busiest_tutors = {}
            for appt in appts_list:
                # Used to get the busiest day(s)
                if appt.scheduledStart and appt.scheduledEnd:
                    date = appt.scheduledStart.strftime('%b %d %Y')
                else:
                    date = appt.actualStart.strftime("%b %d %Y")
                try:
                    if busiest_day[date] != None:
                        count = busiest_day[date] + 1
                        busiest_day.update({
                            date: count
                        })
                except Exception as e:
                    busiest_day.update({
                        date: 1
                    })
                # Used to get busiest time(s) of day
                if appt.scheduledStart and appt.scheduledEnd:
                    timeslot = '{0} - {1}'.format(self.sc.datetimeformat(appt.scheduledStart),
                                                  self.sc.datetimeformat(appt.scheduledEnd))
                else:
                    timeslot = '{0} - {1} (Walk In)'.format(self.sc.datetimeformat(appt.actualStart),
                                                  self.sc.datetimeformat(appt.actualEnd))
                try:
                    if busiest_tod[timeslot] != None:
                        count = busiest_tod[timeslot] + 1
                        busiest_tod.update({
                            timeslot: count
                        })
                except Exception as e:
                    busiest_tod.update({
                        timeslot: 1
                    })
                # Used to get busiest week(s)
                for week in busiest_week:
                    if appt.scheduledStart and appt.scheduledEnd:
                        time_start = appt.scheduledStart
                        time_end = appt.scheduledEnd
                    else:
                        time_start = appt.actualStart
                        time_end = appt.actualEnd
                    if time_start > busiest_week[week]['start'] and time_end < busiest_week[week]['end']:
                        count = busiest_week[week]['count'] + 1
                        busiest_week[week].update({
                            'count': count
                        })
                # Used to get busiest tutor(s)
                tutor = self.sc.get_user_by_id(appt.tutor_id)
                tutor_str = '{0} {1} ({2})'.format(tutor.firstName, tutor.lastName, tutor.username)
                try:
                    if busiest_tutors[tutor_str] != None:
                        count = busiest_tutors[tutor_str] + 1
                        busiest_tutors.update({
                            tutor_str: count
                        })
                except Exception as e:
                    busiest_tutors.update({
                        tutor_str: 1
                    })
            return appointments, walk_in_appts, no_show_appts, busiest_day, busiest_tod, busiest_week, busiest_tutors
        elif stat_id == 'course-busyness':
            # Else if stat_id == course-busyness we are only looking at courses so only return that data
            courses = {}
            for appt in appts_list:
                # Used to get the Courses
                course_str = '{0} {1}'.format(appt.courseCode, appt.courseSection)
                try:
                    if courses[course_str] != None:
                        count = courses[course_str]['count'] + 1
                        courses[course_str].update({
                            'count': count
                        })
                except Exception as e:
                    if appt.courseCode:
                        course_code = appt.courseCode
                        tag = appt.courseCode[-1:]
                        if tag.isalpha() and tag.isupper():
                            course_code = appt.courseCode[:-1]
                            courses.update({
                                course_str: {
                                    'courseCode': course_code,
                                    'tag': tag,
                                    'section': appt.courseSection,
                                    'profName': appt.profName,
                                    'count': 1
                                }
                            })
                        else:
                            courses.update({
                                course_str: {
                                    'courseCode': course_code,
                                    'tag': '',
                                    'section': appt.courseSection,
                                    'profName': appt.profName,
                                    'count': 1
                                }
                            })
            return appointments, walk_in_appts, no_show_appts, courses
        else:
            # Else we are on the statistics homepage so only return general appointment information
            return appointments, walk_in_appts, no_show_appts
 def __init__(self):
     self.sc = StatisticsController()
     self.wcc = WritingCenterController()
Exemple #6
0
class ProfileView(FlaskView):
    route_base = '/profile'

    def __init__(self):
        self.pc = ProfileController()
        self.wcc = WritingCenterController()
        self.mcc = MessageCenterController()

    @route('/edit')
    def index(self):
        self.wcc.check_roles_and_route(
            ['Student', 'Tutor', 'Observer', 'Administrator'])
        user = self.pc.get_user_by_username(flask_session['USERNAME'])
        preferences = self.mcc.get_email_preferences()
        return render_template('profile/profile.html', **locals())

    @route('/save-edits', methods=['POST'])
    def save_edits(self):
        self.wcc.check_roles_and_route(
            ['Student', 'Tutor', 'Observer', 'Administrator'])
        try:
            form = request.form
            first_name = form.get('first-name')
            last_name = form.get('last-name')
            username = form.get('username')

            if isinstance(
                    form.get('shift'), str
            ):  # if shift is there, the box is checked and should be set to true
                self.mcc.toggle_shift(1)
            else:  # otherwise, it should be set to false
                self.mcc.toggle_shift(0)

            if isinstance(
                    form.get('substitute'), str
            ):  # if sub is there, the box is checked and should be set to true
                self.mcc.toggle_substitute(1)
            else:  # otherwise, it should be set to false
                self.mcc.toggle_substitute(0)

            self.pc.edit_user(first_name, last_name, username)
            # Need to reset the users name, which appears in the upper right corner
            flask_session['NAME'] = '{0} {1}'.format(first_name, last_name)
            flask_session.modified = True
            self.wcc.set_alert('success',
                               'Your profile has been edited successfully!')
        except Exception as error:
            self.wcc.set_alert(
                'danger',
                'Failed to edit your profile: {0}.'.format(str(error)))
        return redirect(url_for('ProfileView:index'))

    @route('/view-role')
    def role_viewer(self):
        self.wcc.check_roles_and_route(['Administrator'])
        role_list = self.pc.get_all_roles()
        return render_template('profile/role_viewer.html', **locals())

    @route('/change-role', methods=['POST'])
    def change_role(self):
        self.wcc.check_roles_and_route(['Administrator'])
        if not flask_session['ADMIN-VIEWER']:
            form = request.form
            role_id = form.get('role')
            role = self.pc.get_role(role_id)
            flask_session['ADMIN-VIEWER'] = True
            # Saving old info to return too
            flask_session['ADMIN-USERNAME'] = flask_session['USERNAME']
            flask_session['ADMIN-ROLES'] = flask_session['USER-ROLES']
            flask_session['ADMIN-NAME'] = flask_session['NAME']
            # Setting up viewing role
            flask_session['USERNAME'] = role.name
            flask_session['NAME'] = ""
            flask_session['USER-ROLES'] = role.name
        return redirect(url_for('View:index'))

    @route('/toggle-substitute', methods=['POST'])
    def toggle_substitute(self):
        self.wcc.check_roles_and_route(['Tutor', 'Observer', 'Administrator'])
        data = request.form
        return self.mcc.toggle_substitute(int(data['substitute']))

    @route('/toggle-shift', methods=['POST'])
    def toggle_shift(self):
        self.wcc.check_roles_and_route(['Tutor', 'Observer', 'Administrator'])
        data = request.form
        return self.mcc.toggle_shift(data['shift'])
Exemple #7
0
 def __init__(self):
     self.pc = ProfileController()
     self.wcc = WritingCenterController()
     self.mcc = MessageCenterController()
 def __init__(self):
     self.ac = AppointmentsController()
     self.wcc = WritingCenterController()
     self.mcc = MessageCenterController()
     self.wsapi = WSAPIController()
class AppointmentsView(FlaskView):
    route_base = 'appointments'

    def __init__(self):
        self.ac = AppointmentsController()
        self.wcc = WritingCenterController()
        self.mcc = MessageCenterController()
        self.wsapi = WSAPIController()

    @route('schedule')
    def schedule_appointment_landing(self):
        self.wcc.check_roles_and_route(['Student', 'Administrator'])

        return render_template('appointments/schedule_appointment.html', **locals())

    @route('view-all-appointments')
    def view_appointments(self):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])

        return render_template('appointments/view_appointments.html', **locals())

    @route('load-appointment-data', methods=['POST'])
    def load_appointment_data(self):
        self.wcc.check_roles_and_route(['Student', 'Tutor', 'Observer', 'Administrator'])

        appt_id = json.loads(request.data).get('id')
        schedule = json.loads(request.data).get('schedule')
        cancel = json.loads(request.data).get('cancel')
        pickup_sub_delete = json.loads(request.data).get('subDelete')
        tutor_edit = json.loads(request.data).get('tutorEdit')
        add_google_calendar = json.loads(request.data).get('gcalAdd', None)
        show_zoom_url = json.loads(request.data).get('zoom-url', None)
        if 'Tutor' not in flask_session['USER-ROLES']:
            tutor_edit = False
        appointment = self.ac.get_appointment_by_id(appt_id)
        walk_in_hours = True if appointment.dropIn == 1 else False
        student = self.ac.get_user_by_id(appointment.student_id)
        student_name = 'None'
        student_email = 'None'
        if student:
            student_name = '{0} {1}'.format(student.firstName, student.lastName)
            student_email = student.email
        tutor = self.ac.get_user_by_id(appointment.tutor_id)
        tutor_name = 'None'
        if tutor:
            tutor_name = '{0} {1}'.format(tutor.firstName, tutor.lastName)
        courses = self.wsapi.get_student_courses(flask_session['USERNAME'])

        zoom_url = self.ac.get_zoom_url()[0]

        return render_template('macros/appointment_modal.html', **locals())

    @route('load-appointments', methods=['POST'])
    def load_appointments(self):
        self.wcc.check_roles_and_route(['Student', 'Observer', 'Administrator'])

        dates = json.loads(request.data).get('dates')
        schedule_appt = json.loads(request.data).get('scheduleAppt')
        start = dates['start']
        end = dates['end']
        start = start.replace("T", " ").split(" ")[0]
        start = datetime.strptime(start, '%Y-%m-%d')
        end = end.replace("T", " ").split(" ")[0]
        end = datetime.strptime(end, '%Y-%m-%d').date() - timedelta(days=1)
        end = datetime.combine(end, datetime.max.time())

        if schedule_appt:
            time_limit = int(self.ac.get_time_limit()[0])
            range_appointments = self.ac.get_open_appointments_in_range(start, end, time_limit)
            open_no_show_appts = self.ac.get_no_show_appointments_in_range(start, end, time_limit)
            range_appointments.extend(open_no_show_appts)
        else:
            range_appointments = self.ac.get_appointments_in_range(start, end)
            walk_in_appointments = self.ac.get_walk_in_appointments_in_range(start, end)
            range_appointments.extend(walk_in_appointments)
        appointments = []
        # Formats the times to match the fullcalendar desired format
        if range_appointments:
            for appointment in range_appointments:
                if appointment.actualStart and appointment.actualEnd:
                    start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.actualStart.year,
                                                                  appointment.actualStart.strftime('%m'),
                                                                  appointment.actualStart.strftime('%d'),
                                                                  appointment.actualStart.strftime('%H'),
                                                                  appointment.actualStart.strftime('%M'),
                                                                  appointment.actualStart.strftime('%S'))
                    end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.actualEnd.year,
                                                                appointment.actualEnd.strftime('%m'),
                                                                appointment.actualEnd.strftime('%d'),
                                                                appointment.actualEnd.strftime('%H'),
                                                                appointment.actualEnd.strftime('%M'),
                                                                appointment.actualEnd.strftime('%S'))
                elif appointment.scheduledStart and appointment.scheduledEnd:
                    start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.scheduledStart.year,
                                                                  appointment.scheduledStart.strftime('%m'),
                                                                  appointment.scheduledStart.strftime('%d'),
                                                                  appointment.scheduledStart.strftime('%H'),
                                                                  appointment.scheduledStart.strftime('%M'),
                                                                  appointment.scheduledStart.strftime('%S'))
                    end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.scheduledEnd.year,
                                                                appointment.scheduledEnd.strftime('%m'),
                                                                appointment.scheduledEnd.strftime('%d'),
                                                                appointment.scheduledEnd.strftime('%H'),
                                                                appointment.scheduledEnd.strftime('%M'),
                                                                appointment.scheduledEnd.strftime('%S'))
                else:
                    start_time = None
                    end_time = None
                tutor = self.ac.get_user_by_id(appointment.tutor_id)
                appointments.append({
                    'id': appointment.id,
                    'studentId': appointment.student_id,
                    'tutorName': '{0} {1}'.format(tutor.firstName, tutor.lastName),
                    'startTime': start_time,
                    'endTime': end_time,
                    'multilingual': appointment.multilingual,
                    'virtual': appointment.online,
                    'dropIn': appointment.dropIn,
                    'sub': appointment.sub
                })
        return jsonify(appointments)

    def appointments_and_walk_ins(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])

        tutor = flask_session['USERNAME']
        appointments = self.ac.get_scheduled_appointments(tutor)
        in_progress_appointments = self.ac.get_in_progress_appointments(tutor)
        in_progress_walk_ins = self.ac.get_in_progress_walk_ins(tutor)
        appointments.extend(in_progress_appointments)
        appointments.extend(in_progress_walk_ins)
        users = {}
        for appt in appointments:
            user = self.ac.get_user_by_id(appt.student_id)
            if user != None:
                name = '{0} {1}'.format(user.firstName, user.lastName)
            else:
                name = ""
            users.update({appt.student_id: name})
        return render_template('appointments/appointments_and_walk_ins.html', **locals())

    @route('/begin-checks', methods=['POST'])
    def begin_walk_in_checks(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])

        username = str(json.loads(request.data).get('username'))
        if not self.wsapi.get_names_from_username(username):
            self.wcc.set_alert('danger', 'Username ' + username + ' is not valid. Please try again with a valid username.')
            return 'invalid username'
        exists = self.ac.check_for_existing_user(username)
        if exists:
            self.ac.reactivate_user(exists.id)
        else:
            name = self.wsapi.get_names_from_username(username)
            self.ac.create_user(username, name)

        courses = self.wsapi.get_student_courses(username)
        return render_template('appointments/appointment_sign_in.html', **locals())

    @route('begin-walk-in', methods=['POST'])
    def begin_walk_in(self):
        self.wcc.check_roles_and_route(['Tutor', 'Administrator'])

        form = request.form
        username = form.get('username')
        course = form.get('course')
        assignment = form.get('assignment')
        multilingual = int(form.get('multi'))
        if 'no-course' == course:
            course = None
        else:
            student_courses = self.wsapi.get_student_courses(username)
            for key in student_courses:
                if student_courses[key]['crn'] == course:
                    course_code = '{0}{1}'.format(student_courses[key]['subject'], student_courses[key]['cNumber'])
                    instructor_email = '{0}@bethel.edu'.format(student_courses[key]['instructorUsername'])
                    course = {
                        'course_code': course_code,
                        'section': student_courses[key]['section'],
                        'instructor': student_courses[key]['instructor'],
                        'instructor_email': instructor_email
                    }
                    break
        user = self.ac.get_user_by_username(username)
        tutor = self.ac.get_user_by_username(flask_session['USERNAME'])
        appt = self.ac.begin_walk_in_appointment(user, tutor, course, assignment, multilingual)
        if not appt:
            self.wcc.set_alert('danger', 'Walk in appointment failed to be started.')
            return self.appointments_and_walk_ins()
        self.wcc.set_alert('success', 'Appointment for ' + user.firstName + ' ' + user.lastName + ' started.')
        return redirect(url_for('AppointmentsView:in_progress_appointment', appt_id=appt.id))

    @route('search-appointments')
    def search_appointments(self):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])

        students = self.ac.get_users_by_role("Student")
        tutors = self.ac.get_users_by_role("Tutor")
        profs = self.ac.get_profs()
        courses = self.ac.get_courses()
        return render_template('appointments/search_appointments.html', **locals())

    @route('view-appointments')
    def student_view_appointments(self):
        self.wcc.check_roles_and_route(['Student', 'Administrator'])

        return render_template('appointments/student_view_appointments.html', **locals())

    @route('get-appointments', methods=['GET'])
    def get_users_appointments(self):
        self.wcc.check_roles_and_route(['Student', 'Administrator'])

        if flask_session['USERNAME'] in ['Student', 'Tutor', 'Administrator', 'Observer']:
            return ''

        appts = self.ac.get_all_user_appointments(flask_session['USERNAME'])
        appointments = []
        # Formats the times to match the fullcalendar desired format
        for appointment in appts:
            if appointment.actualStart and appointment.actualEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.actualStart.year,
                                                              appointment.actualStart.strftime('%m'),
                                                              appointment.actualStart.strftime('%d'),
                                                              appointment.actualStart.strftime('%H'),
                                                              appointment.actualStart.strftime('%M'),
                                                              appointment.actualStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.actualEnd.year,
                                                            appointment.actualEnd.strftime('%m'),
                                                            appointment.actualEnd.strftime('%d'),
                                                            appointment.actualEnd.strftime('%H'),
                                                            appointment.actualEnd.strftime('%M'),
                                                            appointment.actualEnd.strftime('%S'))
            elif appointment.scheduledStart and appointment.scheduledEnd:
                start_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.scheduledStart.year,
                                                              appointment.scheduledStart.strftime('%m'),
                                                              appointment.scheduledStart.strftime('%d'),
                                                              appointment.scheduledStart.strftime('%H'),
                                                              appointment.scheduledStart.strftime('%M'),
                                                              appointment.scheduledStart.strftime('%S'))
                end_time = '{0}-{1}-{2}T{3}:{4}:{5}'.format(appointment.scheduledEnd.year,
                                                            appointment.scheduledEnd.strftime('%m'),
                                                            appointment.scheduledEnd.strftime('%d'),
                                                            appointment.scheduledEnd.strftime('%H'),
                                                            appointment.scheduledEnd.strftime('%M'),
                                                            appointment.scheduledEnd.strftime('%S'))
            else:
                start_time = None
                end_time = None
            tutor = self.ac.get_user_by_id(appointment.tutor_id)
            appointments.append({
                'id': appointment.id,
                'studentId': appointment.student_id,
                'tutorName': '{0} {1}'.format(tutor.firstName, tutor.lastName),
                'startTime': start_time,
                'endTime': end_time,
                'multilingual': appointment.multilingual,
                'dropIn': appointment.dropIn
            })

        return jsonify(appointments)

    @route('schedule-appointment', methods=['POST'])
    def schedule_appointment(self):
        self.wcc.check_roles_and_route(['Student', 'Administrator'])

        appt_id = str(json.loads(request.data).get('appt_id'))
        course = str(json.loads(request.data).get('course'))
        assignment = str(json.loads(request.data).get('assignment'))
        username = flask_session['USERNAME']
        if username in ['Administrator', 'Observer', 'Tutor', 'Student']:
            self.wcc.set_alert('danger', 'You cannot schedule an appointment while acting as a role.')
        else:
            # Checks if the user already exists in WC DB. If a user does, we either continue or reactivate them. If they
            # don't exist then we create them
            exists = self.ac.check_for_existing_user(username)
            if not exists:
                name = self.wsapi.get_names_from_username(username)
                self.ac.create_user(username, name)
            user = self.ac.get_user_by_username(username)
            # Checks to make sure the user isn't banned.
            if not user.bannedDate:
                # Checks to make sure the user is part of CAS.
                roles = self.wsapi.get_roles_for_username(username)
                cas = False
                for role in roles:
                    if 'STUDENT-CAS' == roles[role]['userRole']:
                        cas = True
                if flask_session['USERNAME'] == 'schapr':
                    cas = True
                if cas:
                    appt = self.ac.get_appointment_by_id(appt_id)
                    # Checks to make sure the student hasn't scheduled the limit of appointments per week.
                    appt_limit = int(self.ac.get_appointment_limit()[0])
                    date = appt.scheduledStart
                    weekly_appts = self.ac.get_weekly_users_appointments(user.id, date)
                    if len(weekly_appts) < appt_limit:
                        # Checks to make sure the student isn't scheduled for an appointment that overlaps with the one they
                        # are trying to schedule.
                        already_scheduled = False
                        user_appointments = self.ac.get_future_user_appointments(user.id)
                        for appointment in user_appointments:
                            if appointment.scheduledStart <= appt.scheduledStart < appointment.scheduledEnd or \
                                    appointment.scheduledStart < appt.scheduledEnd <= appointment.scheduledEnd:
                                already_scheduled = True
                        if already_scheduled:
                            self.wcc.set_alert('danger', 'Failed to schedule appointment! You already have an appointment '
                                                         'that overlaps with the one you are trying to schedule.')
                        else:
                            # Sets course to none if no specific course was selected for the appointment.
                            if 'no-course' == course:
                                course = None
                            else:
                                # Gets information about the selected course for the appointment.
                                student_courses = self.wsapi.get_student_courses(username)
                                for key in student_courses:
                                    if student_courses[key]['crn'] == course:
                                        course_code = '{0}{1}'.format(student_courses[key]['subject'],
                                                                      student_courses[key]['cNumber'])
                                        instructor_email = '{0}@bethel.edu'.format(student_courses[key]['instructorUsername'])
                                        course = {
                                            'course_code': course_code,
                                            'section': student_courses[key]['section'],
                                            'instructor': student_courses[key]['instructor'],
                                            'instructor_email': instructor_email
                                        }
                                        break
                            # Schedules the appointment and sends an email to the student and tutor if it is scheduled
                            # successfully.
                            if not self.ac.get_appointment_by_id(appt_id).student_id:
                                appt = self.ac.schedule_appointment(appt_id, course, assignment)
                                if appt:
                                    self.mcc.appointment_signup_student(appt_id)
                                    self.mcc.appointment_signup_tutor(appt_id)
                                    self.wcc.set_alert('success', 'Your Appointment Has Been Scheduled! To View Your '
                                                                  'Appointments, Go To The "View Your Appointments" Page!')
                                else:
                                    self.wcc.set_alert('danger', 'Error! Appointment Not Scheduled!')
                            else:
                                self.wcc.set_alert('danger', 'Appointment has already been scheduled by someone else. Please try again.')
                    else:
                        self.wcc.set_alert('danger', 'Failed to schedule appointment. You already have ' + str(appt_limit) +
                                           ' appointments scheduled and can\'t schedule any more.')
                else:
                    # TODO MAYBE GIVE THEM A SPECIFIC EMAIL TO EMAIL?
                    self.wcc.set_alert('danger', 'Appointment NOT scheduled! Only CAS students can schedule'
                                                 ' appointments here. If you wish to schedule an appointment email a'
                                                 ' Writing Center Administrator.')
            else:
                # TODO MAYBE GIVE THEM A SPECIFIC EMAIL TO EMAIL?
                self.wcc.set_alert('danger', 'You are banned from making appointments! If you have any questions email a'
                                             ' Writing Center Administrator.')
        # Returns the appointment id to remove it from the scheduling calendar.
        return appt_id

    @route('cancel-appointment', methods=['POST'])
    def cancel_appointment(self):
        self.wcc.check_roles_and_route(['Student', 'Administrator'])

        appt_id = str(json.loads(request.data).get('appt_id'))
        cancelled = self.ac.cancel_appointment(appt_id)
        if cancelled:
            self.mcc.cancel_appointment_student(appt_id)
            self.wcc.set_alert('success', 'Successfully cancelled appointment.')
        else:
            self.wcc.set_alert('danger', 'Failed to cancel appointment.')
        return appt_id

    @route('start-appt/<int:appt_id>')
    def start_appointment(self, appt_id):
        try:
            self.ac.start_appointment(appt_id)
            self.wcc.set_alert('success', 'Appointment Started Successfully!')
            return redirect(url_for('AppointmentsView:in_progress_appointment', appt_id=appt_id))
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to start appointment: {0}.'.format(error))
            return redirect(url_for('AppointmentsView:appointments_and_walk_ins'))

    @route('toggle-no-show/<int:appt_id>')
    def toggle_no_show(self, appt_id):
        try:
            appt = self.ac.get_appointment_by_id(appt_id)
            student = self.ac.get_user_by_id(appt.student_id)
            if appt.noShow == 0:
                self.ac.mark_no_show(appt_id)
                self.ac.ban_if_no_show_check(appt.student_id)
                self.wcc.set_alert('success', '{0} {1} successfully marked as no show.'.format(student.firstName, student.lastName))
            else:
                self.ac.revert_no_show(appt_id)
                self.wcc.set_alert('success', '{0} {1} no longer marked as no show.'.format(student.firstName, student.lastName))
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to toggle no show: {0}.'.format(error))
        return redirect(url_for('AppointmentsView:appointments_and_walk_ins'))

    @route('toggle-multilingual/<int:appt_id>')
    def toggle_multilingual(self, appt_id):
        try:
            appt = self.ac.get_appointment_by_id(appt_id)
            student = self.ac.get_user_by_id(appt.student_id)
            if appt.multilingual == 0:
                self.ac.mark_multilingual(appt_id)
                self.wcc.set_alert('success', '{0} {1}\'s appointment successfully marked as multilingual.'.format(student.firstName, student.lastName))
            else:
                self.ac.revert_multilingual(appt_id)
                self.wcc.set_alert('success', '{0} {1}\'s appointment no longer marked as multilingual.'.format(student.firstName, student.lastName))
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to toggle multilingual: {0}.'.format(error))
        return redirect(url_for('AppointmentsView:appointments_and_walk_ins'))

    @route('end-appt/<int:appt_id>', methods=['post', 'get'])
    def end_appointment(self, appt_id):
        form = request.form
        course = form.get('course')
        assignment = form.get('assignment')
        notes = form.get('notes')
        suggestions = form.get('suggestions')
        ferpa_agreement = True if form.get('ferpa') == 'on' else False
        if 'no-course' == course:
            course = None
        else:
            appt = self.ac.get_appointment_by_id(appt_id)
            student = self.ac.get_user_by_id(appt.student_id)
            student_courses = self.wsapi.get_student_courses(student.username)
            for key in student_courses:
                if student_courses[key]['crn'] == course:
                    course_code = '{0}{1}'.format(student_courses[key]['subject'], student_courses[key]['cNumber'])
                    instructor_email = '{0}@bethel.edu'.format(student_courses[key]['instructorUsername'])
                    course = {
                        'course_code': course_code,
                        'section': student_courses[key]['section'],
                        'instructor': student_courses[key]['instructor'],
                        'instructor_email': instructor_email
                    }
                    break

        try:
            self.ac.end_appointment(appt_id, course, assignment, notes, suggestions)
            self.mcc.close_session_student(appt_id)
            if ferpa_agreement:
                self.mcc.end_appt_prof(appt_id)
            qualtrics_link = self.ac.get_survey_link()[0]
            self.wcc.set_alert('success', 'Appointment ended successfully!')
            return render_template('appointments/end_appointment.html', **locals())
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to end appointment: {0}.'.format(error))
            return redirect(url_for('AppointmentsView:in_progress_appointment', appt_id=appt_id))

    @route('in-progress/<int:appt_id>')
    def in_progress_appointment(self, appt_id):
        appt = self.ac.get_appointment_by_id(appt_id)
        student = self.ac.get_user_by_id(appt.student_id)
        courses = self.wsapi.get_student_courses(student.username)
        zoom_url = self.ac.get_zoom_url()[0]
        return render_template('appointments/in_progress_appointment.html', **locals())

    @route('save-changes', methods=['POST'])
    def save_changes(self):
        self.wcc.check_roles_and_route(['Tutor'])

        appt_id = json.loads(request.data).get('appt_id')
        assignment = str(json.loads(request.data).get('assignment'))
        notes = str(json.loads(request.data).get('notes'))
        suggestions = str(json.loads(request.data).get('suggestions'))
        success = self.ac.tutor_change_appt(appt_id, assignment, notes, suggestions)
        if success:
            return 'close'
        else:
            return 'failed'

    @route('/search', methods=['POST'])
    def search(self):
        self.wcc.check_roles_and_route(['Observer', 'Administrator'])

        form = request.form
        student = None if form.get('student') == 'None' else int(form.get('student'))
        tutor = None if form.get('tutor') == 'None' else int(form.get('tutor'))
        prof = None if form.get('prof') == 'None' else form.get('prof')
        course = None if form.get('course') == 'None' else form.get('course')
        start = form.get('start')
        start_date = None if start == '' else datetime.strptime(start, "%a %b %d %Y")
        end = form.get('end')
        end_date = None if end == '' else datetime.strptime(end, "%a %b %d %Y")
        # If no parameters sent in return the following message
        if student is None and tutor is None and prof is None and course is None and start_date is None and end_date is None:
            return 'Please enter parameters to search by.'
        appointments = self.ac.search_appointments(student, tutor, prof, course, start_date, end_date)
        appts_and_info = {}
        for appt in appointments:
            appts_and_info[appt] = {
                'student': self.ac.get_user_by_id(appt.student_id),
                'tutor': self.ac.get_user_by_id(appt.tutor_id)
            }
        return render_template('appointments/appointment_search_table.html', **locals())

    @route('/edit/<int:appt_id>', methods=['GET', 'POST'])
    def edit(self, appt_id):
        self.wcc.check_roles_and_route(['Administrator'])

        appt = self.ac.get_appointment_by_id(appt_id)
        all_tutors = self.ac.get_users_by_role('Tutor')
        all_students = self.ac.get_all_users()
        all_profs = self.ac.get_profs_and_emails()
        all_courses = self.ac.get_courses()
        return render_template('appointments/edit_appointment.html', **locals())

    @route('/submit-edits', methods=['POST'])
    def submit_edits(self):
        self.wcc.check_roles_and_route(['Administrator'])
        
        form = request.form

        appt_id = int(form.get('id'))
        tutor_id = None if form.get('tutor') == '-1' else int(form.get('tutor'))
        student_id = None if form.get('student') == '-1' else int(form.get('student'))

        date = None if form.get('date') == '' else form.get('date')
        sched_start_time = None if form.get('sched-start') == '' else form.get('sched-start')
        sched_end_time = None if form.get('sched-end') == '' else form.get('sched-end')
        if not date:
            self.wcc.set_alert('danger', 'You must select a date.')
            return redirect(url_for('AppointmentsView:edit', appt_id=appt_id))
        if sched_start_time and sched_end_time:
            sched_start_time = "{0} {1}".format(datetime.strptime(date, '%a %b %d %Y').strftime("%Y-%m-%d"), sched_start_time)
            sched_end_time = "{0} {1}".format(datetime.strptime(date, '%a %b %d %Y').strftime("%Y-%m-%d"), sched_end_time)

        actual_start_time = None if form.get('actual-start') == '' else form.get('actual-start')
        actual_end_time = None if form.get('actual-end') == '' else form.get('actual-end')
        actual_start = None if not actual_start_time else "{0} {1}".format(datetime.strptime(date, '%a %b %d %Y').strftime("%Y-%m-%d"), actual_start_time)
        actual_end = None if not actual_end_time else "{0} {1}".format(datetime.strptime(date, '%a %b %d %Y').strftime("%Y-%m-%d"), actual_end_time)

        prof = None if form.get('prof') == 'None' else form.get('prof')
        prof_email = None if form.get('email') == 'None' else form.get('email')
        course = None if form.get('course') == 'None' else form.get('course')
        section = None if form.get('section') == 'None' else int(form.get('section'))
        assignment = None if form.get('assignment') == 'None' else form.get('assignment')
        notes = None if form.get('notes') == 'None' else form.get('notes')
        suggestions = None if form.get('suggestions') == 'None' else form.get('suggestions')
        sub = int(form.get('sub-req'))
        drop_in = int(form.get('drop-in-check'))
        multilingual = int(form.get('multi-check'))
        virtual = int(form.get('virtual-check'))
        no_show = int(form.get('no-show-check'))
        in_progress = int(form.get('in-progress-check'))

        try:
            self.ac.edit_appt(appt_id, student_id, tutor_id, sched_start_time, sched_end_time, actual_start, actual_end, prof,
                              prof_email, drop_in, virtual, sub, assignment, notes, suggestions, multilingual, course, section,
                              no_show, in_progress)
            self.wcc.set_alert('success', 'Appointment edited successfully!')
        except Exception as e:
            self.wcc.set_alert('danger', 'Failed to edit appointment: {0}.'.format(str(e)))
        return redirect(url_for('AppointmentsView:edit', appt_id=appt_id))
 def __init__(self):
     self.sc = SettingsController()
     self.wcc = WritingCenterController()
class SettingsView(FlaskView):
    def __init__(self):
        self.sc = SettingsController()
        self.wcc = WritingCenterController()

    @route('/')
    def index(self):
        self.wcc.check_roles_and_route(['Administrator'])
        settings = self.sc.get_settings()
        return render_template('settings/index.html', **locals())

    @route('change-settings', methods=['POST'])
    def change_settings(self):
        self.wcc.check_roles_and_route(['Administrator'])
        form = request.form
        setting_name = form.get('setting_name')
        new_setting = form.get('new_setting')
        try:
            self.sc.update_setting(setting_name, new_setting)
            self.wcc.set_alert('success', 'Settings updated successfully!')
            return 'success'
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to update settings: {0}.'.format(str(error)))
            return 'failed'

    @route('cleanse', methods=['get'])
    def cleanse(self):
        self.wcc.check_roles_and_route(['Administrator'])
        try:
            self.sc.cleanse()
            self.wcc.set_alert('success', 'System cleansed successfully!')
        except Exception as error:
            self.wcc.set_alert('danger', 'Failed to cleanse system: {0}.'.format(str(error)))
        return redirect(url_for('SettingsView:index'))
 def __init__(self):
     self.base = MessageCenterController()
     self.wcc = WritingCenterController()
 def __init__(self):
     self.uc = UsersController()
     self.wsapi = WSAPIController()
     self.wcc = WritingCenterController()
class UsersView(FlaskView):
    route_base = '/user'

    def __init__(self):
        self.uc = UsersController()
        self.wsapi = WSAPIController()
        self.wcc = WritingCenterController()

    @route('/manage-bans/')
    def manage_bans(self):
        self.wcc.check_roles_and_route(['Administrator'])

        users = self.uc.get_banned_users()

        return render_template('users/manage_bans.html', **locals())

    @route('/view-users')
    def view_all_users(self):
        self.wcc.check_roles_and_route(['Administrator'])

        users_query = self.uc.get_users()
        users = {}
        for user, role in users_query:
            try:
                if users[user.id]['roles'] != None:
                    roles = '{0}, {1}'.format(users[user.id]['roles'],
                                              role.name)
                    users.update({
                        user.id: {
                            'id': user.id,
                            'username': user.username,
                            'firstName': user.firstName,
                            'lastName': user.lastName,
                            'email': user.email,
                            'roles': roles
                        }
                    })
            except:
                users.update({
                    user.id: {
                        'id': user.id,
                        'username': user.username,
                        'firstName': user.firstName,
                        'lastName': user.lastName,
                        'email': user.email,
                        'roles': role.name
                    }
                })

        return render_template('users/view_all_users.html', **locals())

    @route("/add-user")
    def add_user(self):
        self.wcc.check_roles_and_route(['Administrator'])

        return render_template('users/add_user.html', **locals())

    @route("/search-users", methods=['POST'])
    def search_users(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        first_name = form.get('firstName')
        last_name = form.get('lastName')
        results = self.wsapi.get_username_from_name(first_name, last_name)
        return render_template('users/user_search_results.html', **locals())

    @route('/create/<username>/<first_name>/<last_name>')
    def select_user_roles(self, username, first_name, last_name):
        self.wcc.check_roles_and_route(['Administrator'])

        roles = self.uc.get_all_roles()
        existing_user = self.uc.get_user_by_username(username)
        if existing_user:  # User exists in system
            if existing_user.deletedAt:  # Has been deactivated in the past
                success = self.uc.activate_existing_user(existing_user.id)
                if success:
                    message = 'This user has been deactivated in the past, but now they are reactivated with their '\
                              'same roles.'
                else:
                    message = 'Failed to reactivate the user.'
        return render_template('users/select_user_roles.html', **locals())

    @route('/create-user', methods=['POST'])
    def create_user(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        first_name = form.get('first-name')
        last_name = form.get('last-name')
        username = form.get('username')
        roles = form.getlist('roles')
        sub_email_pref = 0  # Default sending emails to No
        stu_email_pref = 0  # Default sending emails to No
        # If the user is a administrator or a professor, they get emails.
        if 'Administrator' in roles:
            sub_email_pref = 1
            stu_email_pref = 1
        if 'Tutor' in roles:
            stu_email_pref = 1
        try:
            self.uc.create_user(first_name, last_name, username,
                                sub_email_pref, stu_email_pref)
            self.uc.set_user_roles(username, roles)
            self.wcc.set_alert(
                'success', '{0} {1} ({2}) added successfully!'.format(
                    first_name, last_name, username))
            return redirect(url_for('UsersView:view_all_users'))
        except Exception as error:
            self.wcc.set_alert('danger',
                               'Failed to add user: {0}.'.format(str(error)))
            return redirect(
                url_for('UsersView:select_user_roles',
                        username=username,
                        first_name=first_name,
                        last_name=last_name))

    @route("/edit/<int:user_id>")
    def edit_user(self, user_id):
        self.wcc.check_roles_and_route(['Administrator'])

        user = self.uc.get_user_by_id(user_id)
        roles = self.uc.get_all_roles()
        user_role_ids = self.uc.get_user_role_ids(user_id)

        return render_template('users/edit_user.html', **locals())

    @route('/save-user-edits', methods=['POST'])
    def save_user_edits(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        user_id = form.get('user-id')
        username = form.get('username')
        first_name = form.get('first-name')
        last_name = form.get('last-name')
        email = form.get('email')
        roles = form.getlist('roles')
        try:
            self.uc.update_user_info(user_id, first_name, last_name, email)
            self.uc.clear_current_roles(user_id)
            self.uc.set_user_roles(username, roles)
            self.wcc.set_alert(
                'success',
                '{0} {1} edited successfully!'.format(first_name, last_name))
            return redirect(url_for('UsersView:view_all_users'))
        except Exception as error:
            self.wcc.set_alert('danger',
                               'Failed to edit user: {0}.'.format(str(error)))
            return redirect(url_for('UsersView:edit_user', user_id=user_id))

    @route("/remove-ban/", methods=['POST'])
    def remove_ban(self):
        self.wcc.check_roles_and_route(['Administrator'])

        user_id = str(json.loads(request.data).get('id'))
        self.uc.remove_user_ban(user_id)
        return redirect(url_for('UsersView:manage_bans'))

    @route("/unban-all", methods=['POST'])
    def remove_all_bans(self):
        self.wcc.check_roles_and_route(['Administrator'])

        self.uc.remove_all_bans()
        return redirect(url_for('UsersView:manage_bans'))

    @route('/search-ban-users', methods=['POST'])
    def search_ban_users(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        first_name = form.get('firstName')
        last_name = form.get('lastName')
        users = self.uc.get_users_by_name(first_name, last_name)
        return render_template('users/user_ban_search_results.html',
                               **locals())

    @route('/ban/user/', methods=['POST'])
    def save_user_ban(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        username = form.get('username')
        user = self.uc.get_user_by_username(username)
        appointments = self.uc.get_future_user_appointments(user.id)
        for appt in appointments:
            self.uc.cancel_appointment(appt.id)
        if flask_session['USERNAME'] == username:
            self.wcc.set_alert('danger', 'Error! You Can\'t Ban Yourself!')
        else:
            self.uc.ban_user(username)
        return redirect(url_for('UsersView:manage_bans'))

    def act_as_user(self, user_id):
        self.wcc.check_roles_and_route(['Administrator'])

        if not flask_session['ADMIN-VIEWER']:
            user_info = self.uc.get_user_by_id(user_id)
            flask_session['ADMIN-VIEWER'] = True
            # Saving old info to return to
            flask_session['ADMIN-USERNAME'] = flask_session['USERNAME']
            flask_session['ADMIN-ROLES'] = flask_session['USER-ROLES']
            flask_session['ADMIN-NAME'] = flask_session['NAME']
            # Setting up viewing role
            flask_session['USERNAME'] = user_info.username
            flask_session['NAME'] = '{0} {1}'.format(user_info.firstName,
                                                     user_info.lastName)
            flask_session['USER-ROLES'] = []
            user_roles = self.uc.get_user_roles(user_info.id)
            for role in user_roles:
                flask_session['USER-ROLES'].append(role.name)
        return redirect(url_for('View:index'))

    @route('/reset-act-as', methods=['POST'])
    def reset_act_as(self):
        if flask_session['ADMIN-VIEWER']:
            try:
                # Resetting info
                flask_session['USERNAME'] = flask_session['ADMIN-USERNAME']
                flask_session['ADMIN-VIEWER'] = False
                flask_session['NAME'] = flask_session['ADMIN-NAME']
                flask_session['USER-ROLES'] = flask_session['ADMIN-ROLES']
                # Clearing out unneeded variables
                flask_session.pop('ADMIN-USERNAME')
                flask_session.pop('ADMIN-ROLES')
                flask_session.pop('ADMIN-NAME')
                return redirect(url_for('View:index'))
            except Exception as error:
                self.wcc.set_alert(
                    'danger', 'An error occurred: {0}.'.format(str(error)))
                return redirect(url_for('View:index'))
        else:
            self.wcc.set_alert(
                'danger',
                'You do not have permission to access this function.')
            return redirect(url_for('View:index'))

    @route('/deactivate/<int:user_id>', methods=['POST', 'GET'])
    def deactivate_user(self, user_id):
        self.wcc.check_roles_and_route(['Administrator'])

        try:
            self.uc.deactivate_user(user_id)
            self.wcc.set_alert('success', 'Users deactivated successfully!')
            return redirect(url_for("UsersView:view_all_users"))
        except Exception as e:
            self.wcc.set_alert('danger', 'Failed to deactivate user(s).')
            return redirect(url_for("UsersView:edit", user_id=user_id))

    @route("/deactivate-users", methods=['post'])
    def deactivate_users(self):
        self.wcc.check_roles_and_route(['Administrator'])

        form = request.form
        json_user_ids = form.get('jsonUserIds')
        user_ids = json.loads(json_user_ids)
        try:
            for user in user_ids:
                self.uc.deactivate_user(user)
            self.wcc.set_alert('success', 'User(s) deactivated successfully!')
        except Exception as error:
            self.wcc.set_alert(
                'danger',
                'Failed to deactivate user(s): {0}.'.format(str(error)))
        return 'done'  # Return doesn't matter: success or failure take you to the same page. Only the alert changes.