Esempio n. 1
0
    def test_has_instructors(self, client, admin_session):
        """Admins can see instructor changes that might disrupt scheduled recordings."""
        with test_approvals_workflow(app):
            meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times(
                term_id=self.term_id,
                section_id=section_3_id,
            )
            instructor_uids = SisSection.get_instructor_uids(term_id=self.term_id, section_id=section_3_id)
            Scheduled.create(
                cross_listed_section_ids=[],
                instructor_uids=instructor_uids + ['999999'],
                meeting_days=meeting_days,
                meeting_start_time=meeting_start_time,
                meeting_end_time=meeting_end_time,
                publish_type_='canvas',
                recording_type_='presenter_audio',
                room_id=Room.get_room_id(section_id=section_3_id, term_id=self.term_id),
                section_id=section_3_id,
                term_id=self.term_id,
            )
            std_commit(allow_test_environment=True)

            api_json = self._api_course_changes(client, term_id=self.term_id)
            course = _find_course(api_json=api_json, section_id=section_3_id)
            assert course
            assert course['scheduled']['hasObsoleteRoom'] is False
            assert course['scheduled']['hasObsoleteMeetingTimes'] is False
            assert course['scheduled']['hasObsoleteInstructors'] is True
Esempio n. 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))
Esempio n. 3
0
def _load_courses():
    term_id = app.config['CURRENT_TERM_ID']
    with open(f"{app.config['BASE_DIR']}/fixtures/sis/courses.json",
              'r') as file:
        sis_sections = json.loads(file.read())
        SisSection.refresh(sis_sections=sis_sections, term_id=term_id)
        std_commit(allow_test_environment=True)
    _load_supplemental_course_data(term_id=term_id)
Esempio n. 4
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)))
Esempio n. 5
0
 def _run(self):
     self.term_id = app.config['CURRENT_TERM_ID']
     self.courses = SisSection.get_course_changes(term_id=self.term_id)
     self._date_change_alerts()
     self._instructor_change_alerts()
     self._multiple_meeting_pattern_alerts()
     self._room_change_alerts()
Esempio n. 6
0
 def _assert_schedule_is_obsolete(
         self,
         expect_obsolete_dates,
         expect_obsolete_times,
         meeting,
         override_days=None,
         override_end_date=None,
         override_end_time=None,
         override_start_date=None,
         override_start_time=None,
 ):
     with test_approvals_workflow(app):
         with override_config(app, 'CURRENT_TERM_RECORDINGS_BEGIN', meeting['startDate']):
             with override_config(app, 'CURRENT_TERM_RECORDINGS_END', meeting['endDate']):
                 mock_scheduled(
                     meeting=meeting,
                     override_days=override_days,
                     override_end_date=override_end_date,
                     override_end_time=override_end_time,
                     override_start_date=override_start_date,
                     override_start_time=override_start_time,
                     section_id=self.section_id,
                     term_id=self.term_id,
                 )
                 course = SisSection.get_course(section_id=self.section_id, term_id=self.term_id)
                 scheduled = course['scheduled']
                 assert are_scheduled_dates_obsolete(meeting=meeting, scheduled=scheduled) is expect_obsolete_dates
                 assert are_scheduled_times_obsolete(meeting=meeting, scheduled=scheduled) is expect_obsolete_times
    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)
Esempio n. 8
0
    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)
Esempio n. 9
0
 def _run(self, args=None):
     term_id = app.config['CURRENT_TERM_ID']
     for queued_email in QueuedEmail.get_all(term_id):
         course = SisSection.get_course(term_id,
                                        queued_email.section_id,
                                        include_deleted=True)
         if not course:
             app.logger.warn(
                 f'Email will remain queued until course data is present: {queued_email}'
             )
             continue
         if course['hasOptedOut']:
             QueuedEmail.delete(queued_email)
             continue
         if BConnected().send(
                 message=queued_email.message,
                 recipient=queued_email.recipient,
                 section_id=queued_email.section_id,
                 subject_line=queued_email.subject_line,
                 template_type=queued_email.template_type,
                 term_id=term_id,
         ):
             QueuedEmail.delete(queued_email)
         else:
             # If send() fails then report the error and DO NOT delete the queued item.
             app.logger.error(f'Failed to send email: {queued_email}')
Esempio n. 10
0
def _update_already_scheduled_events():
    kaltura = Kaltura()
    for course in SisSection.get_courses_scheduled(
            term_id=app.config['CURRENT_TERM_ID']):
        course_name = course['label']
        scheduled = course['scheduled']
        kaltura_schedule = kaltura.get_event(
            event_id=scheduled['kalturaScheduleId'])
        if kaltura_schedule:
            if course['canvasCourseSites'] and scheduled[
                    'publishType'] == 'kaltura_media_gallery':
                # From Kaltura, get Canvas course sites (categories) currently mapped to the course.
                template_entry_id = kaltura_schedule['templateEntryId']
                categories = kaltura.get_categories(template_entry_id)

                for s in course['canvasCourseSites']:
                    canvas_course_site_id = str(s['courseSiteId'])
                    if canvas_course_site_id not in [
                            c['name'] for c in categories
                    ]:
                        _update_kaltura_category(canvas_course_site_id,
                                                 course_name, kaltura,
                                                 template_entry_id)
        else:
            app.logger.warn(
                f'The previously scheduled {course_name} has no schedule_event in Kaltura.'
            )
