Ejemplo n.º 1
0
 def test_authorized(self, client, admin_session):
     """Admin user has access."""
     room_id = 1
     room = Room.get_room(room_id)
     capability = 'screencast_and_video' if room.capability == 'screencast' else 'screencast'
     room = self._api_update_capability(client, room_id, capability)
     assert len(room)
     assert room['capability'] == capability
Ejemplo n.º 2
0
 def test_authorized(self, client, admin_session):
     """Admin user has access."""
     room_id = 1
     room = Room.get_room(room_id)
     is_auditorium = not room.is_auditorium
     room = self._api_set_auditorium(client, room_id, is_auditorium)
     assert len(room)
     assert room['isAuditorium'] is is_auditorium
Ejemplo n.º 3
0
def update_room_capability():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if not room or 'capability' not in params:
        raise BadRequestError('Missing required parameters')
    capability = params.get('capability')
    room = Room.update_capability(room_id, capability)
    return tolerant_jsonify(room.to_api_json())
Ejemplo n.º 4
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')
Ejemplo n.º 5
0
def auditorium():
    params = request.get_json()
    room_id = params.get('roomId')
    room = Room.get_room(room_id) if room_id else None
    if room:
        is_auditorium = params.get('isAuditorium')
        if not room_id or is_auditorium is None:
            raise BadRequestError("'roomId' and 'isAuditorium' are required.")
        room = Room.set_auditorium(room_id, is_auditorium)
        return tolerant_jsonify(room.to_api_json())
    else:
        raise ResourceNotFoundError('No such room')
Ejemplo n.º 6
0
 def to_api_json(self, rooms_by_id=None):
     room_feed = None
     if self.room_id:
         if rooms_by_id:
             room_feed = rooms_by_id.get(self.room_id, None).to_api_json()
         else:
             room_feed = Room.get_room(self.room_id).to_api_json()
     formatted_days = format_days(self.meeting_days)
     return {
         'id':
         self.id,
         'alerts':
         self.alerts or [],
         'createdAt':
         to_isoformat(self.created_at),
         'instructorUids':
         self.instructor_uids,
         'kalturaScheduleId':
         self.kaltura_schedule_id,
         'meetingDays':
         formatted_days,
         'meetingDaysNames':
         get_names_of_days(formatted_days),
         'meetingEndDate':
         datetime.strftime(self.meeting_end_date, '%Y-%m-%d'),
         'meetingEndTime':
         self.meeting_end_time,
         'meetingEndTimeFormatted':
         format_time(self.meeting_end_time),
         'meetingStartDate':
         datetime.strftime(self.meeting_start_date, '%Y-%m-%d'),
         'meetingStartTime':
         self.meeting_start_time,
         'meetingStartTimeFormatted':
         format_time(self.meeting_start_time),
         'publishType':
         self.publish_type,
         'publishTypeName':
         NAMES_PER_PUBLISH_TYPE[self.publish_type],
         'recordingType':
         self.recording_type,
         'recordingTypeName':
         NAMES_PER_RECORDING_TYPE[self.recording_type],
         'room':
         room_feed,
         'sectionId':
         self.section_id,
         'termId':
         self.term_id,
     }
Ejemplo n.º 7
0
 def to_api_json(self):
     return {
         'approvedBy': get_calnet_user_for_uid(app, self.approved_by_uid),
         'wasApprovedByAdmin': self.approver_type == 'admin',
         'createdAt': to_isoformat(self.created_at),
         'crossListedSectionIds': self.cross_listed_section_ids,
         'publishType': self.publish_type,
         'publishTypeName': NAMES_PER_PUBLISH_TYPE[self.publish_type],
         'recordingType': self.recording_type,
         'recordingTypeName': NAMES_PER_RECORDING_TYPE[self.recording_type],
         'room': Room.get_room(self.room_id).to_api_json()
         if self.room_id else None,
         'sectionId': self.section_id,
         'termId': self.term_id,
     }
