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))
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.", })
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.')
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())
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())
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())
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')
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)
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())
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')
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.')