Esempio n. 11
0
def test_email_template(template_id):
    email_template = EmailTemplate.get_template(template_id)
    if email_template:
        course = SisSection.get_course(term_id=app.config['CURRENT_TERM_ID'],
                                       section_id='12597')
        template = EmailTemplate.get_template(template_id)
        subject_line = interpolate_email_content(
            course=course,
            recipient_name=current_user.name,
            templated_string=template.subject_line,
        )
        message = interpolate_email_content(
            course=course,
            recipient_name=current_user.name,
            templated_string=template.message,
        )
        BConnected().send(
            recipients=[
                {
                    'email': current_user.email_address,
                    'name': current_user.name,
                    'uid': current_user.uid,
                },
            ],
            message=message,
            subject_line=subject_line,
        )
        return tolerant_jsonify(
            {'message': f'Email sent to {current_user.email_address}'}), 200
    else:
        raise ResourceNotFoundError('No such email_template')
Esempio n. 12
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
Esempio n. 13
0
def refresh_rooms():
    locations = SisSection.get_distinct_meeting_locations()
    existing_locations = Room.get_all_locations()
    new_locations = [
        location for location in locations
        if location not in existing_locations
    ]
    if new_locations:
        app.logger.info(f'Creating {len(new_locations)} new rooms')
        for location in new_locations:
            Room.create(location=location)

    def _normalize(room_location):
        return re.sub(r'[\W_]+', '', room_location).lower()

    kaltura_resource_ids_per_room = {}
    all_rooms = Room.all_rooms()
    for resource in Kaltura().get_schedule_resources():
        location = _normalize(resource['name'])
        if location:
            for room in all_rooms:
                if _normalize(room.location) == location:
                    kaltura_resource_ids_per_room[room.id] = resource['id']
                    break

    if kaltura_resource_ids_per_room:
        Room.update_kaltura_resource_mappings(kaltura_resource_ids_per_room)
Esempio n. 14
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)
Esempio n. 15
0
def test_email_template(template_id):
    email_template = EmailTemplate.get_template(template_id)
    if email_template:
        course = SisSection.get_random_co_taught_course(app.config['CURRENT_TERM_ID'])
        template = EmailTemplate.get_template(template_id)
        publish_types = get_all_publish_types()
        recording_types = get_all_recording_types()

        def _get_interpolated_content(templated_string):
            return interpolate_content(
                course=course,
                pending_instructors=course['instructors'],
                previous_publish_type_name=NAMES_PER_PUBLISH_TYPE[publish_types[0]],
                previous_recording_type_name=NAMES_PER_RECORDING_TYPE[recording_types[0]],
                publish_type_name=NAMES_PER_PUBLISH_TYPE[publish_types[1]],
                recording_type_name=NAMES_PER_RECORDING_TYPE[recording_types[1]],
                recipient_name=current_user.name,
                templated_string=templated_string,
            )
        BConnected().send(
            recipient={
                'email': current_user.email_address,
                'name': current_user.name,
                'uid': current_user.uid,
            },
            message=_get_interpolated_content(template.message),
            subject_line=_get_interpolated_content(template.subject_line),
        )
        return tolerant_jsonify({'message': f'Email sent to {current_user.email_address}'}), 200
    else:
        raise ResourceNotFoundError('No such email_template')
Esempio n. 16
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)
Esempio n. 17
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
Esempio n. 18
0
def get_course(term_id, section_id):
    course = SisSection.get_course(term_id, section_id)
    if not course:
        raise ResourceNotFoundError(f'No section for term_id = {term_id} and section_id = {section_id}')

    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError(f'Sorry, you are unauthorized to view the course {course["label"]}.')
    return tolerant_jsonify(course)