Ejemplo n.º 8
0
 def to_api_json(self):
     return {
         'createdAt': to_isoformat(self.created_at),
         'crossListedSectionIds': self.cross_listed_section_ids,
         'instructorUids': self.instructor_uids,
         'meetingDays': format_days(self.meeting_days),
         'meetingEndTime': format_time(self.meeting_end_time),
         'meetingStartTime': format_time(self.meeting_start_time),
         'publishType': self.publish_type,
         'publishTypeName': NAMES_PER_PUBLISH_TYPE[self.publish_type],
         'recordingType': self.recording_type,
         'recordingTypeName': NAMES_PER_RECORDING_TYPE[self.recording_type],
         'room': Room.get_room(self.room_id).to_api_json()
         if self.room_id else None,
         'sectionId': self.section_id,
         'termId': self.term_id,
     }
Ejemplo n.º 9
0
 def to_api_json(self, rooms_by_id=None):
     room_feed = None
     if self.room_id:
         if rooms_by_id:
             room_feed = rooms_by_id.get(self.room_id, None).to_api_json()
         else:
             room_feed = Room.get_room(self.room_id).to_api_json()
     return {
         'approvedBy': get_calnet_user_for_uid(app, self.approved_by_uid),
         'wasApprovedByAdmin': self.approver_type == 'admin',
         'createdAt': to_isoformat(self.created_at),
         'publishType': self.publish_type,
         'publishTypeName': NAMES_PER_PUBLISH_TYPE[self.publish_type],
         'recordingType': self.recording_type,
         'recordingTypeName': NAMES_PER_RECORDING_TYPE[self.recording_type],
         'room': room_feed,
         'sectionId': self.section_id,
         'termId': self.term_id,
     }
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
def _schedule_recordings(all_approvals, course):
    term_id = course['termId']
    section_id = int(course['sectionId'])
    all_approvals.sort(key=lambda a: a.created_at.isoformat())
    approval = all_approvals[-1]

    room = Room.get_room(approval.room_id)
    meeting_days, meeting_start_time, meeting_end_time = SisSection.get_meeting_times(
        term_id=term_id,
        section_id=section_id,
    )
    time_format = '%H:%M'
    # Recording starts X minutes before/after official start; it ends Y minutes before/after official end time.
    recording_offset_start = app.config['KALTURA_RECORDING_OFFSET_START']
    recording_offset_end = app.config['KALTURA_RECORDING_OFFSET_END']
    adjusted_start_time = datetime.strptime(
        meeting_start_time,
        time_format) + timedelta(minutes=recording_offset_start)
    adjusted_end_time = datetime.strptime(
        meeting_end_time,
        time_format) + timedelta(minutes=recording_offset_end)
    days = format_days(meeting_days)
    instructor_uids = [
        instructor['uid'] for instructor in course['instructors']
    ]

    app.logger.info(f"""
        Prepare to schedule recordings for {course["label"]}:
            Room: {room.location}
            Instructor UIDs: {instructor_uids}
            Schedule: {days}, {adjusted_start_time} to {adjusted_end_time}
            Recording: {approval.recording_type}; {approval.publish_type}
    """)

    if room.kaltura_resource_id:
        Kaltura().schedule_recording(
            course_label=course['label'],
            instructor_uids=instructor_uids,
            days=days,
            start_time=adjusted_start_time,
            end_time=adjusted_end_time,
            publish_type=approval.publish_type,
            recording_type=approval.recording_type,
            room=room,
        )
        scheduled = 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=approval.room_id,
            section_id=section_id,
            term_id=term_id,
        )
        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)}'
        )

    else:
        app.logger.error(f"""
            FAILED to schedule recordings because room has no 'kaltura_resource_id'.
            Course: {course}
            Room: {room}
            Latest approval: {approval}
        """)
