Skip to content

Commit

Permalink
folder hierarchy for event folder
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeseibel committed Jun 25, 2024
1 parent 5935a22 commit 8d460bc
Show file tree
Hide file tree
Showing 9 changed files with 244 additions and 132 deletions.
22 changes: 19 additions & 3 deletions scheduler/dao/panopto/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0

from panopto_client.session import SessionManagement
from panopto_client import PanoptoAPIException
from scheduler.utils import panopto_app_id


Expand Down Expand Up @@ -36,11 +37,26 @@ def delete_sessions(session_ids):
return session_api.deleteSessions(session_ids)


def get_folders_by_id(folder_ids):
try:
return session_api.getFoldersById(folder_ids)
except PanoptoAPIException as ex:
if str(ex).find("was not found") > 0:
raise PanoptoFolderDoesNotExist(ex)
else:
raise ex


def get_folders_with_external_context_list(*args, **kwargs):
return session_api.getFoldersWithExternalContextList(*args, **kwargs)


def get_folders_list(*args, **kwargs):
return session_api.getFoldersList(*args, **kwargs)


def get_all_folders_with_external_context_list(external_ids, providers=None):
def get_all_folders_with_external_context_by_external_id(
external_ids, providers=None):
if not providers:
providers = [panopto_app_id()]

Expand All @@ -52,8 +68,8 @@ def get_all_folders_by_external_id(external_ids):
return session_api.getAllFoldersByExternalId(external_ids)


def add_folder(name):
return session_api.addFolder(name)
def add_folder(name, parent_id=None, is_public=False):
return session_api.addFolder(name, parent_id, is_public)


def provision_external_course(course_name, external_id):
Expand Down
7 changes: 7 additions & 0 deletions scheduler/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Reservation(models.Model):
is_course = models.BooleanField(null=True)
start_datetime = models.DateTimeField()
end_datetime = models.DateTimeField()
reservation_id = models.IntegerField(default=0)

def contact_info(self):
return {
Expand All @@ -29,6 +30,12 @@ def contact_info(self):
'email': self.contact_email
}

def default_folder_name(self):
return "{}".format(self.event_name)

def default_session_external_id(self):
return "res_{}".format(self.reservation_id)


