Beispiel #1
0
def unschedule():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    course = SisSection.get_course(term_id, section_id, include_deleted=True) if (term_id and section_id) else None

    if not course:
        raise BadRequestError('Required params missing or invalid')

    if not (course['scheduled'] or course['hasNecessaryApprovals']):
        raise BadRequestError(f'Section id {section_id}, term id {term_id} is not currently scheduled or queued for scheduling')

    Approval.delete(term_id=term_id, section_id=section_id)
    Scheduled.delete(term_id=term_id, section_id=section_id)

    event_id = (course.get('scheduled') or {}).get('kalturaScheduleId')
    if event_id:
        try:
            Kaltura().delete(event_id)
        except (KalturaClientException, KalturaException) as e:
            message = f'Failed to delete Kaltura schedule: {event_id}'
            app.logger.error(message)
            app.logger.exception(e)
            send_system_error_email(
                message=f'{message}\n\n<pre>{traceback.format_exc()}</pre>',
                subject=message,
            )

    CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=True,
    )
    return tolerant_jsonify(SisSection.get_course(term_id, section_id, include_deleted=True))
Beispiel #2
0
def approve():
    term_id = app.config['CURRENT_TERM_ID']
    term_name = term_name_for_sis_id(term_id)

    params = request.get_json()
    publish_type = params.get('publishType')
    recording_type = params.get('recordingType')
    section_id = params.get('sectionId')

    course = SisSection.get_course(term_id, section_id) if section_id else None

    if not course or publish_type not in get_all_publish_types() or recording_type not in get_all_recording_types():
        raise BadRequestError('One or more required params are missing or invalid')

    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError('Sorry, request unauthorized')

    if Approval.get_approval(approved_by_uid=current_user.uid, section_id=section_id, term_id=term_id):
        raise ForbiddenRequestError(f'You have already approved recording of {course["courseName"]}, {term_name}')

    meetings = course.get('meetings', {}).get('eligible', [])
    if len(meetings) != 1:
        raise BadRequestError('Unique eligible meeting pattern not found for course')
    meeting = meetings[0]

    location = meeting and meeting.get('location')
    room = Room.find_room(location=location)
    if not room:
        raise BadRequestError(f'{location} is not eligible for Course Capture.')

    previous_approvals = Approval.get_approvals_per_section_ids(section_ids=[section_id], term_id=term_id)
    approval = Approval.create(
        approved_by_uid=current_user.uid,
        approver_type_='admin' if current_user.is_admin else 'instructor',
        course_display_name=course['label'],
        publish_type_=publish_type,
        recording_type_=recording_type,
        room_id=room.id,
        section_id=section_id,
        term_id=term_id,
    )

    if previous_approvals:
        # Compare the current approval with preferences submitted in previous approval
        previous_approval = previous_approvals[-1]
        if (approval.publish_type, approval.recording_type) != (previous_approval.publish_type, previous_approval.recording_type):
            notify_instructors_of_changes(course, approval, previous_approvals)

    all_approvals = previous_approvals + [approval]
    if len(course['instructors']) > len(all_approvals):
        approval_uids = [a.approved_by_uid for a in all_approvals]
        pending_instructors = [i for i in course['instructors'] if i['uid'] not in approval_uids]
        last_approver = next((i for i in course['instructors'] if i['uid'] == approval.approved_by_uid), None)
        if last_approver:
            notify_instructor_waiting_for_approval(course, last_approver, pending_instructors)

    return tolerant_jsonify(_after_approval(course=SisSection.get_course(term_id, section_id)))
def _validate_date_range(start_date, end_date, blackout_id=None):
    if start_date > end_date:
        raise BadRequestError(
            'Start date must be less than or equal to end date.')

    for blackout in Blackout.all_blackouts():
        if blackout.id != blackout_id:
            latest_start = max(start_date, blackout.start_date)
            earliest_end = min(end_date, blackout.end_date)
            if max(0, (earliest_end - latest_start).days + 1) > 0:
                raise BadRequestError(
                    f'Date range overlaps with existing blackout {blackout.id}'
                )
def queue_emails():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    template_type = params.get('emailTemplateType')

    if not (term_id and section_id and template_type):
        raise BadRequestError('Required parameters are missing.')
    course = SisSection.get_course(term_id=term_id, section_id=section_id)
    for instructor in course['instructors']:
        if not QueuedEmail.create(section_id=section_id, recipient=instructor, template_type=template_type, term_id=term_id):
            raise BadRequestError(f"Failed to queue email of type '{template_type}'.")
    return tolerant_jsonify({
        'message': f"An email of type '{template_type}' has been queued.",
    })
