def handle(self, *args, **options): self._commit = options['commit'] self._courses = {} self._session = SessionManagement() self._recorder = RemoteRecorderManagement() if options['stdin']: for line in sys.stdin: self._process_course(line.rstrip('\n')) else: for session in args: self._process_course(session)
def set_panopto_generic_folder(event): session_api = SessionManagement() id_string = "%s - %s" % (event['name'], event['space']['id']) folder_name = event['name'] folder_external_id = panopto_generic_external_id(id_string) creators = [] folders = session_api.getFoldersList(search_query=event['name']) if folders and len(folders) == 1: folder_name = folders[0].Name folder_external_id = folders[0].ExternalId creators = get_panopto_folder_creators(folders[0].Id) event['recording']['folder']['name'] = folder_name event['recording']['folder']['external_id'] = folder_external_id event['recording']['folder']['auth'] = {'creators': creators}
def set_panopto_generic_folder(event): session_api = SessionManagement() id_string = "%s - %s" % (event['name'], event['space']['id']) folder_name = event['name'] folder_external_id = panopto_generic_external_id(id_string) creators = [] folders = session_api.getFoldersList(search_query=event['name']) if folders and len(folders) == 1: folder_name = folders[0].Name folder_external_id = folders[0].ExternalId creators = get_panopto_folder_creators(folders[0].Id) event['recording']['folder']['name'] = folder_name event['recording']['folder']['external_id'] = folder_external_id event['recording']['folder']['auth'] = { 'creators': creators }
class Folder(RESTDispatch): def __init__(self): self._session = SessionManagement() self._access = AccessManagement() self._user = UserManagement() def GET(self, request, **kwargs): folder_id = kwargs.get('folder_id') if (folder_id): return self._get_folder_details(space_id) else: params = {} for q in request.GET: params[q] = request.GET.get(q) return self._list_folders(params) def _get_folder_details(self, space_id): return self.json_response('{}') def _list_folders(self, args): folders = [] if 'search' in args: search = args['search'].strip() if len(search) > 3: for folder in self._session.getFoldersList(search_query=search): creators = [] viewers = [] deets = self._access.getFolderAccessDetails(folder['Id']) if deets.UsersWithCreatorAccess: response = self._user.getUsers(deets.UsersWithCreatorAccess.guid) if response and response.User: for user in response.User: match = re.match(r'^%s\\(.+)$' % (settings.PANOPTO_API_APP_ID), user.UserKey) creators.append({ 'key': match.group(1) if match else user.UserKey, 'id': user.UserId }) if deets.UsersWithViewerAccess: response = self._user.getUsers(deets.UsersWithCreatorAccess.guid) if response and response.User: for user in response.User: match = re.match(r'^%s\\(.+)$' % (settings.PANOPTO_API_APP_ID), user.UserKey) viewers.append({ 'key': match.group(1) if match else user.UserKey, 'id': user.UserId }) folders.append({ 'name': folder.Name, 'id': folder.Id, 'auth' : { 'creators': creators, 'viewers': viewers } }) return self.json_response(folders)
class SessionBroadcast(RESTDispatch): def __init__(self): self._session_api = SessionManagement() self._audit_log = logging.getLogger('audit') def GET(self, request, **kwargs): session_id = kwargs.get('session_id') if session_id: raw_session = self._session_api.getSessionsById([session_id])[0][0] broadcast = { 'is_broadcast': raw_session['IsBroadcast'], } else: broadcast = {} return self.json_response(broadcast) def PUT(self, request, **kwargs): try: session_id = kwargs.get('session_id') data = json.loads(request.body) is_broadcast = self._valid_boolean( data.get("is_broadcast", False), 'bad broadcast flag') self._session_api.updateSessionIsBroadcast(session_id, is_broadcast) self._audit_log.info('%s set %s broadcast to %s' % ( request.user, session_id, is_broadcast)) return self.json_response({ 'recording_id': session_id }) except InvalidParamException as ex: return self.error_response(400, "%s" % ex) except Exception as ex: return self.error_response(500, "Unable to save session: %s" % ex) def _valid_boolean(self, v, errstr): if not (v is None or type(v) == bool): raise InvalidParamException(errstr) return v
class Folder(RESTDispatch): def __init__(self): self._session = SessionManagement() self._access = AccessManagement() self._user = UserManagement() def GET(self, request, **kwargs): folder_id = kwargs.get("folder_id") if folder_id: return self._get_folder_details(space_id) else: params = {} for q in request.GET: params[q] = request.GET.get(q) return self._list_folders(params) def _get_folder_details(self, space_id): return self.json_response("{}") def _list_folders(self, args): folders = [] if "search" in args: search = args["search"].strip() if len(search) > 3: for folder in self._session.getFoldersList(search_query=search): creators = [] viewers = [] deets = self._access.getFolderAccessDetails(folder["Id"]) if deets.UsersWithCreatorAccess: response = self._user.getUsers(deets.UsersWithCreatorAccess.guid) if response and response.User: for user in response.User: match = re.match(r"^%s\\(.+)$" % (settings.PANOPTO_API_APP_ID), user.UserKey) creators.append({"key": match.group(1) if match else user.UserKey, "id": user.UserId}) if deets.UsersWithViewerAccess: response = self._user.getUsers(deets.UsersWithCreatorAccess.guid) if response and response.User: for user in response.User: match = re.match(r"^%s\\(.+)$" % (settings.PANOPTO_API_APP_ID), user.UserKey) viewers.append({"key": match.group(1) if match else user.UserKey, "id": user.UserId}) folders.append( {"name": folder.Name, "id": folder.Id, "auth": {"creators": creators, "viewers": viewers}} ) return self.json_response(folders)
class Command(BaseCommand): help = "Matchrecording session dates to SWS meeting times" option_list = BaseCommand.option_list + ( make_option('--commit', dest='commit', action="store_true", default=False, help='Update Panopto recording with SWS meeting time'), make_option('--stdin', dest='stdin', action="store_true", default=False, help='get Panopto session external ids on standard input'), ) def handle(self, *args, **options): self._commit = options['commit'] self._courses = {} self._session = SessionManagement() self._recorder = RemoteRecorderManagement() if options['stdin']: for line in sys.stdin: self._process_course(line.rstrip('\n')) else: for session in args: self._process_course(session) def _process_course(self, session_id): # 2015-spring-PSYCH-202-A-2015-06-04 course = re.match( r'^(20[0-9]{2})-(winter|spring|summer|autumn)' r'-([A-Z ]+)-([0-9]{3})-([A-Z][A-Z0-9]*)-2*', session_id) if course: label = "%s,%s,%s,%s/%s" % (course.group(1), course.group(2), course.group(3), course.group(4), course.group(5)) if label not in self._courses: now = datetime.datetime.now(tz.tzlocal()).replace( second=0, microsecond=0) section = get_section_by_label( label, include_instructor_not_on_time_schedule=False) (start, end) = self._lecture_times(section) self._courses[label] = { 'start': start.split(':'), 'end': end.split(':') } offered = self._courses[label] else: print >> sys.stderr, "unrecognized session id: %s" % session_id return pan_session = self._session.getSessionsByExternalId([session_id]) if 'Session' in pan_session and len(pan_session.Session) == 1: # broken-ass suds. fsuds = re.match(r'.*\<a\:StartTime\>([^<]+)\<\/a\:StartTime\>.*', self._session._api.last_received().plain()) if not fsuds: Exception('Untrustable time') pan_start = parser.parse(fsuds.group(1)) pan_start_local = pan_start.astimezone(tz.tzlocal()) sws_start_local = pan_start_local.replace( hour=int(offered['start'][0]), minute=int(offered['start'][1])) sws_end_local = pan_start_local.replace( hour=int(offered['end'][0]), minute=int(offered['end'][1])) schedule_delta = sws_start_local - pan_start_local duration_delta = (sws_end_local - sws_start_local).seconds - int( pan_session.Session[0].Duration) if schedule_delta or duration_delta: pan_start = (pan_start_local + schedule_delta).astimezone( tz.tzutc()) duration = pan_session.Session[0].Duration if duration_delta: duration += duration_delta pan_end = pan_start + datetime.timedelta(0, duration) adjustment = [ session_id, '(%s)' % pan_session.Session[0].Id, '' if self._commit else 'WOULD', 'RESCHEDULE', fsuds.group(1), 'TO', pan_start.isoformat(), ':' ] if schedule_delta.days < 0: adjustment.append("(-%s shift)" % (datetime.timedelta() - schedule_delta)) else: adjustment.append("(%s shift)" % schedule_delta) if duration_delta: adjustment.append('AND DURATION') adjustment.append("%s" % duration_delta) adjustment.append('seconds') print >> sys.stderr, ' '.join(adjustment) if self._commit: result = self._recorder.updateRecordingTime( pan_session.Session[0].Id, pan_start.isoformat(), pan_end.isoformat()) if not result: print >> sys.stderr, "FAIL: null return value" elif result.ConflictsExist: print >> sys.stderr, "CONFLICT: %s" % ( result.ConflictingSessions[0][0].SessionName) else: print >> sys.stderr, "UPDATED %s" % ( result.SessionIDs[0][0]) else: print >> sys.stderr, "%s: UNCHANGED" % (session_id) else: print >> sys.stderr, "unrecognized session id: %s" % session_id def _lecture_times(self, section): for meeting in section.meetings: if (meeting.meeting_type in ['lecture', 'quiz', 'seminar'] and meeting.start_time and meeting.end_time): return meeting.start_time, meeting.end_time Exception("no lecture times set")
def __init__(self): self._session_api = SessionManagement() self._audit_log = logging.getLogger('audit')
def __init__(self): self._session_api = SessionManagement() self._recorder_api = RemoteRecorderManagement() self._access_api = AccessManagement() self._user_api = UserManagement() self._audit_log = logging.getLogger('audit')
class Session(RESTDispatch): def __init__(self): self._session_api = SessionManagement() self._recorder_api = RemoteRecorderManagement() self._access_api = AccessManagement() self._user_api = UserManagement() self._audit_log = logging.getLogger('audit') def GET(self, request, **kwargs): session_id = kwargs.get('session_id') if session_id: raw_session = self._session_api.getSessionsById( [session_id])[0][0] raw_access = self._access_api.getSessionAccessDetails( session_id) start_utc = pytz.utc.localize( raw_session['StartTime']).astimezone(tz.tzutc()) session = { 'creator_id': raw_session['CreatorId'], 'description': raw_session['Description'], 'duration': raw_session['Duration'], 'external_id': raw_session['ExternalId'], 'folder_id': raw_session['FolderId'], 'folder_name': raw_session['FolderName'], 'folder_creators': [], 'id': raw_session['Id'], 'is_video_url': raw_session['IosVideoUrl'], 'is_broadcast': raw_session['IsBroadcast'], 'is_public': raw_access['IsPublic'], 'is_downloadable': raw_session['IsDownloadable'], 'name': raw_session['Name'], 'remote_recorder_ids': raw_session['RemoteRecorderIds'].get( 'guid', None), 'share_page_url': raw_session['SharePageUrl'], 'start_time': start_utc.isoformat(), 'state': raw_session['State'], 'status_message': raw_session['StatusMessage'], 'thumb_url': raw_session['ThumbUrl'], 'viewer_url': raw_session['ViewerUrl'], } else: session = {} return self.json_response(session) def POST(self, request, **kwargs): try: new_session = self._validate_session(request.body) session = self._recorder_api.scheduleRecording(new_session.get('name'), new_session.get('folder_id'), new_session.get('is_broadcast'), new_session.get('start_time'), new_session.get('end_time'), new_session.get('recorder_id')) if session.ConflictsExist: conflict = session.ConflictingSessions[0][0] start_time = conflict.StartTime end_time = conflict.EndTime content = { 'conflict_name': conflict.SessionName, 'conflict_start': start_time.isoformat(), 'conflict_end': end_time.isoformat() } return self.error_response(409, "Schedule Conflict Exists", content=content) session_id = session.SessionIDs[0][0] self._session_api.updateSessionExternalId( session_id, new_session.get('external_id')) if new_session.get('is_public'): self._access_api.updateSessionIsPublic(session_id, True) messages = [] creators = new_session.get('folder_creators') if creators and type(creators) is list: messages = self._sync_creators( new_session.get('folder_id'), creators) self._audit_log.info('%s scheduled %s for %s from %s to %s' % ( request.user, new_session.get('external_id'), new_session.get('uwnetid'), new_session.get('start_time'), new_session.get('end_time'))) return self.json_response({ 'recording_id': session_id, 'messages': messages }) except InvalidParamException as ex: return self.error_response(400, "%s" % ex) except Exception as ex: return self.error_response(500, "Unable to save session: %s" % ex) def PUT(self, request, **kwargs): try: session_update = self._validate_session(request.body) session = self._session_api.getSessionsById( session_update.get('recording_id'))[0][0] start_utc = session.StartTime.astimezone(pytz.utc) end_utc = start_utc + datetime.timedelta( seconds=int(session.Duration)) session_update_start = self._valid_time(session_update.get('start_time')) session_update_end = self._valid_time(session_update.get('end_time')) if not (start_utc.isoformat() == session_update_start and end_utc.isoformat() == session_update_end): self._recorder_api.updateRecordingTime( session.Id, session_update_start, session_update_end) access = self._access_api.getSessionAccessDetails(session.Id) if access.IsPublic != session_update.get('is_public'): self._access_api.updateSessionIsPublic( session.Id, session_update.get('is_public')) if session.IsBroadcast != session_update.get('is_broadcast'): self._session_api.updateSessionIsBroadcast( session.Id, session_update.get('is_broadcast')) folder_name = session_update.get('folder_name') if session.FolderName != folder_name: self._session_api.moveSessions( [session.Id], session_update.get('folder_id')) messages = [] creators = session_update.get('folder_creators') if creators and type(creators) is list: messages = self._sync_creators( session_update.get('folder_id'), creators) self._audit_log.info('%s modified %s for %s from %s to %s in %s' % ( request.user, session_update.get('external_id'), session_update.get('uwnetid'), session_update.get('start_time'), session_update.get('end_time'), session_update.get('folder_name'))) return self.json_response({ 'recording_id': session.Id, 'messages': messages }) except InvalidParamException as ex: return self.error_response(400, "%s" % ex) except Exception as ex: return self.error_response(500, "Unable to save session: %s" % ex) def DELETE(self, request, **kwargs): try: session_id = self._valid_recorder_id(kwargs.get('session_id')) # do not permit param tampering key = course_event_key(request.GET.get('uwnetid', ''), request.GET.get('name', ''), request.GET.get('eid', ''), request.GET.get('rid', '')) if key != request.GET.get("key", None): raise InvalidParamException('Invalid Client Key') self._session_api.deleteSessions([session_id]) self._audit_log.info('%s deleted session %s' % (request.user, session_id)) return self.json_response({ 'deleted_recording_id': session_id }) except InvalidParamException as err: return self.error_response(400, "Invalid Parameter: %s" % err) def _valid_folder(self, name, external_id): try: folder_id = Validation().panopto_id(external_id) return folder_id except InvalidParamException: pass try: if external_id and len(external_id): folders = self._session_api.getAllFoldersByExternalId( [external_id]) if folders and len(folders) == 1 and len(folders[0]): return folders[0][0].Id folders = self._session_api.getFoldersList(search_query=name) if folders and len(folders): for folder in folders: if folder.Name == name: folder_id = folder.Id if external_id and len(external_id): self._session_api.updateFolderExternalId( folder_id, external_id) return folder_id new_folder = self._session_api.addFolder(name) if not new_folder: raise InvalidParamException('Cannot add folder: %s' % name) new_folder_id = new_folder.Id if external_id and len(external_id): self._session_api.updateFolderExternalId( new_folder_id, external_id) return new_folder_id except Exception as ex: raise InvalidParamException('Cannot add folder: %s' % ex) def _validate_session(self, request_body): session = {} data = json.loads(request_body) session['recording_id'] = data.get("recording_id", "") session['uwnetid'] = data.get("uwnetid", "") session['name'] = self._valid_recording_name(data.get("name", "").strip()) session['external_id'] = self._valid_external_id( data.get("external_id", "").strip()) session['recorder_id'] = self._valid_recorder_id( data.get("recorder_id", "").strip()) session['folder_external_id'] = data.get( "folder_external_id", "").strip() session['session_id'] = data.get("session_id", "").strip() if len(session['session_id']): self._valid_external_id(session['session_id']) # do not permit param tamperings key = course_event_key(session['uwnetid'], session['name'], session['external_id'], session['recorder_id']) if key != data.get("key", ''): raise InvalidParamException('Invalid Client Key') session['is_broadcast'] = self._valid_boolean(data.get("is_broadcast", False)) session['is_public'] = self._valid_boolean(data.get("is_public", False)) session['start_time'] = self._valid_time(data.get("start_time", "").strip()) session['end_time'] = self._valid_time(data.get("end_time", "").strip()) session['folder_name'] = data.get("folder_name", "").strip() session['folder_id'] = self._valid_folder(session['folder_name'], session['folder_external_id']) session['folder_creators'] = data.get("creators", None) return session def _valid_external_id(self, external_id): if external_id and len(external_id): return external_id raise InvalidParamException('bad external_id') def _valid_recorder_id(self, recorder_id): if (recorder_id): return Validation().panopto_id(recorder_id) raise InvalidParamException('missing recorder id') def _valid_recording_name(self, name): if name and len(name): return name raise InvalidParamException('bad recording name') def _valid_boolean(self, is_broadcast): if not (is_broadcast is None or type(is_broadcast) == bool): raise InvalidParamException('bad broadcast flag') return is_broadcast def _valid_time(self, time): if time and len(time): return time raise InvalidParamException('bad time value') def _sync_creators(self, folder_id, folder_creators): messages = [] new_creator_ids = [] deleted_creator_ids = [] current_creators = get_panopto_folder_creators(folder_id) for creator in folder_creators: if creator not in current_creators: try: new_creator_ids.append(self._get_panopto_user_id(creator)) except PanoptoUserException as ex: messages.append('Invalid UWNetId %s' % creator) for creator in current_creators: if creator not in folder_creators: try: deleted_creator_ids.append(self._get_panopto_user_id(creator)) except PanoptoUserException as ex: messages.append('Invalid UWNetId %s' % creator) if len(new_creator_ids): try: self._access_api.grantUsersAccessToFolder( folder_id, new_creator_ids, 'Creator') except PanoptoAPIException as ex: match = re.match(r'.*Server raised fault: \'(.+)\'$', str(ex)) messages.append('%s: %s' % (creator, match.group(1) if match else str(ex))) if len(deleted_creator_ids): try: self._access_api.revokeUsersAccessFromFolder( folder_id, deleted_creator_ids, 'Creator') except PanoptoAPIException as ex: match = re.match(r'.*Server raised fault: \'(.+)\'$', str(ex)) messages.append('%s: %s' % (creator, match.group(1) if match else str(ex))) return messages def _get_panopto_user_id(self, netid): key = "%s\%s" % (settings.PANOPTO_API_APP_ID, netid) user = self._user_api.getUserByKey(key) if not user or user['UserId'] == '00000000-0000-0000-0000-000000000000': raise PanoptoUserException('Unprovisioned UWNetId: %s' % (netid)) return user['UserId']
def __init__(self): self._session = SessionManagement() self._access = AccessManagement() self._user = UserManagement()
def get_sessions_by_external_ids(external_ids): api = SessionManagement() sessions = api.getSessionsByExternalId(external_ids) return sessions.Session if (sessions and 'Session' in sessions and len(sessions.Session)) else None
def get_sessions_by_session_ids(session_ids): api = SessionManagement() sessions = api.getSessionsById(session_ids) return sessions.Session if (sessions and 'Session' in sessions and len(sessions.Session)) else None
class Command(BaseCommand): help = "Matchrecording session dates to SWS meeting times" option_list = BaseCommand.option_list + ( make_option('--commit', dest='commit', action="store_true", default=False, help='Update Panopto recording with SWS meeting time'), make_option('--stdin', dest='stdin', action="store_true", default=False, help='get Panopto session external ids on standard input'), ) def handle(self, *args, **options): self._commit = options['commit'] self._courses = {} self._session = SessionManagement() self._recorder = RemoteRecorderManagement() if options['stdin']: for line in sys.stdin: self._process_course(line.rstrip('\n')) else: for session in args: self._process_course(session) def _process_course(self, session_id): # 2015-spring-PSYCH-202-A-2015-06-04 course = re.match(r'^(20[0-9]{2})-(winter|spring|summer|autumn)' r'-([A-Z ]+)-([0-9]{3})-([A-Z][A-Z0-9]*)-2*', session_id) if course: label = "%s,%s,%s,%s/%s" % ( course.group(1), course.group(2), course.group(3), course.group(4), course.group(5)) if label not in self._courses: now = datetime.datetime.now( tz.tzlocal()).replace(second=0, microsecond=0) section = get_section_by_label( label, include_instructor_not_on_time_schedule=False) (start, end) = self._lecture_times(section) self._courses[label] = { 'start': start.split(':'), 'end': end.split(':') } offered = self._courses[label] else: print >> sys.stderr, "unrecognized session id: %s" % session_id return pan_session = self._session.getSessionsByExternalId([session_id]) if 'Session' in pan_session and len(pan_session.Session) == 1: # broken-ass suds. fsuds = re.match(r'.*\<a\:StartTime\>([^<]+)\<\/a\:StartTime\>.*', self._session._api.last_received().plain()) if not fsuds: Exception('Untrustable time') pan_start = parser.parse(fsuds.group(1)) pan_start_local = pan_start.astimezone(tz.tzlocal()) sws_start_local = pan_start_local.replace( hour=int(offered['start'][0]), minute=int(offered['start'][1])) sws_end_local = pan_start_local.replace( hour=int(offered['end'][0]), minute=int(offered['end'][1])) schedule_delta = sws_start_local - pan_start_local duration_delta = (sws_end_local - sws_start_local).seconds - int( pan_session.Session[0].Duration) if schedule_delta or duration_delta: pan_start = (pan_start_local + schedule_delta).astimezone(tz.tzutc()) duration = pan_session.Session[0].Duration if duration_delta: duration += duration_delta pan_end = pan_start + datetime.timedelta(0, duration) adjustment = [session_id, '(%s)' % pan_session.Session[0].Id, '' if self._commit else 'WOULD', 'RESCHEDULE', fsuds.group(1), 'TO', pan_start.isoformat(), ':'] if schedule_delta.days < 0: adjustment.append("(-%s shift)" % (datetime.timedelta() - schedule_delta)) else: adjustment.append("(%s shift)" % schedule_delta) if duration_delta: adjustment.append('AND DURATION') adjustment.append("%s" % duration_delta) adjustment.append('seconds') print >> sys.stderr, ' '.join(adjustment) if self._commit: result = self._recorder.updateRecordingTime( pan_session.Session[0].Id, pan_start.isoformat(), pan_end.isoformat()) if not result: print >> sys.stderr, "FAIL: null return value" elif result.ConflictsExist: print >> sys.stderr, "CONFLICT: %s" % ( result.ConflictingSessions[0][0].SessionName) else: print >> sys.stderr, "UPDATED %s" % ( result.SessionIDs[0][0]) else: print >> sys.stderr, "%s: UNCHANGED" % (session_id) else: print >> sys.stderr, "unrecognized session id: %s" % session_id def _lecture_times(self, section): for meeting in section.meetings: if (meeting.meeting_type in ['lecture', 'quiz', 'seminar'] and meeting.start_time and meeting.end_time): return meeting.start_time, meeting.end_time Exception("no lecture times set")