Esempio n. 19
0
 def _schedule():
     mock_scheduled(
         override_room_id=Room.find_room('Barker 101').id,
         section_id=section_id,
         term_id=term_id,
     )
     course = SisSection.get_course(section_id=section_id, term_id=term_id)
     assert course['scheduled']['hasObsoleteRoom'] is True
    def test_room_change_no_longer_eligible(self, db_session):
        section_id = 50004
        term_id = app.config['CURRENT_TERM_ID']

        def _move_course(meeting_location):
            db.session.execute(
                text(
                    'UPDATE sis_sections SET meeting_location = :meeting_location WHERE term_id = :term_id AND section_id = :section_id'
                ),
                {
                    'meeting_location': meeting_location,
                    'section_id': section_id,
                    'term_id': term_id,
                },
            )

        with enabled_job(job_key=InstructorEmailsJob.key()):
            with test_approvals_workflow(app):
                course = SisSection.get_course(section_id=section_id,
                                               term_id=term_id)
                eligible_meetings = course.get('meetings',
                                               {}).get('eligible', [])
                assert len(eligible_meetings) == 1
                original_room = eligible_meetings[0]['room']
                assert original_room['location'] == 'Li Ka Shing 145'

                # Schedule
                _schedule(original_room['id'], section_id)
                _run_instructor_emails_job()
                _assert_email_count(0, section_id,
                                    'room_change_no_longer_eligible')

                # Move course to some other eligible room.
                _move_course('Barker 101')
                _run_instructor_emails_job()
                _assert_email_count(0, section_id,
                                    'room_change_no_longer_eligible')

                # Move course to an ineligible room.
                ineligible_room = 'Wheeler 150'
                _move_course(ineligible_room)
                _run_instructor_emails_job()
                _assert_email_count(1, section_id,
                                    'room_change_no_longer_eligible')

                # Move course back to its original location
                _move_course(original_room['location'])

                # Finally, let's pretend the course is scheduled to a room that was previously eligible.
                Scheduled.delete(section_id=section_id, term_id=term_id)
                _schedule(Room.find_room(ineligible_room).id, section_id)
                _run_instructor_emails_job()
                # Expect email.
                _assert_email_count(2, section_id,
                                    'room_change_no_longer_eligible')
                Scheduled.delete(section_id=section_id, term_id=term_id)
Esempio n. 21
0
    def after_sis_data_refresh(cls, term_id):
        distinct_instructor_uids = SisSection.get_distinct_instructor_uids()
        insert_or_update_instructors(distinct_instructor_uids)
        app.logger.info(f'{len(distinct_instructor_uids)} instructors updated')

        refresh_rooms()
        app.logger.info('RDS indexes updated.')

        refresh_cross_listings(term_id=term_id)
        app.logger.info('Cross-listings updated.')
Esempio n. 22
0
 def test_interpolate_email_content(self):
     user = get_calnet_user_for_uid(app, '8765432')
     course = SisSection.get_course(app.config['CURRENT_TERM_ID'], '28165')
     interpolated = interpolate_email_content(
         course=course,
         recipient_name=user['name'],
         templated_string=_get_email_template(),
     )
     actual = _normalize(interpolated)
     expected = _normalize(_get_expected_email())
     assert expected == actual
Esempio n. 23
0
def get_user(uid):
    user = get_calnet_user_for_uid(app=app, uid=uid)
    if user.get('isExpiredPerLdap', True):
        raise ResourceNotFoundError('No such user')
    else:
        courses = SisSection.get_courses_per_instructor_uid(
            term_id=app.config['CURRENT_TERM_ID'],
            instructor_uid=uid,
        )
        user['courses'] = courses
        return tolerant_jsonify(user)
Esempio n. 24
0
def get_room(room_id):
    room = Room.get_room(room_id)
    if room:
        api_json = room.to_api_json()
        api_json['courses'] = SisSection.get_courses_per_location(
            term_id=app.config['CURRENT_TERM_ID'],
            location=room.location,
        )
        return tolerant_jsonify(api_json)
    else:
        raise ResourceNotFoundError('No such room')
Esempio n. 25
0
    def test_currently_no_person_teaching_course(self):
        """Refuse to queue emails for a course without a proper instructor."""
        term_id = app.config['CURRENT_TERM_ID']
        section_id = 50006
        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 creation fails.
        assert not QueuedEmail.create(section_id, email_template_type, term_id, recipient=None)
        assert section_id not in QueuedEmail.get_all_section_ids(template_type=email_template_type, term_id=term_id)
Esempio n. 26
0
 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,
                     )
    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
Esempio n. 28
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))
Esempio n. 29
0
def find_courses():
    params = request.get_json()
    term_id = params.get('termId')
    filter_ = params.get('filter', 'Not Invited')
    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_ == '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_ == 'Scheduled':
        courses = SisSection.get_courses_scheduled(term_id)
    else:
        raise BadRequestError(f'Invalid filter: {filter_}')

    return tolerant_jsonify(courses)
Esempio n. 30
0
def get_course(term_id, section_id):
    course = SisSection.get_course(term_id, section_id, include_deleted=True)
    if not course:
        raise ResourceNotFoundError(f'No section for term_id = {term_id} and section_id = {section_id}')
    if not current_user.is_admin and current_user.uid not in [i['uid'] for i in course['instructors']]:
        raise ForbiddenRequestError(f'Sorry, you are unauthorized to view the course {course["label"]}.')

    if current_user.is_admin and course['scheduled']:
        # When debugging, the raw Kaltura-provided JSON is useful.
        event_id = course['scheduled'].get('kalturaScheduleId')
        course['scheduled']['kalturaSchedule'] = Kaltura().get_event(event_id)
    return tolerant_jsonify(course)