Beispiel #5
0
def queue_emails():
    params = request.get_json()
    term_id = params.get('termId')
    section_ids = params.get('sectionIds')
    template_type = params.get('emailTemplateType')

    if term_id and section_ids and template_type:
        section_ids_already_queued = QueuedEmail.get_all_section_ids(
            template_type=template_type, term_id=term_id)
        section_ids_to_queue = [
            id_ for id_ in section_ids if id_ not in section_ids_already_queued
        ]

        for section_id in section_ids_to_queue:
            QueuedEmail.create(section_id=section_id,
                               template_type=template_type,
                               term_id=term_id)

        section_id_count = len(section_ids)
        queued_count = len(section_ids_to_queue)
        if queued_count < section_id_count:
            message = f"""
                {len(section_ids_already_queued)} '{template_type}' emails were already queued up for the
                {'course' if section_id_count == 1 else 'courses'} you submitted.
                Thus, {'no' if queued_count == 0 else f'only {queued_count}'} emails added to the queue.
            """
        else:
            message = f'{queued_count} \'{template_type}\' emails will be sent.'
        return tolerant_jsonify({
            'message': message,
        })
    else:
        raise BadRequestError('Required parameters are missing.')
Beispiel #6
0
def update_room_capability():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if not room or 'capability' not in params:
        raise BadRequestError('Missing required parameters')
    capability = params.get('capability')
    room = Room.update_capability(room_id, capability)
    return tolerant_jsonify(room.to_api_json())
Beispiel #7
0
def update_opt_out():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')

    course = SisSection.get_course(term_id, section_id) if (term_id and section_id) else None
    opt_out = params.get('optOut')
    if not course or opt_out is None:
        raise BadRequestError('Required params missing or invalid')
    if course['scheduled']:
        raise BadRequestError('Cannot update opt-out on scheduled course')

    preferences = CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=opt_out,
    )
    return tolerant_jsonify(preferences.to_api_json())
def update_schedule():
    params = request.get_json()
    job_id = params.get('jobId')
    schedule_type = params.get('type')
    schedule_value = params.get('value')

    if not job_id or not schedule_type or not schedule_value:
        raise BadRequestError('Required parameters are missing.')
    job = Job.get_job(job_id=job_id)
    if not job.disabled or JobHistory.is_job_running(job_key=job.key):
        raise BadRequestError(
            'You cannot edit job schedule if job is either enabled or running.'
        )
    job = Job.update_schedule(job_id=job_id,
                              schedule_type=schedule_type,
                              schedule_value=schedule_value)

    background_job_manager.restart()

    return tolerant_jsonify(job.to_api_json())
Beispiel #9
0
def approve():
    term_id = app.config['CURRENT_TERM_ID']
    term_name = term_name_for_sis_id(term_id)

    params = request.get_json()
    publish_type = params.get('publishType')
    recording_type = params.get('recordingType')
    section_id = params.get('sectionId')
    course = SisSection.get_course(term_id, section_id) if section_id else None

    if not course or publish_type not in get_all_publish_types() or recording_type not in get_all_recording_types():
        raise BadRequestError('One or more required params are missing or invalid')

    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError('Sorry, request unauthorized')

    if Approval.get_approval(approved_by_uid=current_user.uid, section_id=section_id, term_id=term_id):
        raise ForbiddenRequestError(f'You have already approved recording of {course["courseName"]}, {term_name}')

    location = course['meetingLocation']
    room = Room.find_room(location=location)
    if not room:
        raise BadRequestError(f'{location} is not eligible for Course Capture.')

    previous_approvals = Approval.get_approvals_per_section_ids(section_ids=[section_id], term_id=term_id)
    approval = Approval.create(
        approved_by_uid=current_user.uid,
        approver_type_='admin' if current_user.is_admin else 'instructor',
        cross_listed_section_ids=[c['sectionId'] for c in course['crossListings']],
        publish_type_=publish_type,
        recording_type_=recording_type,
        room_id=room.id,
        section_id=section_id,
        term_id=term_id,
    )
    _notify_instructors_of_approval(
        approval=approval,
        course=course,
        previous_approvals=previous_approvals,
    )
    return tolerant_jsonify(SisSection.get_course(term_id, section_id))
def job_disable():
    params = request.get_json()
    job_id = params.get('jobId')
    disable = params.get('disable')

    if not job_id or disable is None:
        raise BadRequestError('Required parameters are missing.')
    job = Job.update_disabled(job_id=job_id, disable=disable)

    background_job_manager.restart()

    return tolerant_jsonify(job.to_api_json())
Beispiel #11
0
def auditorium():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if room:
        is_auditorium = params.get('isAuditorium')
        if not room_id or is_auditorium is None:
            raise BadRequestError("'roomId' and 'isAuditorium' are required.")
        room = Room.set_auditorium(room_id, is_auditorium)
        return tolerant_jsonify(room.to_api_json())
    else:
        raise ResourceNotFoundError('No such room')
