コード例 #1
0
    def test_course_with_partial_approval(self, client, admin_session):
        """Course with two instructors and one approval."""
        with test_approvals_workflow(app):
            # If course has approvals but not scheduled then it will show up in the feed.
            approved_by_uid = get_instructor_uids(section_id=section_1_id, term_id=self.term_id)[0]
            room_id = Room.get_room_id(section_id=section_1_id, term_id=self.term_id)
            Approval.create(
                approved_by_uid=approved_by_uid,
                approver_type_='instructor',
                publish_type_='kaltura_my_media',
                recording_type_='presentation_audio',
                room_id=room_id,
                section_id=section_1_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = api_get_course(
                client,
                term_id=self.term_id,
                section_id=section_1_id,
            )
            assert [i['uid'] for i in api_json['instructors']] == ['10001', '10002']

            approvals = api_json['approvals']
            assert len(approvals) == 1
            assert approved_by_uid == approvals[0]['approvedBy']['uid']
            assert api_json['approvalStatus'] == 'Partially Approved'
            assert api_json['schedulingStatus'] == 'Not Scheduled'
            assert api_json['meetings']['eligible'][0]['room']['id'] == room_id
            assert api_json['meetings']['eligible'][0]['room']['location'] == 'Barrows 106'
コード例 #2
0
    def test_admin_approval(self):
        """Course is scheduled for recording if an admin user has approved."""
        with test_approvals_workflow(app):
            section_id = 22287
            term_id = app.config['CURRENT_TERM_ID']
            course = SisSection.get_course(section_id=section_id,
                                           term_id=term_id)
            instructors = course['instructors']
            assert len(instructors) == 2

            # Verify that course is not scheduled
            assert Scheduled.get_scheduled(section_id=section_id,
                                           term_id=term_id) is None

            Approval.create(
                approved_by_uid=admin_uid,
                approver_type_='admin',
                cross_listed_section_ids=[],
                publish_type_='canvas',
                recording_type_='presentation_audio',
                room_id=Room.find_room('Barker 101').id,
                section_id=section_id,
                term_id=term_id,
            )
            KalturaJob(app.app_context).run()
            std_commit(allow_test_environment=True)
            # Admin approval is all we need.
            assert Scheduled.get_scheduled(section_id=section_id,
                                           term_id=term_id)
コード例 #3
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))
コード例 #4
0
    def test_admin_approval(self):
        """Course is scheduled for recording if an admin user has approved."""
        with test_approvals_workflow(app):
            section_id = 50005
            term_id = app.config['CURRENT_TERM_ID']
            course = SisSection.get_course(section_id=section_id,
                                           term_id=term_id)
            instructors = course['instructors']
            assert len(instructors) == 2

            # Verify that course is not scheduled
            assert Scheduled.get_scheduled(section_id=section_id,
                                           term_id=term_id) is None

            Approval.create(
                approved_by_uid=admin_uid,
                approver_type_='admin',
                course_display_name=course['label'],
                publish_type_='kaltura_my_media',
                recording_type_='presentation_audio',
                room_id=Room.find_room('Barker 101').id,
                section_id=section_id,
                term_id=term_id,
            )
            KalturaJob(simply_yield).run()
            std_commit(allow_test_environment=True)
            # Admin approval is all we need.
            assert Scheduled.get_scheduled(section_id=section_id,
                                           term_id=term_id)
コード例 #5
0
    def test_course_with_partial_approval(self, client, db, admin_session):
        """Course with two instructors and one approval."""
        with test_approvals_workflow(app):
            # If course has approvals but not scheduled then it will show up in the feed.
            approved_by_uid = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)[0]
            room_id = Room.get_room_id(section_id=section_1_id, term_id=self.term_id)
            Approval.create(
                approved_by_uid=approved_by_uid,
                approver_type_='instructor',
                cross_listed_section_ids=[],
                publish_type_='canvas',
                recording_type_='presentation_audio',
                room_id=room_id,
                section_id=section_1_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = api_get_course(
                client,
                term_id=self.term_id,
                section_id=section_1_id,
            )
            assert [i['uid'] for i in api_json['instructors']] == ['234567', '8765432']

            approvals = api_json['approvals']
            assert len(approvals) == 1
            assert approved_by_uid == approvals[0]['approvedBy']['uid']
            assert api_json['room']['id'] == room_id
            assert api_json['room']['location'] == 'Barrows 106'
コード例 #6
0
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)))
コード例 #7
0
 def _create_approval(self, section_id):
     Approval.create(
         approved_by_uid=get_instructor_uids(section_id=section_id, term_id=self.term_id)[0],
         approver_type_='instructor',
         publish_type_='kaltura_my_media',
         recording_type_='presentation_audio',
         room_id=Room.get_room_id(section_id=section_id, term_id=self.term_id),
         section_id=section_id,
         term_id=self.term_id,
     )