class Curriculum(models.Model):
""" Maps curricula to campus code
Expand Down
7 changes: 5 additions & 2 deletions scheduler/org/uw/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from scheduler.utils import local_ymd_from_utc_date_string
from scheduler.dao.canvas import get_course_by_sis_id
from scheduler.dao.panopto.sessions import (
get_all_folders_with_external_context_list, provision_external_course)
get_all_folders_with_external_context_by_external_id,
provision_external_course)
from scheduler.dao.sws import (
get_sws_section_for_course, get_sws_section_by_url)
from scheduler.exceptions import (
Expand Down Expand Up @@ -125,7 +126,9 @@ def panopto_course_folder(self):

def _panopto_folder_from_course_id(self, course_id):
if course_id:
folders = get_all_folders_with_external_context_list([course_id])
folders = \
get_all_folders_with_external_context_by_external_id(
[course_id])
if folders:
if len(folders) == 1:
if len(folders[0]) == 1:
Expand Down
55 changes: 30 additions & 25 deletions scheduler/panopto/folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
from scheduler.utils import local_ymd_from_utc_date_string
from scheduler.dao.panopto.access import get_folder_access_details
from scheduler.dao.panopto.user import get_users_from_guids
from scheduler.dao.panopto.sessions import get_folders_list
from scheduler.dao.panopto.sessions import (
get_folders_with_external_context_list)
from hashlib import sha1
import logging
import re


logger = logging.getLogger(__name__)


def get_panopto_folder_creators(folder_id):
creators = []
folder_access = get_folder_access_details(folder_id)
Expand All @@ -30,32 +35,32 @@ def get_panopto_folder_creators(folder_id):
return creators


def set_panopto_generic_folder(event):
id_string = "{} - {}".format(event['name'], event['space']['id'])
folder_name = event['name']
folder_external_id = panopto_generic_external_id(id_string)
creators = []
def panopto_folder_id(event_folder):
try:
if event_folder['id']:
return event_folder['id']

folders = get_folders_list(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}
folder_name = event_folder['name']
if not folder_name:
return None

except KeyError:
return None

creators = []

def set_panopto_generic_session(event):
name = "{} - {}".format(
event['name'],
local_ymd_from_utc_date_string(event['event']['start']))
id_string = "{} - {}".format(name, event['space']['id'])
event['recording']['name'] = name
event['recording']['external_id'] = panopto_generic_external_id(id_string)
event['recording']['is_public'] = False
folders = get_folders_with_external_context_list(
search_query=folder_name)

if folders:
if len(folders) == 1:
return folders[0].Id
else:
logger.info(
"panopto_folder_id: many folders named {}".format(
folder_name))
for f in folders:
logger.info("folder name '{}', Id: {}, ExternalId".format(
f.Name, f.Id, f.ExternalId))

def panopto_generic_external_id(id_string):
return sha1(id_string.encode('utf-8')).hexdigest().upper()
return None
3 changes: 2 additions & 1 deletion scheduler/reservations/r25.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ def _reservation_from_r25(self, r25):
is_instruction=(self.course_type(r25.profile_name)
in self.instruction_profiles),
start_datetime=r25.start_datetime,
end_datetime=r25.end_datetime)
end_datetime=r25.end_datetime,
reservation_id=r25.reservation_id)

@property
def instruction_profiles(self):
Expand Down
89 changes: 46 additions & 43 deletions scheduler/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from scheduler.reservations import Reservations
from scheduler.utils import schedule_key
from scheduler.panopto.folder import (
get_panopto_folder_creators, set_panopto_generic_folder,
set_panopto_generic_session)
get_panopto_folder_creators, panopto_folder_id)
from scheduler.dao.panopto.recorder import get_recorder_details
from scheduler.dao.panopto.sessions import (
get_sessions_by_external_ids, get_sessions_by_session_ids)
Expand Down Expand Up @@ -68,8 +67,10 @@ def course_recording_sessions(course, event):

event_session['joint'] = joint if len(joint) else None

# override default folder
event_session['recording']['folder'] = folder

# overwrite default external_id with course session external_id
name, external_id = course.panopto_course_session(rsv.start_datetime)
event_session['recording']['name'] = name
event_session['recording']['external_id'] = external_id
Expand All @@ -84,6 +85,7 @@ def course_recording_sessions(course, event):
event_session['schedulable'] = True if (
folder['id'] and event_session['space']['id']) else False

# override default contact info
event_session['contact'] = {}

event_sessions.append(event_session)
Expand Down Expand Up @@ -147,72 +149,72 @@ def space_events_and_recordings(params):

if event_session:
event_sessions.append(event_session)
event_external_ids.append(
event_session['recording']['external_id'])


"""
# overlay session data
for event_session in list(event_sessions):
set_panopto_generic_folder(event_session)
set_panopto_generic_session(event_session)
event_external_ids.append(
event_session['recording']['external_id'])
event_session['recording']['folder']['id'] = panopto_folder_id(
event_session['recording']['folder'])
"""

mash_in_panopto_sessions(event_sessions, event_external_ids, recorders)

return event_sessions


def event_session_from_reservation(r):
session = {
"""
return session data from r25 Reservation model
"""
start_dt = parser.parse(r.start_datetime)
end_dt = parser.parse(r.end_datetime) + \
datetime.timedelta(seconds=int(
getattr(settings, 'DEFAULT_RECORDING_TIME_FUDGE', 120)))
start_utc = start_dt.astimezone(tz.tzutc())
end_utc = end_dt.astimezone(tz.tzutc())

if not r.space_id:
logger.error(
"Meeting for {} on {} has no space reservation ".format(
r.event_name, r.start_datetime))

return {
'profile': r.profile_name,
'name': r.event_name,
'schedulable': True,
'event': {
'start': start_utc.isoformat(),
'end': end_utc.isoformat(),
},
'space': {
'id': None,
'name': None,
'formal_name': None
'id': getattr(r, 'space_id'),
'name': getattr(r, 'space_name'),
'formal_name': getattr(r, 'space_formal_name'),
},
'recording': {
'name': None,
'id': None,
'external_id': None,
'external_id': r.default_session_external_id(),
'recorder_id': None,
'start': None,
'end': None,
'start': start_utc.isoformat(),
'end': end_utc.isoformat(),
'folder': {
'name': None,
'name': r.default_folder_name(),
'id': None,
'external_id': None
}
},
'contact': r.contact_info()
}

if r.space_id:
session['space']['id'] = r.space_id
session['space']['name'] = r.space_name
session['space']['formal_name'] = r.space_formal_name
else:
logger.error(
"Meeting for {} on {} has no space reservation ".format(
r.event_name, r.start_datetime))

start_dt = parser.parse(r.start_datetime)
end_dt = parser.parse(r.end_datetime) + \
datetime.timedelta(seconds=int(
getattr(settings, 'DEFAULT_RECORDING_TIME_FUDGE', 120)))
start_utc = start_dt.astimezone(tz.tzutc())
end_utc = end_dt.astimezone(tz.tzutc())

session['event'] = {}
session['event']['start'] = start_utc.isoformat()
session['event']['end'] = end_utc.isoformat()

session['recording']['start'] = start_utc.isoformat()
session['recording']['end'] = end_utc.isoformat()

return session


def event_session_from_scheduled_recording(s):
"""
flesh out session data from scheduled event
"""
start_utc = s.StartTime.astimezone(pytz.utc)
end_utc = start_utc + datetime.timedelta(seconds=int(s.Duration))

Expand All @@ -237,9 +239,8 @@ def event_session_from_scheduled_recording(s):
'id': s.FolderId,
'external_id': None,
},
'is_broadcast': s.IsBroadcast
# property below is added conditionally for events
# 'is_public': False,
'is_broadcast': s.IsBroadcast,
'is_public': False,
},
'contact': {
'name': '',
Expand All @@ -261,7 +262,9 @@ def event_session_from_scheduled_recording(s):


def mash_in_panopto_sessions(event_sessions, session_external_ids, recorders):
# mash in panopto recorder schedule
"""
overlay event details with any matching scheduled recordings
"""
session_access = {}
if len(session_external_ids):
sessions = get_sessions_by_external_ids(session_external_ids)
Expand Down
13 changes: 9 additions & 4 deletions scheduler/templates/scheduler/events.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ <h2>Lecture Capture Scheduler</h2>
<h3>Find an Event</h3>

<form role="form" class="form-inline event-search">

<div class="input-group input-append date" id="date-picker">
<input id="calendar" name="calendar" type="text" class="form-control input-date" disabled />
<span class="input-group-addon add-on"><i class="fa fa-calendar"></i></span>
Expand Down Expand Up @@ -145,26 +144,32 @@ <h4><b>Recording Settings</b></h4>
</div>
</div>
<div class="container">
<h4><b>Save Recording To <span class="folder-exists {{#if folder_id}}{{else}}hidden{{/if}}">Existing</span><span style="display: none;">New</span> Folder &hellip;</b></h4>
<h4><b>Save Recording</b></h4>
<div class="row">
<div class="col-md-1 text-right">
<i class="fa fa-folder"></i>
<!-- <i class="fa fa-folder"></i> -->To Folder:>
</div>
<div class="col-md-11">
<div class="field-editor foldername">
<div class="field">
<input class="form-control" value="{{folder_name}}"><span class="open-folder-picker field-search" title="Click to search folders"><i class="fa fa-search"></i></span>
<!--
<a href="javascript:void(0);" title="Click to edit folder name">{{folder_name}}</a>
-->
</div>
<div class="form-group hidden">
<input class="form-control" value=""><span class="open-folder-picker field-search" title="Click to search folders"><i class="fa fa-search"></i></span>
<!-- <input class="form-control" value=""><span class="open-folder-picker field-search" title="Click to search folders"><i class="fa fa-search"></i></span>
-->
<div class="folder-picker hidden">
<!--
<div class="prompt">
Search by folder name<button type="button" class="close pull-right">x</button>
</div>
<div class="search">
<input type="search" class="form-control"><span class="clear-field hidden"><i class="fa fa-times-circle"></i></span>
</div>
<hr>
-->
<div class="result">
</div>
</div>
Expand Down
Loading

0 comments on commit 8d460bc

Please sign in to comment.