def test_no_email_queued(self): """Do nothing if 'queued_emails' table is empty.""" term_id = app.config['CURRENT_TERM_ID'] QueuedEmailsJob(simply_yield).run() std_commit(allow_test_environment=True) # Verify that the next job run will have zero queued emails. assert len(QueuedEmail.get_all(term_id=term_id)) == 0 QueuedEmailsJob(simply_yield).run() std_commit(allow_test_environment=True)
def test_invite_new_instructors(self, app, db_session): """Invite all assigned instructors who haven't yet received an invitation.""" term_id = app.config['CURRENT_TERM_ID'] with test_approvals_workflow(app): # The job creates many new invitations. timestamp = utc_now() # Emails are queued but not sent. InvitationJob(simply_yield).run() assert len(_get_invitations_since(term_id, timestamp)) == 0 # Emails are sent. We have more emails than courses since some courses have multiple instructors. QueuedEmailsJob(simply_yield).run() invitations = _get_invitations_since(term_id, timestamp) assert len(invitations) == 14 # Each eligible course has an invitation. eligible_courses = [ c for c in SisSection.get_courses(term_id=term_id) if len(c['meetings']['eligible']) == 1 ] assert len(eligible_courses) == 11 for course in eligible_courses: for i in course['instructors']: sent_email = next((e for e in invitations if e.section_id == course['sectionId'] and i['uid'] == e.recipient_uid), None) assert sent_email email_json = sent_email.to_api_json() assert email_json['recipientUid'] == i['uid'] assert email_json['sectionId'] == course['sectionId'] assert email_json['templateType'] == 'invitation' assert email_json['termId'] == term_id assert email_json['sentAt'] # Add an instructor. section = SisSection.query.filter_by(term_id=term_id, section_id=50002).first() db_session.expunge(section) make_transient(section) section.id = None section.instructor_uid = '10008' db_session.add(section) std_commit(allow_test_environment=True) # Re-run the job. An email is sent to the new instructor only. timestamp = utc_now() InvitationJob(simply_yield).run() QueuedEmailsJob(simply_yield).run() invitations = _get_invitations_since(term_id, timestamp) assert len(invitations) == 1 invitation = invitations[0].to_api_json() assert invitation['sectionId'] == 50002 assert invitation['recipientUid'] == '10008'
def test_course_has_opted_out(self): """Do not send email to courses that have opted out.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 50000 CoursePreference.update_opt_out(term_id=term_id, section_id=section_id, opt_out=True) email_template_type = 'invitation' recipient = { 'name': 'William Peter Blatty', 'uid': '10001', } QueuedEmail.create(section_id, email_template_type, term_id, recipient=recipient) std_commit(allow_test_environment=True) emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(simply_yield).run() std_commit(allow_test_environment=True) # Expect no emails sent emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) assert list(map(lambda e: e.id, emails_sent_before)) == list( map(lambda e: e.id, emails_sent_after))
def test_no_email_template_available(self): """If email_template is not available then keep related emails in the queue.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 22287 email_template_type = 'waiting_for_approval' queued_email = QueuedEmail.create(section_id, email_template_type, term_id) std_commit(allow_test_environment=True) emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) # Expect no email sent emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) # Assert that email is still queued assert section_id in QueuedEmail.get_all_section_ids( template_type=email_template_type, term_id=term_id) # Clean up QueuedEmail.delete(queued_email)
def test_currently_no_person_teaching_course(self): """If course does not have a proper instructor then the email remains queued.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 22460 email_template_type = 'invitation' # Courses with no proper instructor are excluded from query results. assert not SisSection.get_course(term_id=term_id, section_id=section_id) queued_email = QueuedEmail.create(section_id, email_template_type, term_id) std_commit(allow_test_environment=True) emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) # Expect no email sent emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) # Assert that email is still queued assert section_id in QueuedEmail.get_all_section_ids( template_type=email_template_type, term_id=term_id) # Clean up QueuedEmail.delete(queued_email)
def test_queued_email_for_admin(self): """Certain email template types are for admin recipients only.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 22287 email_template_type = 'admin_alert_room_change' QueuedEmail.create(section_id, email_template_type, term_id) std_commit(allow_test_environment=True) before = utc_now() emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) # Expect email to admin email address emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) + 1 sent_email = next( (e for e in emails_sent_after if e.section_id == section_id and e.sent_at > before), None) assert sent_email json_ = sent_email.to_api_json() assert json_['recipientUids'] == [app.config['EMAIL_DIABLO_ADMIN_UID']] assert json_['sectionId'] == section_id assert json_['templateType'] == email_template_type assert json_['termId'] == term_id assert json_['sentAt']
def test_course_has_opted_out(self): """Do not send email to courses that have opted out.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 28602 CoursePreference.update_opt_out(term_id=term_id, section_id=section_id, opt_out=True) email_template_type = 'invitation' QueuedEmail.create(section_id, email_template_type, term_id) std_commit(allow_test_environment=True) before = utc_now() emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) # Expect no emails sent emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) assert not next( (e for e in emails_sent_after if e.section_id == section_id and e.sent_at > before), None)
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_approval_by_instructors(self, app, client, fake_auth): """Instructor can submit approval if s/he is teaching the requested course.""" with test_approvals_workflow(app): instructor_uids = get_instructor_uids(section_id=section_1_id, term_id=self.term_id) fake_auth.login(instructor_uids[0]) api_approve( client, publish_type='kaltura_my_media', recording_type='presentation_audio', section_id=section_1_id, ) std_commit(allow_test_environment=True) fake_auth.login(instructor_uids[1]) api_approve( client, publish_type='kaltura_media_gallery', recording_type='presentation_audio', section_id=section_1_id, ) std_commit(allow_test_environment=True) QueuedEmailsJob(app.app_context).run() # First instructor was notified 1) that second instructor needed to approve; 2) that second instructor made changes. emails_sent = SentEmail.get_emails_sent_to(instructor_uids[0]) assert len(emails_sent) == 2 for email in emails_sent: assert email.section_id == section_1_id assert email.term_id == self.term_id assert emails_sent[0].template_type == 'waiting_for_approval' assert emails_sent[1].template_type == 'notify_instructor_of_changes' # Second instructor received no notifications. assert len(SentEmail.get_emails_sent_to(instructor_uids[1])) == 0 fake_auth.login(admin_uid) api_json = api_get_course( client, term_id=self.term_id, section_id=section_1_id, ) assert api_json['meetings']['eligible'][0]['room']['location'] == 'Barrows 106' instructor_uids = [i['uid'] for i in api_json['instructors']] assert instructor_uids == instructor_uids approvals_ = api_json['approvals'] assert len(approvals_) == 2 assert approvals_[0]['approvedBy']['uid'] == instructor_uids[0] assert approvals_[0]['publishType'] == 'kaltura_my_media' assert approvals_[1]['approvedBy']['uid'] == instructor_uids[1] assert approvals_[1]['publishType'] == 'kaltura_media_gallery' assert approvals_[1]['recordingType'] == 'presentation_audio' assert approvals_[1]['recordingTypeName'] == 'Presentation and Audio' assert api_json['hasNecessaryApprovals'] is True assert api_json['scheduled'] is None
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_send_invitation_emails(self): """Send all email in 'queued_emails' table.""" def _emails_sent(section_id): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] courses = [ { 'sectionId': 28602, 'instructorUids': ['234567', '8765432'], }, { 'sectionId': 28165, 'instructorUids': ['8765432'], }, ] email_template_type = 'invitation' for course in courses: QueuedEmail.create(course['sectionId'], email_template_type, term_id) std_commit(allow_test_environment=True) def _get_emails_to_courses(): emails_sent_ = [] for course_ in courses: emails_sent_.extend(_emails_sent(course_['sectionId'])) return emails_sent_ before = utc_now() emails_sent_before = _get_emails_to_courses() # Run the job QueuedEmailsJob(app.app_context).run() std_commit(allow_test_environment=True) # Expect one email per instructor emails_sent_after = _get_emails_to_courses() assert len(emails_sent_after) == len(emails_sent_before) + 2 def _find_email(section_id): return next((e for e in emails_sent_after if e.section_id == section_id and e.sent_at > before), None) for course in courses: sent_email = _find_email(section_id=course['sectionId']) assert sent_email json_ = sent_email.to_api_json() assert set(json_['recipientUids']) == set(course['instructorUids']) assert json_['sectionId'] == course['sectionId'] assert json_['templateType'] == email_template_type assert json_['termId'] == term_id assert json_['sentAt']
def test_send_invitation_emails(self): """Send all email in 'queued_emails' table.""" term_id = app.config['CURRENT_TERM_ID'] courses = SisSection.get_courses(section_ids=[50000, 50001], term_id=term_id) email_template_type = 'invitation' for course in courses: for instructor in course['instructors']: QueuedEmail.create(course['sectionId'], email_template_type, term_id, recipient=instructor) std_commit(allow_test_environment=True) def _get_emails_to_courses(): emails_sent = [] for c in courses: emails_sent.extend( _get_emails_sent( email_template_type=email_template_type, section_id=c['sectionId'], term_id=term_id, ), ) return emails_sent before = utc_now() emails_sent_before = _get_emails_to_courses() # Run the job QueuedEmailsJob(simply_yield).run() std_commit(allow_test_environment=True) # Expect one email per instructor emails_sent_after = _get_emails_to_courses() assert len(emails_sent_after) == len(emails_sent_before) + 3 def _find_email(section_id, uid): return next( (e for e in emails_sent_after if e.section_id == section_id and e.sent_at > before and uid == e.recipient_uid), None) for course in courses: for instructor in course['instructors']: sent_email = _find_email(section_id=course['sectionId'], uid=instructor['uid']) assert sent_email email_json = sent_email.to_api_json() assert email_json['recipientUid'] == instructor['uid'] assert email_json['sectionId'] == course['sectionId'] assert email_json['templateType'] == email_template_type assert email_json['termId'] == term_id assert email_json['sentAt']
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_course_opted_out(self, app): """Do not send email to courses that have opted out.""" term_id = app.config['CURRENT_TERM_ID'] with test_approvals_workflow(app): section_id = 50006 CoursePreference.update_opt_out(term_id=term_id, section_id=section_id, opt_out=True) std_commit(allow_test_environment=True) timestamp = utc_now() # Emails are queued but not sent. InvitationJob(simply_yield).run() assert len(_get_invitations_since(term_id, timestamp)) == 0 # Emails are sent. QueuedEmailsJob(simply_yield).run() invitations = _get_invitations_since(term_id, timestamp) assert len(invitations) == 15 assert not next( (e for e in invitations if e.section_id == section_id), None)
def test_queued_email_for_admin(self): """Certain email template types are for admin recipients only.""" def _emails_sent(): return _get_emails_sent(email_template_type=email_template_type, section_id=section_id, term_id=term_id) term_id = app.config['CURRENT_TERM_ID'] section_id = 50005 email_template_type = 'admin_alert_room_change' recipient = { 'name': 'Course Capture Admin', 'uid': app.config['EMAIL_DIABLO_ADMIN_UID'], } QueuedEmail.create(section_id, email_template_type, term_id, recipient=recipient) std_commit(allow_test_environment=True) before = utc_now() emails_sent_before = _emails_sent() # Run the job QueuedEmailsJob(simply_yield).run() std_commit(allow_test_environment=True) # Expect email to admin email address emails_sent_after = _emails_sent() assert len(emails_sent_after) == len(emails_sent_before) + 1 sent_email = next( (e for e in emails_sent_after if e.section_id == section_id and e.sent_at > before), None) assert sent_email email_json = sent_email.to_api_json() assert email_json['recipientUid'] == app.config[ 'EMAIL_DIABLO_ADMIN_UID'] assert email_json['sectionId'] == section_id assert email_json['templateType'] == email_template_type assert email_json['termId'] == term_id assert email_json['sentAt']
def _run_jobs(): AdminEmailsJob(simply_yield).run() QueuedEmailsJob(simply_yield).run()
def _run_jobs(): InstructorEmailsJob(simply_yield).run() QueuedEmailsJob(simply_yield).run()
def test_cross_listed_course(self): """Recordings will be scheduled if and only if all instructor(s) approve.""" with test_approvals_workflow(app): section_id = 50012 term_id = app.config['CURRENT_TERM_ID'] email_template_type = 'recordings_scheduled' def _get_emails_sent(): return SentEmail.get_emails_of_type( section_ids=[section_id], template_type=email_template_type, term_id=term_id, ) emails_sent = _get_emails_sent() KalturaJob(simply_yield).run() std_commit(allow_test_environment=True) # Expect no emails sent during action above assert _get_emails_sent() == emails_sent course = SisSection.get_course(section_id=section_id, term_id=term_id) instructors = course['instructors'] cross_listings = course['crossListings'] room_id = Room.find_room( course['meetings']['eligible'][0]['location']).id assert len(instructors) == 2 assert room_id == Room.find_room("O'Brien 212").id assert len(cross_listings) == 1 assert len(course['canvasCourseSites']) == 2 # This course requires two (2) approvals. approvals = [ Approval.create( approved_by_uid=instructors[0]['uid'], approver_type_='instructor', course_display_name=course['label'], publish_type_='kaltura_my_media', recording_type_='presentation_audio', room_id=room_id, section_id=section_id, term_id=term_id, ), ] """If we have insufficient approvals then do nothing.""" email_count = _get_emails_sent() KalturaJob(simply_yield).run() std_commit(allow_test_environment=True) assert _get_emails_sent() == email_count # The second approval final_approval = Approval.create( approved_by_uid=instructors[1]['uid'], approver_type_='instructor', course_display_name=course['label'], publish_type_='kaltura_media_gallery', recording_type_='presenter_presentation_audio', room_id=room_id, section_id=section_id, term_id=term_id, ) approvals.append(final_approval) """If a course is scheduled for recording then email is sent to its instructor(s).""" email_count = len(_get_emails_sent()) KalturaJob(simply_yield).run() std_commit(allow_test_environment=True) # Verify publish and recording types scheduled = Scheduled.get_scheduled(term_id=term_id, section_id=section_id) assert scheduled.publish_type == final_approval.publish_type assert scheduled.recording_type == final_approval.recording_type assert scheduled.section_id == section_id assert scheduled.term_id == term_id # Verify emails sent QueuedEmailsJob(app.app_context).run() emails_sent = _get_emails_sent() assert len(emails_sent) == email_count + 2 assert [ emails_sent[-1].recipient_uid, emails_sent[-2].recipient_uid ] == ['10009', '10010'] email_sent = emails_sent[-1] assert email_sent.section_id == section_id assert email_sent.template_type == 'recordings_scheduled' assert email_sent.term_id == term_id """If recordings were already scheduled then do nothing, send no email.""" email_count = len(_get_emails_sent()) KalturaJob(simply_yield).run() assert len(_get_emails_sent()) == email_count
def _run_instructor_emails_job(): InstructorEmailsJob(simply_yield).run() QueuedEmailsJob(simply_yield).run()