コード例 #8
0
    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
コード例 #9
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)
コード例 #10
0
 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
コード例 #11
0
 def test_partially_approved_filter(self, client, db, admin_session):
     """Partially approved: Eligible, invited course with 1+ approvals, but not ALL instructors have approved."""
     with test_approvals_workflow(app):
         # Assert multiple instructors
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         section_6_instructor_uids = _get_instructor_uids(section_id=section_6_id, term_id=self.term_id)
         assert len(section_1_instructor_uids) > 1
         assert len(section_6_instructor_uids) > 1
         # Send invites
         courses = [
             {'section_id': section_1_id, 'instructor_uids': section_1_instructor_uids},
             {'section_id': section_6_id, 'instructor_uids': section_6_instructor_uids},
         ]
         for course in courses:
             SentEmail.create(
                 section_id=course['section_id'],
                 recipient_uids=course['instructor_uids'],
                 template_type='invitation',
                 term_id=self.term_id,
             )
             Approval.create(
                 approved_by_uid=course['instructor_uids'][0],
                 approver_type_='instructor',
                 cross_listed_section_ids=[],
                 publish_type_='canvas',
                 recording_type_='presentation_audio',
                 room_id=Room.get_room_id(section_id=course['section_id'], term_id=self.term_id),
                 section_id=course['section_id'],
                 term_id=self.term_id,
             )
         # Feed will include both scheduled and not scheduled.
         _schedule_recordings(
             section_id=section_1_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Partially Approved')
         assert len(api_json) == 2
         assert _find_course(api_json=api_json, section_id=section_1_id)
         course = _find_course(api_json=api_json, section_id=section_6_id)
         assert course
         assert course['label'] == 'LAW 23, LEC 002'
コード例 #12
0
ファイル: sis_section.py プロジェクト: pauline2k/diablo
def _get_approvals_and_scheduled(section_ids, term_id):
    approvals = Approval.get_approvals_per_section_ids(section_ids=section_ids,
                                                       term_id=term_id)
    scheduled = None
    for section_id in section_ids:
        if not scheduled:
            scheduled = Scheduled.get_scheduled(section_id=section_id,
                                                term_id=term_id)
            scheduled = scheduled and scheduled.to_api_json()
            break
    return [a.to_api_json() for a in approvals], scheduled
コード例 #13
0
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))
コード例 #14
0
ファイル: admin_emails_job.py プロジェクト: pauline2k/diablo
def _approval_uids_per_section_id(scheduled, term_id):
    section_ids = [s.section_id for s in scheduled]
    all_approvals = Approval.get_approvals_per_section_ids(
        section_ids=section_ids, term_id=term_id)
    approval_uids_per_section_id = {
        section_id: []
        for section_id in section_ids
    }
    for approval in all_approvals:
        approval_uids_per_section_id[approval.section_id].append(
            approval.approved_by_uid)
    return approval_uids_per_section_id
コード例 #15
0
def _schedule_the_ready_to_schedule():
    term_id = app.config['CURRENT_TERM_ID']
    approvals = Approval.get_approvals_per_term(term_id=term_id)
    if approvals:
        approvals_per_section_id = objects_to_dict_organized_by_section_id(objects=approvals)
        ready_to_schedule = get_courses_ready_to_schedule(approvals=approvals, term_id=term_id)
        app.logger.info(f'Prepare to schedule recordings for {len(ready_to_schedule)} courses.')
        for course in ready_to_schedule:
            section_id = int(course['sectionId'])
            schedule_recordings(
                all_approvals=approvals_per_section_id[section_id],
                course=course,
            )
