Beispiel #1
0
def get_courses_ready_to_schedule(approvals, term_id):
    ready_to_schedule = []

    scheduled_section_ids = [
        s.section_id for s in Scheduled.get_all_scheduled(term_id=term_id)
    ]
    unscheduled_approvals = [
        approval for approval in approvals
        if approval.section_id not in scheduled_section_ids
    ]

    if unscheduled_approvals:
        courses = SisSection.get_courses(
            section_ids=[a.section_id for a in unscheduled_approvals],
            term_id=term_id)
        courses_per_section_id = dict(
            (int(course['sectionId']), course) for course in courses)
        admin_user_uids = set([
            user.uid
            for user in AdminUser.all_admin_users(include_deleted=True)
        ])

        for section_id, uids in _get_uids_per_section_id(
                approvals=unscheduled_approvals).items():
            if admin_user_uids.intersection(set(uids)):
                ready_to_schedule.append(courses_per_section_id[section_id])
            else:
                course = courses_per_section_id[section_id]
                necessary_uids = [i['uid'] for i in course['instructors']]
                if all(uid in uids for uid in necessary_uids):
                    ready_to_schedule.append(
                        courses_per_section_id[section_id])
    return ready_to_schedule
Beispiel #2
0
    def _room_change_alert(self):
        template_type = 'room_change_no_longer_eligible'
        all_scheduled = list(
            filter(
                lambda s: template_type not in (s.alerts or []),
                Scheduled.get_all_scheduled(term_id=self.term_id),
            ), )
        if all_scheduled:
            email_template = EmailTemplate.get_template_by_type(template_type)
            courses = SisSection.get_courses(
                term_id=self.term_id,
                section_ids=[s.section_id for s in all_scheduled],
                include_deleted=True,
            )
            courses_per_section_id = dict(
                (course['sectionId'], course) for course in courses)
            for scheduled in all_scheduled:
                course = courses_per_section_id.get(scheduled.section_id)
                if course:
                    if self._has_moved_to_ineligible_room(
                            course, scheduled) or course['deletedAt']:
                        if email_template:
                            for instructor in course['instructors']:

                                def _get_interpolate_content(template):
                                    return interpolate_content(
                                        course=course,
                                        publish_type_name=course.get(
                                            'scheduled',
                                            {}).get('publishTypeName'),
                                        recipient_name=instructor['name'],
                                        recording_type_name=course.get(
                                            'scheduled',
                                            {}).get('recordingTypeName'),
                                        templated_string=template,
                                    )

                                QueuedEmail.create(
                                    message=_get_interpolate_content(
                                        email_template.message),
                                    recipient=instructor,
                                    section_id=course['sectionId'],
                                    subject_line=_get_interpolate_content(
                                        email_template.subject_line),
                                    template_type=template_type,
                                    term_id=self.term_id,
                                )
                            Scheduled.add_alert(
                                scheduled_id=course['scheduled']['id'],
                                template_type=template_type)
                        else:
                            send_system_error_email(f"""
                                No '{template_type}' email template available.
                                We are unable to notify {course['label']} instructors of room change.
                            """)
                else:
                    subject = f'Scheduled course has no SIS data (section_id={scheduled.section_id})'
                    message = f'{subject}\n\nScheduled:<pre>{scheduled}</pre>'
                    app.logger.error(message)
                    send_system_error_email(message=message, subject=subject)
 def email_new_invites(self):
     for course in SisSection.get_courses(term_id=self.term_id):
         if not course['hasOptedOut'] and len(course.get('meetings', {}).get('eligible', [])) == 1:
             for i in course['instructors']:
                 if not i['wasSentInvite']:
                     QueuedEmail.create(
                         recipient=i,
                         section_id=course['sectionId'],
                         template_type='invitation',
                         term_id=self.term_id,
                     )
Beispiel #4
0
    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_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'
Beispiel #6
0
 def run(self):
     term_id = app.config['CURRENT_TERM_ID']
     all_scheduled = Scheduled.get_all_scheduled(term_id=term_id)
     if all_scheduled:
         courses = SisSection.get_courses(
             term_id=term_id,
             section_ids=[s.section_id for s in all_scheduled])
         _alert_admin_of_instructor_change(
             courses=courses,
             approval_uids_per_section_id=_approval_uids_per_section_id(
                 scheduled=all_scheduled,
                 term_id=term_id,
             ),
         )
         _alert_admin_of_room_change(
             courses=courses,
             scheduled_rooms_per_section_id=
             _scheduled_locations_per_section_id(all_scheduled),
         )
Beispiel #7
0
 def run(self):
     term_id = app.config['CURRENT_TERM_ID']
     all_scheduled = Scheduled.get_all_scheduled(term_id=term_id)
     if all_scheduled:
         courses = SisSection.get_courses(
             term_id=term_id,
             section_ids=[s.section_id for s in all_scheduled])
         courses_per_section_id = dict(
             (course['sectionId'], course) for course in courses)
         for scheduled in all_scheduled:
             course = courses_per_section_id[scheduled.section_id]
             if course:
                 if scheduled.room_id != course['room']['id']:
                     email_template = EmailTemplate.get_template_by_type(
                         'room_change_no_longer_eligible')
                     for instructor in course['instructor']:
                         BConnected().send(
                             message=interpolate_email_content(
                                 templated_string=email_template.message,
                                 course=course,
                                 instructor_name=instructor['name'],
                                 recipient_name=instructor['name'],
                                 recording_type_name=scheduled.
                                 recording_type,
                             ),
                             recipients=course['instructors'],
                             subject_line=interpolate_email_content(
                                 templated_string=email_template.
                                 subject_line,
                                 course=course,
                                 instructor_name=instructor['name'],
                                 recipient_name=instructor['name'],
                                 recording_type_name=scheduled.
                                 recording_type,
                             ),
                         )
             else:
                 error = f'section_id of scheduled recordings was not found in SIS data: {scheduled}'
                 app.logger.error(error)
                 send_system_error_email(message=error)
Beispiel #8
0
def get_courses_ready_to_schedule(approvals, term_id):
    ready_to_schedule = []
    scheduled_section_ids = [
        s.section_id for s in Scheduled.get_all_scheduled(term_id=term_id)
    ]
    unscheduled_approvals = [
        approval for approval in approvals
        if approval.section_id not in scheduled_section_ids
    ]

    if unscheduled_approvals:
        courses = SisSection.get_courses(
            section_ids=[a.section_id for a in unscheduled_approvals],
            term_id=term_id)
        courses_per_section_id = dict(
            (int(course['sectionId']), course) for course in courses)
        admin_user_uids = set([
            user.uid
            for user in AdminUser.all_admin_users(include_deleted=True)
        ])

        for section_id, approved_by_uids in _get_uids_per_section_id(
                approvals=unscheduled_approvals).items():
            course = courses_per_section_id.get(section_id)
            if not course:
                continue
            if len(course.get('meetings', {}).get('eligible', [])) != 1:
                app.logger.warn(
                    f'Unique meeting pattern not found for section id {section_id}; will not schedule.'
                )
                continue
            if admin_user_uids.intersection(set(approved_by_uids)):
                ready_to_schedule.append(course)
            else:
                necessary_uids = [i['uid'] for i in course['instructors']]
                if all(uid in approved_by_uids for uid in necessary_uids):
                    ready_to_schedule.append(course)

    return ready_to_schedule
Beispiel #9
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