Beispiel #12
0
def find_courses():
    params = request.get_json()
    term_id = params.get('termId')
    filter_ = params.get('filter', 'Not Invited')
    if filter_ not in get_search_filter_options() or not term_id:
        raise BadRequestError('One or more required params are missing or invalid')

    if filter_ == 'Do Not Email':
        courses = SisSection.get_courses_opted_out(term_id)
    elif filter_ == 'Invited':
        courses = SisSection.get_courses_invited(term_id)
    elif filter_ == 'Not Invited':
        courses = SisSection.get_eligible_courses_not_invited(term_id)
    elif filter_ == 'Partially Approved':
        courses = SisSection.get_courses_partially_approved(term_id)
    elif filter_ == 'Scheduled':
        courses = SisSection.get_courses_scheduled(term_id)
    else:
        raise BadRequestError(f'Invalid filter: {filter_}')

    return tolerant_jsonify(courses)
Beispiel #13
0
def _get_courses_per_filter(filter_, term_id):
    if filter_ not in get_search_filter_options() or not term_id:
        raise BadRequestError('One or more required params are missing or invalid')

    if filter_ == 'All':
        courses = SisSection.get_courses(term_id)
    elif filter_ == 'Do Not Email':
        courses = SisSection.get_courses_opted_out(term_id)
    elif filter_ == 'Invited':
        courses = SisSection.get_courses_invited(term_id)
    elif filter_ == 'Not Invited':
        courses = SisSection.get_eligible_courses_not_invited(term_id)
    elif filter_ == 'Partially Approved':
        courses = SisSection.get_courses_partially_approved(term_id)
    elif filter_ == 'Queued for Scheduling':
        courses = SisSection.get_courses_queued_for_scheduling(term_id)
    elif filter_ == 'Scheduled':
        courses = SisSection.get_courses_scheduled_standard_dates(term_id)
    elif filter_ == 'Scheduled (Nonstandard Dates)':
        courses = SisSection.get_courses_scheduled_nonstandard_dates(term_id)
    else:
        raise BadRequestError(f'Invalid filter: {filter_}')
    return courses
def create_blackout():
    params = request.get_json()
    name = params.get('name')
    start_date = params.get('startDate')
    end_date = params.get('endDate')
    if None in [name, start_date, end_date]:
        raise BadRequestError('Required parameters are missing.')

    start_date = _local_blackout_date_to_utc(f'{start_date}T00:00:00')
    end_date = _local_blackout_date_to_utc(f'{end_date}T23:59:59')
    _validate_date_range(start_date, end_date)

    blackout = Blackout.create(name=name,
                               start_date=start_date,
                               end_date=end_date)
    return tolerant_jsonify(blackout.to_api_json())
Beispiel #15
0
def create():
    params = request.get_json()
    template_type = params.get('templateType')
    name = params.get('name')
    subject_line = params.get('subjectLine')
    message = params.get('message')

    if None in [template_type, name, subject_line, message]:
        raise BadRequestError('Required parameters are missing.')

    email_template = EmailTemplate.create(
        template_type=template_type,
        name=name,
        subject_line=subject_line,
        message=message,
    )
    return tolerant_jsonify(email_template.to_api_json())
def update_template():
    params = request.get_json()
    template_id = params.get('templateId')
    email_template = EmailTemplate.get_template(template_id) if template_id else None
    if email_template:
        template_type = params.get('templateType')
        name = params.get('name')
        subject_line = params.get('subjectLine')
        message = params.get('message')

        if None in [template_type, name, subject_line, message]:
            raise BadRequestError('Required parameters are missing.')

        email_template = EmailTemplate.update(
            template_id=template_id,
            template_type=template_type,
            name=name,
            subject_line=subject_line,
            message=message,
        )
        return tolerant_jsonify(email_template.to_api_json())
    else:
        raise ResourceNotFoundError('No such email template')
def update_blackout():
    params = request.get_json()
    blackout_id = params.get('blackoutId')
    blackout = Blackout.get_blackout(blackout_id) if blackout_id else None
    if blackout:
        name = params.get('name')
        start_date = params.get('startDate')
        end_date = params.get('endDate')
        if None in [name, start_date, end_date]:
            raise BadRequestError('Required parameters are missing.')

        start_date = _local_blackout_date_to_utc(f'{start_date}T00:00:00')
        end_date = _local_blackout_date_to_utc(f'{end_date}T23:59:59')
        _validate_date_range(start_date, end_date)

        blackout = Blackout.update(
            blackout_id=blackout_id,
            name=name,
            start_date=start_date,
            end_date=end_date,
        )
        return tolerant_jsonify(blackout.to_api_json())
    else:
        raise ResourceNotFoundError('No such email template')
Beispiel #18
0
 def _raise_error():
     raise BadRequestError(f'Invalid day_count: {day_count}')
def _local_blackout_date_to_utc(blackout_date):
    try:
        return localized_timestamp_to_utc(blackout_date)
    except ValueError:
        raise BadRequestError(
            f'{blackout_date} does not match expected date format.')