コード例 #16
0
    def test_partially_approved_filter(self, client, admin_session):
        """Partially approved: Eligible, invited course with 1+ approvals, but not ALL instructors have approved."""
        with test_approvals_workflow(app):
            for section_id in [section_1_id, section_6_id, section_7_id]:
                # Assert multiple instructors
                assert len(get_instructor_uids(section_id=section_id, term_id=self.term_id)) > 1
                # Send invites
                self._send_invitation_email(section_id)
                if section_id == section_1_id:
                    # If course is "approved" by admin only then it will NOT show up on the partially-approval list.
                    Approval.create(
                        approved_by_uid=admin_uid,
                        approver_type_='admin',
                        publish_type_='kaltura_my_media',
                        recording_type_='presentation_audio',
                        room_id=Room.get_room_id(section_id=section_id, term_id=self.term_id),
                        section_id=section_id,
                        term_id=self.term_id,
                    )
                else:
                    # Approval by first instructor only
                    self._create_approval(section_id)

            # Feed will include both scheduled and not scheduled.
            for section_id in [section_1_id, section_7_id]:
                mock_scheduled(section_id=section_id, term_id=self.term_id)

            # Unschedule one of them
            Approval.delete(section_id=section_7_id, term_id=self.term_id)
            Scheduled.delete(section_id=section_7_id, term_id=self.term_id)

            std_commit(allow_test_environment=True)
            api_json = self._api_courses(client, term_id=self.term_id, filter_='Partially Approved')
            assert len(api_json) == 1
            course = _find_course(api_json=api_json, section_id=section_6_id)
            assert course
            assert course['label'] == 'LAW 23, LEC 002'
            assert course['approvalStatus'] == 'Partially Approved'
            assert course['schedulingStatus'] == 'Not Scheduled'
