def test_has_obsolete_meeting_dates(self, client, admin_session): """Admins can see meeting date changes that might disrupt scheduled recordings.""" with test_approvals_workflow(app): meeting = get_eligible_meeting(section_id=section_1_id, term_id=self.term_id) obsolete_meeting_end_date = '2020-04-01' assert meeting['endDate'] != obsolete_meeting_end_date Scheduled.create( instructor_uids=get_instructor_uids(term_id=self.term_id, section_id=section_1_id), kaltura_schedule_id=random.randint(1, 10), meeting_days=meeting['days'], meeting_end_date=obsolete_meeting_end_date, meeting_end_time=meeting['endTime'], meeting_start_date=meeting['startDate'], meeting_start_time=meeting['startTime'], publish_type_='kaltura_my_media', recording_type_='presentation_audio', room_id=Room.get_room_id(section_id=section_1_id, term_id=self.term_id), section_id=section_1_id, term_id=self.term_id, ) std_commit(allow_test_environment=True) api_json = self._api_course_changes(client, term_id=self.term_id) course = _find_course(api_json=api_json, section_id=section_1_id) assert course assert course['scheduled']['hasObsoleteRoom'] is False assert course['scheduled']['hasObsoleteDates'] is True assert course['scheduled']['hasObsoleteTimes'] is False assert course['scheduled']['hasObsoleteInstructors'] is False
def test_has_obsolete_instructors(self, client, admin_session): """Admins can see instructor changes that might disrupt scheduled recordings.""" with test_approvals_workflow(app): meeting = get_eligible_meeting(section_id=section_1_id, term_id=self.term_id) instructor_uids = get_instructor_uids(term_id=self.term_id, section_id=section_1_id) # Course has multiple instructors; we will schedule using only one instructor UID. assert len(instructor_uids) > 1 scheduled_with_uid = instructor_uids[0] Scheduled.create( instructor_uids=[scheduled_with_uid], kaltura_schedule_id=random.randint(1, 10), meeting_days=meeting['days'], meeting_end_date=get_recording_end_date(meeting), meeting_end_time=meeting['endTime'], meeting_start_date=get_recording_start_date(meeting, return_today_if_past_start=True), meeting_start_time=meeting['startTime'], publish_type_='kaltura_my_media', recording_type_='presenter_audio', room_id=Room.get_room_id(section_id=section_1_id, term_id=self.term_id), section_id=section_1_id, term_id=self.term_id, ) std_commit(allow_test_environment=True) api_json = self._api_course_changes(client, term_id=self.term_id) course = _find_course(api_json=api_json, section_id=section_1_id) assert course assert course['scheduled']['hasObsoleteRoom'] is False assert course['scheduled']['hasObsoleteDates'] is False assert course['scheduled']['hasObsoleteTimes'] is False assert course['scheduled']['hasObsoleteInstructors'] is True assert len(course['instructors']) == 2 assert len(course['scheduled']['instructors']) == 1 assert course['scheduled']['instructors'][0]['uid'] == scheduled_with_uid
def mock_scheduled( section_id, term_id, meeting=None, override_days=None, override_end_date=None, override_end_time=None, override_room_id=None, override_start_date=None, override_start_time=None, publish_type='kaltura_media_gallery', recording_type='presenter_presentation_audio', ): meeting = meeting or get_eligible_meeting(section_id=section_id, term_id=term_id) Scheduled.create( course_display_name=f'term_id:{term_id} section_id:{section_id}', instructor_uids=get_instructor_uids(term_id=term_id, section_id=section_id), kaltura_schedule_id=random.randint(1, 10), meeting_days=override_days or meeting['days'], meeting_end_date=override_end_date or get_recording_end_date(meeting), meeting_end_time=override_end_time or meeting['endTime'], meeting_start_date=override_start_date or get_recording_start_date(meeting, return_today_if_past_start=True), meeting_start_time=override_start_time or meeting['startTime'], publish_type_=publish_type, recording_type_=recording_type, room_id=override_room_id or Room.get_room_id(section_id=section_id, term_id=term_id), section_id=section_id, term_id=term_id, ) std_commit(allow_test_environment=True)
def test_has_instructors(self, client, admin_session): """Admins can see instructor changes that might disrupt scheduled recordings.""" with test_approvals_workflow(app): meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times( term_id=self.term_id, section_id=section_3_id, ) instructor_uids = SisSection.get_instructor_uids(term_id=self.term_id, section_id=section_3_id) Scheduled.create( cross_listed_section_ids=[], instructor_uids=instructor_uids + ['999999'], meeting_days=meeting_days, meeting_start_time=meeting_start_time, meeting_end_time=meeting_end_time, publish_type_='canvas', recording_type_='presenter_audio', room_id=Room.get_room_id(section_id=section_3_id, term_id=self.term_id), section_id=section_3_id, term_id=self.term_id, ) std_commit(allow_test_environment=True) api_json = self._api_course_changes(client, term_id=self.term_id) course = _find_course(api_json=api_json, section_id=section_3_id) assert course assert course['scheduled']['hasObsoleteRoom'] is False assert course['scheduled']['hasObsoleteMeetingTimes'] is False assert course['scheduled']['hasObsoleteInstructors'] is True
def test_admin_alert_multiple_meeting_patterns(self): """Emails admin if course is scheduled with weird start/end dates.""" with test_approvals_workflow(app): with enabled_job(job_key=AdminEmailsJob.key()): term_id = app.config['CURRENT_TERM_ID'] section_id = 50014 room_id = Room.find_room('Barker 101').id # The course has two instructors. instructor_uid = get_instructor_uids(section_id=section_id, term_id=term_id)[0] approval = Approval.create( approved_by_uid=instructor_uid, approver_type_='instructor', publish_type_='kaltura_my_media', recording_type_='presenter_audio', room_id=room_id, section_id=section_id, term_id=term_id, ) # Uh oh! Only one of them has been scheduled. meeting = get_eligible_meeting(section_id=section_id, term_id=term_id) Scheduled.create( instructor_uids=[instructor_uid], kaltura_schedule_id=random.randint(1, 10), meeting_days=meeting['days'], meeting_end_date=get_recording_end_date(meeting), meeting_end_time=meeting['endTime'], meeting_start_date=get_recording_start_date( meeting, return_today_if_past_start=True), meeting_start_time=meeting['startTime'], publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=room_id, section_id=section_id, term_id=term_id, ) courses = SisSection.get_courses_scheduled_nonstandard_dates( term_id=term_id) course = next( (c for c in courses if c['sectionId'] == section_id), None) assert course # Message queued but not sent. admin_uid = app.config['EMAIL_DIABLO_ADMIN_UID'] AdminEmailsJob(simply_yield).run() queued_messages = QueuedEmail.query.filter_by( section_id=section_id).all() assert len(queued_messages) == 1 for queued_message in queued_messages: assert '2020-08-26 to 2020-10-02' in queued_message.message # Message sent. QueuedEmailsJob(simply_yield).run() emails_sent = SentEmail.get_emails_sent_to(uid=admin_uid) assert len(emails_sent) == 1 assert emails_sent[ 0].template_type == 'admin_alert_multiple_meeting_patterns' assert emails_sent[0].section_id == section_id
def test_alert_admin_of_instructor_change(self): """Emails admin when a scheduled course gets a new instructor.""" with test_approvals_workflow(app): with enabled_job(job_key=AdminEmailsJob.key()): term_id = app.config['CURRENT_TERM_ID'] section_id = 50005 room_id = Room.find_room('Barker 101').id # The course has two instructors. instructor_1_uid, instructor_2_uid = get_instructor_uids( section_id=section_id, term_id=term_id) approval = Approval.create( approved_by_uid=instructor_1_uid, approver_type_='instructor', course_display_name= f'term_id:{term_id} section_id:{section_id}', publish_type_='kaltura_my_media', recording_type_='presenter_audio', room_id=room_id, section_id=section_id, term_id=term_id, ) # Uh oh! Only one of them has been scheduled. meeting = get_eligible_meeting(section_id=section_id, term_id=term_id) Scheduled.create( course_display_name= f'term_id:{term_id} section_id:{section_id}', instructor_uids=[instructor_1_uid], kaltura_schedule_id=random.randint(1, 10), meeting_days=meeting['days'], meeting_end_date=get_recording_end_date(meeting), meeting_end_time=meeting['endTime'], meeting_start_date=get_recording_start_date( meeting, return_today_if_past_start=True), meeting_start_time=meeting['startTime'], publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=room_id, section_id=section_id, term_id=term_id, ) admin_uid = app.config['EMAIL_DIABLO_ADMIN_UID'] email_count = _get_email_count(admin_uid) # Message queued but not sent. AdminEmailsJob(simply_yield).run() assert _get_email_count(admin_uid) == email_count queued_messages = QueuedEmail.query.filter_by( template_type='admin_alert_instructor_change').all() assert len(queued_messages) == 1 for snippet in [ 'LAW 23', 'Old instructor(s) Regan MacNeil', 'New instructor(s) Regan MacNeil, Burke Dennings' ]: assert snippet in queued_messages[0].message # Message sent. QueuedEmailsJob(simply_yield).run() assert _get_email_count(admin_uid) == email_count + 1
def test_alert_admin_of_room_change(self, db_session): """Emails admin when a scheduled course gets a room change.""" with test_approvals_workflow(app): with enabled_job(job_key=AdminEmailsJob.key()): term_id = app.config['CURRENT_TERM_ID'] section_id = 50004 approved_by_uid = '10004' the_old_room = 'Wheeler 150' scheduled_in_room = Room.find_room(the_old_room) approval = Approval.create( approved_by_uid=approved_by_uid, approver_type_='instructor', publish_type_='kaltura_media_gallery', recording_type_='presenter_audio', room_id=scheduled_in_room.id, section_id=section_id, term_id=term_id, ) meeting = get_eligible_meeting(section_id=section_id, term_id=term_id) Scheduled.create( instructor_uids=get_instructor_uids(term_id=term_id, section_id=section_id), kaltura_schedule_id=random.randint(1, 10), meeting_days=meeting['days'], meeting_end_date=get_recording_end_date(meeting), meeting_end_time=meeting['endTime'], meeting_start_date=get_recording_start_date( meeting, return_today_if_past_start=True), meeting_start_time=meeting['startTime'], publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=scheduled_in_room.id, section_id=section_id, term_id=term_id, ) admin_uid = app.config['EMAIL_DIABLO_ADMIN_UID'] # Message queued, then sent. AdminEmailsJob(simply_yield).run() QueuedEmailsJob(simply_yield).run() emails_sent = SentEmail.get_emails_sent_to(uid=admin_uid) assert len(emails_sent) == 1 assert emails_sent[0].section_id == section_id assert emails_sent[ 0].template_type == 'admin_alert_room_change'
def test_alert_admin_of_instructor_change(self): """Emails admin when a scheduled course gets a new instructor.""" with test_approvals_workflow(app): term_id = app.config['CURRENT_TERM_ID'] section_id = 22287 approved_by_uid = '8765432' room_id = Room.find_room('Barker 101').id approval = Approval.create( approved_by_uid=approved_by_uid, approver_type_='instructor', cross_listed_section_ids=[], publish_type_='canvas', recording_type_='presenter_audio', room_id=room_id, section_id=section_id, term_id=term_id, ) meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times( term_id=term_id, section_id=section_id, ) Scheduled.create( cross_listed_section_ids=approval.cross_listed_section_ids, instructor_uids=SisSection.get_instructor_uids( term_id=term_id, section_id=section_id), meeting_days=meeting_days, meeting_start_time=meeting_start_time, meeting_end_time=meeting_end_time, publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=room_id, section_id=section_id, term_id=term_id, ) admin_uid = app.config['EMAIL_DIABLO_ADMIN_UID'] email_count = _get_email_count(admin_uid) std_commit(allow_test_environment=True) AdminEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) assert _get_email_count(admin_uid) > email_count
def test_alert_admin_of_room_change(self): """Emails admin when a scheduled course gets a room change.""" with test_approvals_workflow(app): term_id = app.config['CURRENT_TERM_ID'] section_id = 26094 approved_by_uid = '6789' the_old_room = 'Wheeler 150' scheduled_in_room = Room.find_room(the_old_room) approval = Approval.create( approved_by_uid=approved_by_uid, approver_type_='instructor', cross_listed_section_ids=[], publish_type_='kaltura_media_gallery', recording_type_='presenter_audio', room_id=scheduled_in_room.id, section_id=section_id, term_id=term_id, ) meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times( term_id=term_id, section_id=section_id, ) Scheduled.create( cross_listed_section_ids=approval.cross_listed_section_ids, instructor_uids=SisSection.get_instructor_uids( term_id=term_id, section_id=section_id), meeting_days=meeting_days, meeting_start_time=meeting_start_time, meeting_end_time=meeting_end_time, publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=scheduled_in_room.id, section_id=section_id, term_id=term_id, ) admin_uid = app.config['EMAIL_DIABLO_ADMIN_UID'] email_count = _get_email_count(admin_uid) AdminEmailsJob(app.app_context).run() assert _get_email_count(admin_uid) == email_count + 1
def _schedule_recordings( section_id, term_id, publish_type='kaltura_media_gallery', recording_type='presenter_presentation_audio', room_id=None, ): meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times( term_id=term_id, section_id=section_id, ) Scheduled.create( cross_listed_section_ids=[], instructor_uids=SisSection.get_instructor_uids(term_id=term_id, section_id=section_id), meeting_days=meeting_days, meeting_start_time=meeting_start_time, meeting_end_time=meeting_end_time, publish_type_=publish_type, recording_type_=recording_type, room_id=room_id or Room.get_room_id(section_id=section_id, term_id=term_id), section_id=section_id, term_id=term_id, ) std_commit(allow_test_environment=True)
def schedule_recordings(all_approvals, course): def _report_error(subject): message = f'{subject}\n\n<pre>{course}</pre>' app.logger.error(message) send_system_error_email(message=message, subject=subject) meetings = course.get('meetings', {}).get('eligible', []) meeting = meetings[0] if len(meetings) == 1 else None if not meeting: _report_error( subject= f"{course['label']} not scheduled. Unique eligible meeting pattern not found." ) return None all_approvals.sort(key=lambda a: a.created_at.isoformat()) latest_approval = all_approvals[-1] room = Room.get_room(latest_approval.room_id) if room.location != meeting['location']: _report_error( subject= f"{course['label']} not scheduled. Room change: {room.location} to {meeting['location']}" ) return None has_admin_approval = next( (a for a in all_approvals if a.approver_type == 'admin'), None) approved_by_uids = set(a.approved_by_uid for a in all_approvals) instructor_uids = set([i['uid'] for i in course['instructors']]) if not has_admin_approval and not instructor_uids.issubset( approved_by_uids): _report_error( subject= f"{course['label']} not scheduled. We are missing instructor approval(s)." ) return None term_id = course['termId'] section_id = int(course['sectionId']) scheduled = None if room.kaltura_resource_id: try: kaltura_schedule_id = Kaltura().schedule_recording( canvas_course_site_ids=[ c['courseSiteId'] for c in course['canvasCourseSites'] ], course_label=course['label'], instructors=course['instructors'], meeting=meeting, publish_type=latest_approval.publish_type, recording_type=latest_approval.recording_type, room=room, term_id=term_id, ) scheduled = Scheduled.create( course_display_name=course['label'], instructor_uids=instructor_uids, kaltura_schedule_id=kaltura_schedule_id, meeting_days=meeting['days'], meeting_end_date=get_recording_end_date(meeting), meeting_end_time=meeting['endTime'], meeting_start_date=get_recording_start_date( meeting, return_today_if_past_start=True), meeting_start_time=meeting['startTime'], publish_type_=latest_approval.publish_type, recording_type_=latest_approval.recording_type, room_id=room.id, section_id=section_id, term_id=term_id, ) # Turn off opt-out setting if present. if section_id in CoursePreference.get_section_ids_opted_out( term_id=term_id): CoursePreference.update_opt_out( term_id=term_id, section_id=section_id, opt_out=False, ) notify_instructors_recordings_scheduled(course=course, scheduled=scheduled) uids = [approval.approved_by_uid for approval in all_approvals] app.logger.info( f'Recordings scheduled for course {section_id} per approvals: {", ".join(uids)}' ) except (KalturaClientException, KalturaException) as e: # Error codes: https://developer.kaltura.com/api-docs/Error_Codes summary = f"Failed to schedule recordings {course['label']} (section_id: {course['sectionId']})" app.logger.error(summary) app.logger.exception(e) send_system_error_email( message=f'{summary}\n\n<pre>{traceback.format_exc()}</pre>', subject=f'{summary[:50]}...' if len(summary) > 50 else summary, ) else: app.logger.warn(f""" SKIP schedule recordings because room has no 'kaltura_resource_id'. Course: {course['label']} Room: {room.location} Latest approved_by_uid: {latest_approval.approved_by_uid} """) return scheduled
def _schedule_recordings(all_approvals, course): term_id = course['termId'] section_id = int(course['sectionId']) all_approvals.sort(key=lambda a: a.created_at.isoformat()) approval = all_approvals[-1] room = Room.get_room(approval.room_id) meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times( term_id=term_id, section_id=section_id, ) time_format = '%H:%M' # Recording starts X minutes before/after official start; it ends Y minutes before/after official end time. recording_offset_start = app.config['KALTURA_RECORDING_OFFSET_START'] recording_offset_end = app.config['KALTURA_RECORDING_OFFSET_END'] adjusted_start_time = datetime.strptime( meeting_start_time, time_format) + timedelta(minutes=recording_offset_start) adjusted_end_time = datetime.strptime( meeting_end_time, time_format) + timedelta(minutes=recording_offset_end) days = format_days(meeting_days) instructor_uids = [ instructor['uid'] for instructor in course['instructors'] ] app.logger.info(f""" Prepare to schedule recordings for {course["label"]}: Room: {room.location} Instructor UIDs: {instructor_uids} Schedule: {days}, {adjusted_start_time} to {adjusted_end_time} Recording: {approval.recording_type}; {approval.publish_type} """) if room.kaltura_resource_id: Kaltura().schedule_recording( course_label=course['label'], instructor_uids=instructor_uids, days=days, start_time=adjusted_start_time, end_time=adjusted_end_time, publish_type=approval.publish_type, recording_type=approval.recording_type, room=room, ) scheduled = Scheduled.create( cross_listed_section_ids=approval.cross_listed_section_ids, instructor_uids=SisSection.get_instructor_uids( term_id=term_id, section_id=section_id), meeting_days=meeting_days, meeting_start_time=meeting_start_time, meeting_end_time=meeting_end_time, publish_type_=approval.publish_type, recording_type_=approval.recording_type, room_id=approval.room_id, section_id=section_id, term_id=term_id, ) notify_instructors_recordings_scheduled(course=course, scheduled=scheduled) uids = [approval.approved_by_uid for approval in all_approvals] app.logger.info( f'Recordings scheduled for course {section_id} per approvals: {", ".join(uids)}' ) else: app.logger.error(f""" FAILED to schedule recordings because room has no 'kaltura_resource_id'. Course: {course} Room: {room} Latest approval: {approval} """)