Skip to content

Commit

Permalink
folder mgmt with context, push/post session requires valid folder id
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeseibel committed Jun 14, 2024
1 parent d1e6259 commit d35a183
Show file tree
Hide file tree
Showing 24 changed files with 132 additions and 116 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
ENV: localdev
PORT: 8000
AUTH: SAML_MOCK BLTI_DEV
PANOPTO_API_APP_ID: UWNetid
LTI_DEVELOP_APP: scheduler/course
restart: always
container_name: app-scheduler
Expand Down
5 changes: 3 additions & 2 deletions docker/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@
'is_desktop': True,
}

PANOPTO_API_USER = os.getenv('PANOPTO_API_USER', '')
PANOPTO_API_APP_ID = os.getenv('PANOPTO_API_APP_ID', '')

if os.getenv("ENV", "localdev") == "localdev":
LTI_DEVELOP_APP = os.getenv("LTI_DEVELOP_APP", '')
DEBUG = True
else:
PANOPTO_API_USER = os.getenv('PANOPTO_API_USER')
PANOPTO_API_APP_ID = os.getenv('PANOPTO_API_APP_ID')
PANOPTO_API_TOKEN = os.getenv('PANOPTO_API_TOKEN')
PANOPTO_SERVER = os.getenv('PANOPTO_SERVER')
DEBUG = (os.getenv("ENV", "UNSET") == "dev")
Expand Down
7 changes: 0 additions & 7 deletions scheduler/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ def reservation_uid(self):
"""
pass

@abstractmethod
def canvas_sis_id(self):
"""Canvas SIS_ID assigned to a Canvas course by Student
system provisioning process
"""
pass

@abstractmethod
def panopto_course_external_id(self, start_datetime):
"""Opaque string unique to each course offering."""
Expand Down
11 changes: 10 additions & 1 deletion scheduler/dao/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
from uw_canvas.courses import Courses as CanvasCourses


canvas_api = CanvasCourses()

def get_course_by_sis_id(sis_id):
return CanvasCourses().get_course_by_sis_id(sis_id)
return canvas_api.get_course_by_sis_id(sis_id)


def get_course_by_canvas_id(course_id):
if canvas_api.valid_course_id(course_id):
return canvas_api.get_course(course_id)

raise Exception("Invalid course id: {}".format(course_id))
2 changes: 0 additions & 2 deletions scheduler/dao/panopto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
# Copyright 2023 UW-IT, University of Washington
# SPDX-License-Identifier: Apache-2.0
13 changes: 13 additions & 0 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 scheduler.utils import panopto_app_id


session_api = SessionManagement()
Expand Down Expand Up @@ -39,6 +40,14 @@ def get_folders_list(*args, **kwargs):
return session_api.getFoldersList(*args, **kwargs)


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

return session_api.getAllFoldersWithExternalContextByExternalId(
external_ids, providers)


def get_all_folders_by_external_id(external_ids):
return session_api.getAllFoldersByExternalId(external_ids)

Expand All @@ -47,6 +56,10 @@ def add_folder(name):
return session_api.addFolder(name)


def provision_external_course(course_name, external_id):
return session_api.provisionExternalCourse(course_name, external_id)


def update_folder_external_id_with_provider(folder_id, external_id, provider):
return session_api.updateFolderExternalIdWithProvider(
folder_id, external_id, provider)
8 changes: 7 additions & 1 deletion scheduler/dao/sws.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@
from uw_sws.section import get_section_by_label, get_section_by_url


def sws_course_label(course):
return "{},{},{},{}/{}".format(course.year, course.quarter,
course.curriculum, course.number,
course.section)


def get_sws_section_for_course(course):
return get_section_by_label(
course.sws_course_label(),
sws_course_label(course),
include_instructor_not_on_time_schedule=False)


Expand Down
4 changes: 4 additions & 0 deletions scheduler/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ class ValidationException(Exception):

class PanoptoUserException(Exception):
pass


class PanoptoFolderDoesNotExist(Exception):
pass
84 changes: 61 additions & 23 deletions scheduler/org/uw/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
from scheduler.models import Curriculum
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)
from scheduler.dao.sws import (
get_sws_section_for_course, get_sws_section_by_url)
from scheduler.exceptions import (
MissingParamException, InvalidParamException)
MissingParamException, InvalidParamException, PanoptoFolderDoesNotExist)

from restclients_core.exceptions import DataFailureException
from dateutil import parser
from nameparser import HumanName
Expand All @@ -26,6 +29,7 @@ class Course(BaseCourse):
number = ""
section = ""
external_id = ""
canvas_course_id = None
UW_CAMPUS = ['seattle', 'bothell', 'tacoma']
UW_TERMS = ['spring', 'summer', 'autumn', 'winter']

Expand Down Expand Up @@ -81,11 +85,6 @@ def reservation_uid(self):
self.curriculum.replace(' ', ''),
self.number, self.section)

def sws_course_label(self):
return "{},{},{},{}/{}".format(self.year, self.quarter,
self.curriculum, self.number,
self.section)

def canvas_sis_id(self):
return "{}".format('-'.join([
self.year, self.quarter, self.curriculum,
Expand All @@ -99,28 +98,67 @@ def panopto_course_external_id(self, start_datetime):
self.year, self.quarter, self.curriculum,
self.number, self.section, start_date)

def panopto_course_folder(self, title):
quarter_initial = self.quarter[0:1].upper()
quarter_lower = self.quarter[1:]
folder_prefix = "{}{} {} - ".format(
quarter_initial, quarter_lower, self.year)
def canvas_course(self):
if self.canvas_course_id is None:
try:
return get_course_by_sis_id(self.canvas_sis_id())
except DataFailureException as ex:
raise DataFailureException(
ex.url, 404 if ex.status == 404 else 424,
"course_id fetch: {}".format(ex))

return self.canvas_course_id

def panopto_course_folder(self):
canvas_course = self.canvas_course()
try:
# folder id needs to match canvas course id
canvas_course = get_course_by_sis_id(self.canvas_sis_id())
external_id = str(canvas_course.course_id)
folder = canvas_course.name
except Exception as ex:
logger.exception(ex)
external_id = None
folder = "{} {} {} {}{} {}: {}".format(
self.curriculum, self.number, self.section, quarter_initial,
quarter_lower[:1], str(self.year)[2:], title.title())
panopto_folder = self._panopto_folder_from_course_id(
str(canvas_course.course_id))
except PanoptoFolderDoesNotExist:
panopto_folder = self._create_panopto_folder(canvas_course)

return {
'name': "{}{}".format(folder_prefix, folder),
'external_id': external_id
'name': panopto_folder.Name,
'id': panopto_folder.Id
}

def _panopto_folder_from_course_id(self, course_id):
if course_id:
folders = get_all_folders_with_external_context_list([course_id])
if folders:
if len(folders) == 1:
if len(folders[0]) == 1:
return folders[0][0]
else:
self._bad_folder_response(len(folders[0]), course_id)
else:
self._bad_folder_response(len(folders), course_id)

raise PanoptoFolderDoesNotExist()

def _bad_folder_response(self, folder_count, course_id):
message = 'Unexpected folder count {} for {}'.format(
folder_count, course_id)
logger.error(message)
raise DataFailureException("", 424, message)

def _create_panopto_folder(self, canvas_course):
try:
folder = provision_external_course(
self.panopto_folder_name(canvas_course.name),
str(canvas_course.course_id))
if folder:
return folder

raise DataFailureException('', 424, 'folder provision failed')
except DataFailureException as ex:
raise DataFailureException(
ex.url, 424, "folder provision: {}".format(ex))

def panopto_folder_name(self, course_name):
return "{} {} - ".format(
self.quarter.capitalize(), self.year, course_name)

def panopto_course_session(self, start_datetime):
name = "{} {} {} - {}".format(
self.curriculum, self.number, self.section,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><ScheduleRecordingResponse xmlns="http://tempuri.org/"><ScheduleRecordingResult xmlns:a="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V42.Soap" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:ConflictingSessions/><a:ConflictsExist>false</a:ConflictsExist><a:SessionIDs xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><b:guid>23012340-0000-0000-0000-ab000001234d</b:guid></a:SessionIDs></ScheduleRecordingResult></ScheduleRecordingResponse></s:Body></s:Envelope>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetAllFoldersByExternalIdResponse xmlns="http://tempuri.org/"><GetAllFoldersByExternalIdResult xmlns:a="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:Folder><a:AllowPublicNotes>true</a:AllowPublicNotes><a:AllowSessionDownload>false</a:AllowSessionDownload><a:AudioPodcastITunesUrl>itpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&amp;type=mp3</a:AudioPodcastITunesUrl><a:AudioRssUrl>http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&amp;type=mp3</a:AudioRssUrl><a:ChildFolders xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:DeliveriesHaveSpecifiedOrder>false</a:DeliveriesHaveSpecifiedOrder><a:Description i:nil="true"/><a:EmbedUploaderUrl>https://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444e-0000-0000-0000-ab71234c452d</a:EmbedUploaderUrl><a:EmbedUrl>https://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&amp;folderID=c9123444e-0000-0000-0000-ab71234c452d</a:EmbedUrl><a:EnablePodcast>true</a:EnablePodcast><a:Id>c9123444e-0000-0000-0000-ab71234c452d</a:Id><a:IsPublic>false</a:IsPublic><a:ListUrl>https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444e-0000-0000-0000-ab71234c452d</a:ListUrl><a:Name>Autumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychology</a:Name><a:ParentFolder i:nil="true"/><a:Presenters xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:Sessions xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:SettingsUrl>https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&amp;folderID=c9123444e-0000-0000-0000-ab71234c452d</a:SettingsUrl><a:VideoPodcastITunesUrl>itpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&amp;type=mp4</a:VideoPodcastITunesUrl><a:VideoRssUrl>http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&amp;type=mp4</a:VideoRssUrl><a:ExternalId i:nil="true"/></a:Folder></GetAllFoldersByExternalIdResult></GetAllFoldersByExternalIdResponse></s:Body></s:Envelope>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetAllFoldersByExternalIdResponse xmlns="http://tempuri.org/"><GetAllFoldersByExternalIdResult xmlns:a="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:Folder><a:AllowPublicNotes>true</a:AllowPublicNotes><a:AllowSessionDownload>false</a:AllowSessionDownload><a:AudioPodcastITunesUrl>itpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&amp;type=mp3</a:AudioPodcastITunesUrl><a:AudioRssUrl>http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&amp;type=mp3</a:AudioRssUrl><a:ChildFolders xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:DeliveriesHaveSpecifiedOrder>false</a:DeliveriesHaveSpecifiedOrder><a:Description i:nil="true"/><a:EmbedUploaderUrl>https://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444-0000-0000-0000-ab71234c452d</a:EmbedUploaderUrl><a:EmbedUrl>https://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&amp;folderID=c9123444-0000-0000-0000-ab71234c452d</a:EmbedUrl><a:EnablePodcast>true</a:EnablePodcast><a:Id>c9123444-0000-0000-0000-ab71234c452d</a:Id><a:IsPublic>false</a:IsPublic><a:ListUrl>https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444-0000-0000-0000-ab71234c452d</a:ListUrl><a:Name>Autumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychology</a:Name><a:ParentFolder i:nil="true"/><a:Presenters xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:Sessions xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><a:SettingsUrl>https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&amp;folderID=c9123444-0000-0000-0000-ab71234c452d</a:SettingsUrl><a:VideoPodcastITunesUrl>itpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&amp;type=mp4</a:VideoPodcastITunesUrl><a:VideoRssUrl>http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&amp;type=mp4</a:VideoRssUrl><a:ExternalId i:nil="true"/></a:Folder></GetAllFoldersByExternalIdResult></GetAllFoldersByExternalIdResponse></s:Body></s:Envelope>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><GetAllFoldersWithExternalContextByExternalIdResponse xmlns="http://tempuri.org/"><GetAllFoldersWithExternalContextByExternalIdResult xmlns:a="http://schemas.datacontract.org/2004/07/Panopto.Data.Server.Services.PublicAPI.V46" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><a:FolderWithExternalContext><AllowPublicNotes xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">true</AllowPublicNotes><AllowSessionDownload xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">false</AllowSessionDownload><AudioPodcastITunesUrl i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><AudioRssUrl i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><ChildFolders xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><DeliveriesHaveSpecifiedOrder xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">false</DeliveriesHaveSpecifiedOrder><Description i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><EmbedUploaderUrl xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">https://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444-0000-0000-0000-ab71234c452d</EmbedUploaderUrl><EmbedUrl xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">https://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&amp;folderID=c9123444-0000-0000-0000-ab71234c452d</EmbedUrl><EnablePodcast xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">true</EnablePodcast><Id xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">696c3acb-71c2-4071-9668-b18f00eecf3b</Id><IsPublic xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">false</IsPublic><ListUrl xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444-0000-0000-0000-ab71234c452d</ListUrl><Name xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">Autumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychology</Name><ParentFolder i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><Presenters xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><Sessions xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap" xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/><SettingsUrl xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap">https://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&amp;folderID=c9123444-0000-0000-0000-ab71234c452d</SettingsUrl><VideoPodcastITunesUrl i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><VideoRssUrl i:nil="true" xmlns="http://schemas.datacontract.org/2004/07/Panopto.Server.Services.PublicAPI.V46.Soap"/><a:ExternalIds xmlns:b="http://schemas.microsoft.com/2003/10/Serialization/Arrays"><b:string>UWNetid-uw-test\\1730776</b:string></a:ExternalIds></a:FolderWithExternalContext></GetAllFoldersWithExternalContextByExternalIdResult></GetAllFoldersWithExternalContextByExternalIdResponse></s:Body></s:Envelope>
Loading

0 comments on commit d35a183

Please sign in to comment.