コード例 #17
0
 def test_not_invited_filter(self, client, db, admin_session):
     """Not-invited filter: Courses in eligible rooms, never sent an invitation. No approval. Not scheduled."""
     with test_approvals_workflow(app):
         # The first course gets an invitation
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         SentEmail.create(
             section_id=section_1_id,
             recipient_uids=section_1_instructor_uids,
             template_type='invitation',
             term_id=self.term_id,
         )
         # The second course did not receive an invitation BUT it does have approval.
         invite = SentEmail.get_emails_of_type(
             section_id=section_4_id,
             template_type='invitation',
             term_id=self.term_id,
         )
         assert not invite
         Approval.create(
             approved_by_uid=_get_instructor_uids(section_id=section_4_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_4_id, term_id=self.term_id),
             section_id=section_4_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Not Invited')
         assert not _find_course(api_json=api_json, section_id=section_1_id)
         assert not _find_course(api_json=api_json, section_id=section_4_id)
         # Third course is in enabled room and has not received an invite. Therefore, it is in the feed.
         assert _is_course_in_enabled_room(section_id=section_3_id, term_id=self.term_id)
         course = _find_course(api_json=api_json, section_id=section_3_id)
         assert course
         assert course['label'] == 'BIO 1B, LEC 001'
コード例 #18
0
 def test_scheduled_filter(self, client, db, admin_session):
     """Scheduled filter: Courses with recordings scheduled."""
     with test_approvals_workflow(app):
         section_1_instructor_uids = _get_instructor_uids(section_id=section_1_id, term_id=self.term_id)
         section_6_instructor_uids = _get_instructor_uids(section_id=section_6_id, term_id=self.term_id)
         # Send invites
         courses = [
             {'section_id': section_1_id, 'instructor_uids': section_1_instructor_uids},
             {'section_id': section_6_id, 'instructor_uids': section_6_instructor_uids},
         ]
         for course in courses:
             SentEmail.create(
                 section_id=course['section_id'],
                 recipient_uids=course['instructor_uids'],
                 template_type='invitation',
                 term_id=self.term_id,
             )
             Approval.create(
                 approved_by_uid=course['instructor_uids'][0],
                 approver_type_='instructor',
                 cross_listed_section_ids=[],
                 publish_type_='canvas',
                 recording_type_='presentation_audio',
                 room_id=Room.get_room_id(section_id=course['section_id'], term_id=self.term_id),
                 section_id=course['section_id'],
                 term_id=self.term_id,
             )
         # Feed will only include courses that were scheduled.
         _schedule_recordings(
             section_id=section_1_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Scheduled')
         assert len(api_json) == 1
         assert _find_course(api_json=api_json, section_id=section_1_id)
         assert not _find_course(api_json=api_json, section_id=section_6_id)
コード例 #19
0
 def test_invited_filter(self, client, db, admin_session):
     """Invited filter: Course in an eligible room, have received invitation. No approvals. Not scheduled."""
     with test_approvals_workflow(app):
         # First, send invitations
         SentEmail.create(
             section_id=section_4_id,
             recipient_uids=_get_instructor_uids(section_id=section_4_id, term_id=self.term_id),
             template_type='invitation',
             term_id=self.term_id,
         )
         section_5_instructor_uids = _get_instructor_uids(section_id=section_5_id, term_id=self.term_id)
         SentEmail.create(
             section_id=section_5_id,
             recipient_uids=section_5_instructor_uids,
             template_type='invitation',
             term_id=self.term_id,
         )
         # The section with approval will NOT show up in search results
         Approval.create(
             approved_by_uid=section_5_instructor_uids[0],
             approver_type_='instructor',
             cross_listed_section_ids=[],
             publish_type_='canvas',
             recording_type_='presentation_audio',
             room_id=SisSection.get_course(term_id=self.term_id, section_id=section_5_id)['room']['id'],
             section_id=section_5_id,
             term_id=self.term_id,
         )
         std_commit(allow_test_environment=True)
         api_json = self._api_courses(client, term_id=self.term_id, filter_='Invited')
         # Section with ZERO approvals will show up in search results
         course = _find_course(api_json=api_json, section_id=section_4_id)
         assert course
         assert course['label'] == 'CHEM C110L, LAB 001'
         # The section with approval will NOT show up in search results
         assert not _find_course(api_json=api_json, section_id=section_5_id)
コード例 #20
0
    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'
コード例 #21
0
    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
コード例 #22
0
    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
コード例 #23
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
コード例 #24
0
def _to_api_json(term_id, rows, include_rooms=True):
    rows = rows.fetchall()
    section_ids = list(set(int(row['section_id']) for row in rows))
    courses_per_id = {}

    # Perform bulk queries and build data structures for feed generation.
    section_ids_opted_out = CoursePreference.get_section_ids_opted_out(
        term_id=term_id)

    invited_uids_by_section_id = {section_id: [] for section_id in section_ids}
    for invite in SentEmail.get_emails_of_type(section_ids=section_ids,
                                               template_type='invitation',
                                               term_id=term_id):
        if invite.recipient_uid not in invited_uids_by_section_id[
                invite.section_id]:
            invited_uids_by_section_id[invite.section_id].append(
                invite.recipient_uid)

    approval_results = Approval.get_approvals_per_section_ids(
        section_ids=section_ids, term_id=term_id)
    scheduled_results = Scheduled.get_scheduled_per_section_ids(
        section_ids=section_ids, term_id=term_id)

    room_ids = set(row['room_id'] for row in rows)
    room_ids.update(a.room_id for a in approval_results)
    room_ids.update(s.room_id for s in scheduled_results)
    rooms = Room.get_rooms(list(room_ids))
    rooms_by_id = {room.id: room for room in rooms}

    approvals_by_section_id = {section_id: [] for section_id in section_ids}
    for approval in approval_results:
        approvals_by_section_id[approval.section_id].append(
            approval.to_api_json(rooms_by_id=rooms_by_id))

    scheduled_by_section_id = {
        s.section_id: s.to_api_json(rooms_by_id=rooms_by_id)
        for s in scheduled_results
    }

    cross_listings_per_section_id, instructors_per_section_id, canvas_sites_by_section_id = _get_cross_listed_courses(
        section_ids=section_ids,
        term_id=term_id,
        approvals=approvals_by_section_id,
        invited_uids=invited_uids_by_section_id,
    )

    # Construct course objects.
    # If course has multiple instructors or multiple rooms then the section_id will be represented across multiple rows.
    # Multiple rooms are rare, but a course is sometimes associated with both an eligible and an ineligible room. We
    # order rooms in SQL by capability, NULLS LAST, and use scheduling data from the first row available.
    for row in rows:
        section_id = int(row['section_id'])
        if section_id in courses_per_id:
            course = courses_per_id[section_id]
        else:
            # Approvals and scheduled (JSON)
            approvals = approvals_by_section_id.get(section_id)
            scheduled = scheduled_by_section_id.get(section_id)
            # Instructors per cross-listings
            cross_listed_courses = cross_listings_per_section_id.get(
                section_id, [])
            instructors = instructors_per_section_id.get(section_id, [])
            # Construct course
            course = {
                'allowedUnits':
                row['allowed_units'],
                'approvals':
                approvals,
                'canvasCourseSites':
                canvas_sites_by_section_id.get(section_id, []),
                'courseName':
                row['course_name'],
                'courseTitle':
                row['course_title'],
                'crossListings':
                cross_listed_courses,
                'deletedAt':
                safe_strftime(row['deleted_at'], '%Y-%m-%d'),
                'hasOptedOut':
                section_id in section_ids_opted_out,
                'instructionFormat':
                row['instruction_format'],
                'instructors':
                instructors,
                'invitees':
                invited_uids_by_section_id.get(section_id),
                'isPrimary':
                row['is_primary'],
                'label':
                _construct_course_label(
                    course_name=row['course_name'],
                    instruction_format=row['instruction_format'],
                    section_num=row['section_num'],
                    cross_listings=cross_listed_courses,
                ),
                'meetings': {
                    'eligible': [],
                    'ineligible': [],
                },
                'nonstandardMeetingDates':
                False,
                'sectionId':
                section_id,
                'sectionNum':
                row['section_num'],
                'scheduled':
                scheduled,
                'termId':
                row['term_id'],
            }
            courses_per_id[section_id] = course

        # Note: Instructors associated with cross-listings are slurped up separately.
        instructor_uid = row['instructor_uid']
        if instructor_uid and instructor_uid not in [
                i['uid'] for i in course['instructors']
        ]:
            course['instructors'].append(
                _to_instructor_json(
                    row=row,
                    approvals=course['approvals'],
                    invited_uids=course['invitees'],
                ), )

        meeting = _to_meeting_json(row)
        eligible_meetings = course['meetings']['eligible']
        ineligible_meetings = course['meetings']['ineligible']
        if not next((m for m in (eligible_meetings + ineligible_meetings)
                     if meeting.items() <= m.items()), None):
            room = rooms_by_id.get(
                row['room_id']) if 'room_id' in row.keys() else None
            if room and room.capability:
                meeting['eligible'] = True
                meeting.update({
                    'recordingEndDate':
                    safe_strftime(get_recording_end_date(meeting), '%Y-%m-%d'),
                    'recordingStartDate':
                    safe_strftime(get_recording_start_date(meeting),
                                  '%Y-%m-%d'),
                })
                eligible_meetings.append(meeting)
                eligible_meetings.sort(
                    key=lambda m: f"{m['startDate']} {m['startTime']}")
                if meeting['startDate'] != app.config[
                        'CURRENT_TERM_BEGIN'] or meeting[
                            'endDate'] != app.config['CURRENT_TERM_END']:
                    course['nonstandardMeetingDates'] = True
            else:
                meeting['eligible'] = False
                ineligible_meetings.append(meeting)
                ineligible_meetings.sort(
                    key=lambda m: f"{m['startDate']} {m['startTime']}")
            if include_rooms:
                meeting['room'] = room.to_api_json() if room else None

    # Next, construct the feed
    api_json = []
    for section_id, course in courses_per_id.items():
        _decorate_course(course)
        # Add course to the feed
        api_json.append(course)

    return api_json
コード例 #25
0
    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
コード例 #26
0
    def test_scheduling_of_recordings(self):
        """If a course is scheduled for recording then email is sent to its instructor(s)."""
        with test_approvals_workflow(app):
            section_id = 22287
            term_id = app.config['CURRENT_TERM_ID']
            email_template_type = 'recordings_scheduled'

            def _get_emails_sent():
                return SentEmail.get_emails_of_type(
                    section_id=section_id,
                    template_type=email_template_type,
                    term_id=term_id,
                )

            emails_sent = _get_emails_sent()
            KalturaJob(app.app_context).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']
            assert len(instructors) == 2
            room_id = Room.find_room(course['meetingLocation']).id

            # This course requires two (2) approvals.
            approvals = [
                Approval.create(
                    approved_by_uid=instructors[0]['uid'],
                    approver_type_='instructor',
                    cross_listed_section_ids=[],
                    publish_type_='canvas',
                    recording_type_='presentation_audio',
                    room_id=Room.find_room('Barker 101').id,
                    section_id=section_id,
                    term_id=term_id,
                ),
            ]
            """If we have insufficient approvals then do nothing."""
            email_count = _get_emails_sent()
            KalturaJob(app.app_context).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',
                cross_listed_section_ids=[],
                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(app.app_context).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
            emails_sent = _get_emails_sent()
            assert len(emails_sent) == email_count + 1
            email_sent = emails_sent[-1].to_api_json()
            assert set(email_sent['recipientUids']) == {'98765', '87654'}
            assert email_sent['sectionId'] == section_id
            assert email_sent['templateType'] == 'recordings_scheduled'
            assert email_sent['termId'] == term_id
            assert email_sent['sentAt']
            """If recordings were already scheduled then do nothing, send no email."""
            email_count = len(_get_emails_sent())
            KalturaJob(app.app_context).run()
            assert len(_get_emails_sent()) == email_count