Ejemplo n.º 12
0
def _to_api_json(term_id, rows, include_rooms=True):
    courses_per_id = {}
    instructors_per_section_id = {}
    section_ids_opted_out = CoursePreference.get_section_ids_opted_out(
        term_id=term_id)
    # If course has multiple instructors then the section_id will be represented across multiple rows.
    for row in rows:
        approvals = []
        section_id = int(row['section_id'])
        if section_id not in courses_per_id:
            # Construct new course
            instructors_per_section_id[section_id] = []
            has_opted_out = section_id in section_ids_opted_out
            cross_listings = _get_cross_listed_courses(section_id=section_id,
                                                       term_id=term_id)
            approvals, scheduled = _get_approvals_and_scheduled(
                section_ids=[section_id] +
                [c['sectionId'] for c in cross_listings],
                term_id=term_id,
            )
            course_name = row['course_name']
            instruction_format = row['instruction_format']
            section_num = row['section_num']
            course = {
                'allowedUnits': row['allowed_units'],
                'canvasCourseSites': _canvas_course_sites(term_id, section_id),
                'courseName': course_name,
                'courseTitle': row['course_title'],
                'crossListings': cross_listings,
                'hasOptedOut': has_opted_out,
                'instructionFormat': instruction_format,
                'instructors': [],
                'isPrimary': row['is_primary'],
                'label': f'{course_name}, {instruction_format} {section_num}',
                'meetingDays': format_days(row['meeting_days']),
                'meetingEndDate': row['meeting_end_date'],
                'meetingEndTime': format_time(row['meeting_end_time']),
                'meetingLocation': row['meeting_location'],
                'meetingStartDate': row['meeting_start_date'],
                'meetingStartTime': format_time(row['meeting_start_time']),
                'sectionId': section_id,
                'sectionNum': section_num,
                'termId': row['term_id'],
                'approvals': approvals,
                'scheduled': scheduled,
            }
            invites = SentEmail.get_emails_of_type(
                section_id=section_id,
                template_type='invitation',
                term_id=term_id,
            )
            course['invitees'] = []
            for invite in invites:
                course['invitees'].extend(invite.recipient_uids)

            if scheduled:
                course['status'] = 'Scheduled'
            elif approvals:
                course['status'] = 'Partially Approved'
            else:
                course['status'] = 'Invited' if invites else 'Not Invited'

            if include_rooms:
                room = Room.get_room(
                    row['room_id']).to_api_json() if 'room_id' in row else None
                course['room'] = room
            courses_per_id[section_id] = course

        # Build upon course object with one instructor per row.
        instructor_uid = row['instructor_uid']
        if instructor_uid not in [
                i['uid'] for i in instructors_per_section_id[section_id]
        ]:
            instructors_per_section_id[section_id].append({
                'approval':
                next((a for a in approvals
                      if a['approvedBy']['uid'] == instructor_uid), False),
                'deptCode':
                row['instructor_dept_code'],
                'email':
                row['instructor_email'],
                'name':
                row['instructor_name'],
                'roleCode':
                row['instructor_role_code'],
                'uid':
                instructor_uid,
                'wasSentInvite':
                instructor_uid in courses_per_id[section_id]['invitees'],
            })

    api_json = []
    for section_id, course in courses_per_id.items():
        room_id = course.get('room', {}).get('id')

        def _add_and_verify_room(approval_or_scheduled):
            action_room_id = approval_or_scheduled.get('room', {}).get('id')
            is_obsolete_room = not room_id or room_id != action_room_id
            approval_or_scheduled['hasObsoleteRoom'] = is_obsolete_room

        course['instructors'] = instructors_per_section_id[section_id]
        course['hasNecessaryApprovals'] = _has_necessary_approvals(course)
        scheduled = course['scheduled']
        # Check for course changes w.r.t. room, meeting times, and instructors.
        if scheduled:

            def _meeting(obj):
                return f'{obj["meetingDays"]}-{obj["meetingStartTime"]}-{obj["meetingEndTime"]}'

            instructor_uids = set(
                [instructor['uid'] for instructor in course['instructors']])
            scheduled['hasObsoleteInstructors'] = instructor_uids != set(
                scheduled['instructorUids'])
            scheduled['hasObsoleteMeetingTimes'] = _meeting(
                course) != _meeting(scheduled)
            _add_and_verify_room(scheduled)

        for approval in course['approvals']:
            _add_and_verify_room(approval)

        # Add course to the feed
        api_json.append(course)
    return api_json