コード例 #1
0
    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))
コード例 #2
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))
コード例 #3
0
    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)
コード例 #4
0
    def test_do_not_email_filter(self, client, db, admin_session):
        """Do Not Email filter: Courses in eligible room; "opt out" is true; all stages of approval; not scheduled."""
        with test_approvals_workflow(app):
            # Send invites them opt_out.
            for section_id in (section_1_id, section_in_ineligible_room, section_3_id, section_4_id):
                CoursePreference.update_opt_out(section_id=section_id, term_id=self.term_id, opt_out=True)

                in_enabled_room = _is_course_in_enabled_room(section_id=section_id, term_id=self.term_id)
                if section_id == section_in_ineligible_room:
                    # Courses in ineligible rooms will be excluded from the feed.
                    assert not in_enabled_room
                else:
                    assert in_enabled_room
                    SentEmail.create(
                        section_id=section_id,
                        recipient_uids=_get_instructor_uids(section_id=section_id, term_id=self.term_id),
                        template_type='invitation',
                        term_id=self.term_id,
                    )

            # If course has approvals but not scheduled then it will show up in the feed.
            Approval.create(
                approved_by_uid=_get_instructor_uids(section_id=section_1_id, term_id=self.term_id)[0],
                approver_type_='instructor',
                cross_listed_section_ids=[],
                publish_type_='canvas',
                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,
            )
            # Feed will exclude scheduled.
            _schedule_recordings(
                section_id=section_3_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = self._api_courses(client, term_id=self.term_id, filter_='Do Not Email')
            for section_id in (section_1_id, section_4_id):
                # The 'Do Not Email' course is in the feed
                assert _find_course(api_json=api_json, section_id=section_id)

            for section_id in (section_3_id, section_in_ineligible_room):
                # Excluded courses
                assert not _find_course(api_json=api_json, section_id=section_id)
コード例 #5
0
def update_opt_out():
    params = request.get_json()
    term_id = params.get('termId')
    section_id = params.get('sectionId')
    opt_out = params.get('optOut')
    preferences = CoursePreference.update_opt_out(
        term_id=term_id,
        section_id=section_id,
        opt_out=opt_out,
    )
    return tolerant_jsonify(preferences.to_api_json())
コード例 #6
0
    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)
コード例 #7
0
def _after_approval(course):
    section_id = course['sectionId']
    term_id = course['termId']
    approvals = Approval.get_approvals(section_id=section_id, term_id=term_id)

    if get_courses_ready_to_schedule(approvals=approvals, term_id=term_id):
        # Queuing course for scheduling wipes any opt-out preference.
        if course['hasOptedOut']:
            CoursePreference.update_opt_out(
                term_id=term_id,
                section_id=section_id,
                opt_out=False,
            )
        if app.config['FEATURE_FLAG_SCHEDULE_RECORDINGS_SYNCHRONOUSLY']:
            # Feature flag intended for dev workstation ONLY. Do not enable in diablo-dev|qa|prod.
            schedule_recordings(
                all_approvals=approvals,
                course=course,
            )
        return SisSection.get_course(section_id=course['sectionId'], term_id=course['termId'])
    else:
        return course
コード例 #8
0
    def test_do_not_email_filter(self, client, admin_session):
        """Do Not Email filter: Courses in eligible room; "opt out" is true; all stages of approval; not scheduled."""
        with test_approvals_workflow(app):
            # Send invites them opt_out.
            for section_id in (section_1_id, section_in_ineligible_room, section_3_id, section_4_id):
                CoursePreference.update_opt_out(section_id=section_id, term_id=self.term_id, opt_out=True)

                in_enabled_room = _is_course_in_enabled_room(section_id=section_id, term_id=self.term_id)
                if section_id == section_in_ineligible_room:
                    # Courses in ineligible rooms will be excluded from the feed.
                    assert not in_enabled_room
                else:
                    assert in_enabled_room
                    self._send_invitation_email(section_id)

            # If course has approvals but not scheduled then it will show up in the feed.
            self._create_approval(section_4_id)
            # Feed will exclude scheduled.
            mock_scheduled(
                section_id=section_3_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = self._api_courses(client, term_id=self.term_id, filter_='Do Not Email')

            # Opted-out courses are in the feed, whether approved or not
            course_1 = _find_course(api_json=api_json, section_id=section_1_id)
            assert course_1['approvalStatus'] == 'Invited'
            assert course_1['schedulingStatus'] == 'Not Scheduled'
            course_4 = _find_course(api_json=api_json, section_id=section_4_id)
            assert course_4['approvalStatus'] == 'Approved'
            assert course_4['schedulingStatus'] == 'Queued for Scheduling'

            for section_id in (section_3_id, section_in_ineligible_room):
                # Excluded courses
                assert not _find_course(api_json=api_json, section_id=section_id)
コード例 #9
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())
コード例 #10
0
ファイル: util.py プロジェクト: ets-berkeley-edu/diablo
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