diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 190157c..7f6d31d 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -68,10 +68,10 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.10' @@ -82,10 +82,10 @@ jobs: exclude_paths: 'migrations' - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-$(echo ${{ hashFiles('Dockerfile') }} | head -c 16) @@ -93,7 +93,7 @@ jobs: ${{ runner.os }}-buildx- - name: Build App Image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: target: app-container tags: ${{ needs.context.outputs.image_tag }} @@ -103,7 +103,7 @@ jobs: cache-to: type=local,dest=/tmp/.buildx-cache - name: Build Test Image - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: target: app-test-container tags: app-test-container @@ -149,11 +149,11 @@ jobs: steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Deployment Pipeline if: >- - contains(fromJSON('["main", "develop"]'), + contains(fromJSON('["main", "develop"'), needs.context.outputs.git_repo_branch) uses: uw-it-aca/actions/cicd-deploy@main with: diff --git a/Dockerfile b/Dockerfile index d49c0a5..f47f1bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,7 @@ RUN /app/bin/pip install psycopg2 RUN . /app/bin/activate && pip install nodeenv && nodeenv -p && \ npm install -g npm && ./bin/npm install less -g -RUN . /app/bin/activate && python manage.py collectstatic --noinput &&\ - python manage.py compress -f +RUN . /app/bin/activate && python manage.py compress -f && python manage.py collectstatic --noinput FROM us-docker.pkg.dev/uwit-mci-axdd/containers/django-test-container:${DJANGO_CONTAINER_VERSION} as app-test-container diff --git a/docker-compose.yml b/docker-compose.yml index 63c485d..426a9ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/docker/settings.py b/docker/settings.py index 8f03c7d..6a123b6 100644 --- a/docker/settings.py +++ b/docker/settings.py @@ -34,7 +34,7 @@ MIDDLEWARE += ['userservice.user.UserServiceMiddleware',] -COMPRESS_ENABLED = True +COMPRESS_ENABLED = False if os.getenv("ENV") == "localdev" else True COMPRESS_OFFLINE = True COMPRESS_ROOT = '/static/' @@ -66,6 +66,12 @@ 'scheduler.context_processors.localdev_mode', ] +SCHEDULER_TIMEZONE = "America/Los_Angeles" + +USER_MODULE = 'scheduler.org.uw.user' +COURSES_MODULE = 'scheduler.org.uw.course' +RESERVATIONS_MODULE = 'scheduler.org.uw.reservations' + PANOPTO_ADMIN_GROUP = 'u_acadev_panopto_support' RESTCLIENTS_ADMIN_GROUP = PANOPTO_ADMIN_GROUP USERSERVICE_ADMIN_GROUP = PANOPTO_ADMIN_GROUP @@ -89,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") diff --git a/scheduler/authorization.py b/scheduler/authorization.py index 638890d..5fcf318 100644 --- a/scheduler/authorization.py +++ b/scheduler/authorization.py @@ -3,7 +3,7 @@ from django.conf import settings from uw_saml.utils import is_member_of_group -import re +from scheduler.user import User def userservice_validate(username): @@ -13,8 +13,7 @@ def userservice_validate(username): if username != username.lower(): return "Usernames must be all lowercase" - re_personal_netid = re.compile(r'^[a-z][a-z0-9]{0,7}$', re.I) - if not re_personal_netid.match(username): + if not User().validate_login_id(username): return ("Username not a valid netid (starts with a letter, " "then 0-7 letters or numbers)") diff --git a/scheduler/course.py b/scheduler/course.py new file mode 100644 index 0000000..0641822 --- /dev/null +++ b/scheduler/course.py @@ -0,0 +1,63 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +# +# Course reflects the attributes necessary to correlate a +# specific course offering's meeting schedule with +# Panopto recording sessions, the facilities scheduling system, +# and Canvas LMS. +# + +from abc import ABC, abstractmethod +from scheduler.utils.loader import load_class_from_module_setting + + +COURSES_MODULE_SETTING = 'COURSES_MODULE' + + +class Course(object): + """Indirection class used to load campus-specific Course class""" + def __new__(self, courseId): + return load_class_from_module_setting( + COURSES_MODULE_SETTING, 'Course', courseId) + + +class BaseCourse(ABC): + """Base class definining methods necessary to associate a course + offering with the course meeting location and panopto folder and + scheduled recording sessions + """ + + @abstractmethod + def reservation_uid(self): + """ID used to reference the course offering in the reservation + system. For R25 this is would be the Alien UID unique for + the couse offering. + """ + pass + + @abstractmethod + def panopto_course_external_id(self, start_datetime): + """Opaque string unique to each course offering.""" + pass + + @abstractmethod + def panopto_course_folder(self, title): + """Folder name and folder external_id that must match the values + set by Panopto's Recordings LTI + """ + pass + + @abstractmethod + def panopto_course_session(self, start_datetime): + """Recording session name and external_id unique to a course + offering meeting time. + """ + pass + + @abstractmethod + def get_crosslisted_course(self): + """If the Course is crosslisted (joined), return the Course + class corresponding to the course crosslisted are linked into + """ + return self diff --git a/scheduler/dao/__init__.py b/scheduler/dao/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scheduler/dao/canvas.py b/scheduler/dao/canvas.py new file mode 100644 index 0000000..69883e7 --- /dev/null +++ b/scheduler/dao/canvas.py @@ -0,0 +1,18 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from uw_canvas.courses import Courses as CanvasCourses + + +canvas_api = CanvasCourses() + + +def 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)) diff --git a/scheduler/dao/panopto/__init__.py b/scheduler/dao/panopto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scheduler/dao/panopto/access.py b/scheduler/dao/panopto/access.py new file mode 100644 index 0000000..3002090 --- /dev/null +++ b/scheduler/dao/panopto/access.py @@ -0,0 +1,29 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from panopto_client.access import AccessManagement + + +access_api = AccessManagement() + + +def get_session_access_details(session_id): + return access_api.getSessionAccessDetails(session_id) + + +def get_folder_access_details(folder_id): + return access_api.getFolderAccessDetails(folder_id) + + +def update_session_is_public(session_id, is_public): + return access_api.updateSessionIsPublic(session_id, is_public) + + +def grant_users_access_to_folder(folder_id, creator_ids, access_type): + return access_api.grantUsersAccessToFolder( + folder_id, creator_ids, access_type) + + +def revoke_users_access_from_folder(folder_id, creator_ids, access_type): + return access_api.revokeUsersAccessFromFolder( + folder_id, creator_ids, access_type) diff --git a/scheduler/dao/panopto/recorder.py b/scheduler/dao/panopto/recorder.py new file mode 100644 index 0000000..b30225b --- /dev/null +++ b/scheduler/dao/panopto/recorder.py @@ -0,0 +1,49 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from scheduler.utils.validation import Validation +from panopto_client.remote_recorder import RemoteRecorderManagement +import re + + +recorder_api = RemoteRecorderManagement() + + +def list_recorders(): + return recorder_api.listRecorders() + + +def get_recorder_details(recorder_id): + if re.match(r'^\d+$', recorder_id): + recorders = get_recorder_by_external_id(recorder_id) + else: + Validation().panopto_id(recorder_id) + recorders = get_recorder_by_id(recorder_id) + + if not (recorders and hasattr(recorders, 'RemoteRecorder')): + return None + + return recorders.RemoteRecorder + + +def get_recorder_by_id(recorder_id): + return recorder_api.getRemoteRecordersById(recorder_id) + + +def get_recorder_by_external_id(external_id): + return recorder_api.getRemoteRecordersByExternalId(external_id) + + +def update_recorder_external_id(recorder_id, external_id): + return recorder_api.updateRemoteRecorderExternalId( + recorder_id, external_id) + + +def schedule_recording( + name, folder_id, is_broadcast, start_time, end_time, recorder_id): + return recorder_api.scheduleRecording( + name, folder_id, is_broadcast, start_time, end_time, recorder_id) + + +def update_recording_time(session_id, start_time, end_time): + return recorder_api.updateRecordingTime(session_id, start_time, end_time) diff --git a/scheduler/dao/panopto/sessions.py b/scheduler/dao/panopto/sessions.py new file mode 100644 index 0000000..5b1f992 --- /dev/null +++ b/scheduler/dao/panopto/sessions.py @@ -0,0 +1,82 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from panopto_client.session import SessionManagement +from panopto_client import PanoptoAPIException +from scheduler.utils import panopto_app_id +from scheduler.exceptions import PanoptoFolderDoesNotExist + + +session_api = SessionManagement() + + +def get_sessions_by_external_ids(external_ids): + sessions = session_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): + sessions = session_api.getSessionsById(session_ids) + return sessions.Session if (sessions and 'Session' in sessions and + len(sessions.Session)) else None + + +def update_session_external_id(session_id, external_id): + return session_api.updateSessionExternalId(session_id, external_id) + + +def update_session_is_broadcast(session_id, is_broadcast): + return session_api.updateSessionIsBroadcast(session_id, is_broadcast) + + +def move_sessions(session_ids, folder_id): + return session_api.moveSessions(session_ids, folder_id) + + +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_by_external_id( + 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) + + +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): + 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) diff --git a/scheduler/dao/panopto/user.py b/scheduler/dao/panopto/user.py new file mode 100644 index 0000000..2d69158 --- /dev/null +++ b/scheduler/dao/panopto/user.py @@ -0,0 +1,15 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from panopto_client.user import UserManagement + + +user_api = UserManagement() + + +def get_users_from_guids(guids): + return user_api.getUsers(guids) + + +def get_user_by_key(key): + return user_api.getUserByKey(key) diff --git a/scheduler/dao/r25.py b/scheduler/dao/r25.py new file mode 100644 index 0000000..12a36b0 --- /dev/null +++ b/scheduler/dao/r25.py @@ -0,0 +1,33 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from uw_r25.events import get_event_by_alien_id +from uw_r25.reservations import get_reservations +from uw_r25.spaces import get_space_by_id as r25_space_by_id +from uw_r25.spaces import get_spaces as r25_get_spaces +from scheduler.exceptions import CourseReservationsException + + +def get_event_by_course(course): + """""" + reservation_uid = course.reservation_uid() + event = get_event_by_alien_id(reservation_uid) + + if not event: + raise CourseReservationsException( + "No course events found for {}".format(reservation_uid)) + + return event + + +def get_reservations_by_search_params(search): + """""" + return get_reservations(**search) + + +def get_space_by_id(id): + return r25_space_by_id(id) + + +def get_spaces(*args, **kwargs): + return r25_get_spaces(*args, **kwargs) diff --git a/scheduler/dao/sws.py b/scheduler/dao/sws.py new file mode 100644 index 0000000..0d6432a --- /dev/null +++ b/scheduler/dao/sws.py @@ -0,0 +1,17 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from uw_sws.section import get_section_by_label, get_section_by_url +from uw_sws.models import SWS_SECTION_LABEL + + +def get_sws_section_for_course(course): + label = SWS_SECTION_LABEL.format( + year=course.year, quarter=course.quarter, curr_abbr=course.curriculum, + course_num=course.number, section_id=course.section_id) + return get_section_by_label( + label, include_instructor_not_on_time_schedule=False) + + +def get_sws_section_by_url(url): + return get_section_by_url(url) diff --git a/scheduler/exceptions.py b/scheduler/exceptions.py index ebf1695..b9cda8e 100644 --- a/scheduler/exceptions.py +++ b/scheduler/exceptions.py @@ -15,3 +15,35 @@ class InvalidUser(Exception): class StudentWebServiceUnavailable(Exception): def __str__(self): return _("sws_not_available") + + +class CourseReservationsException(Exception): + pass + + +class RecorderException(Exception): + pass + + +class MissingParamException(Exception): + pass + + +class InvalidParamException(Exception): + pass + + +class ValidationException(Exception): + pass + + +class PanoptoUserException(Exception): + pass + + +class PanoptoFolderDoesNotExist(Exception): + pass + + +class PanoptoFolderSearchTooShort(Exception): + pass diff --git a/scheduler/management/commands/confirm_recordings.py b/scheduler/management/commands/confirm_recordings.py index d405d53..f8072e0 100644 --- a/scheduler/management/commands/confirm_recordings.py +++ b/scheduler/management/commands/confirm_recordings.py @@ -5,14 +5,11 @@ from django.core.management.base import BaseCommand from django.core.mail import EmailMessage from prometheus_client import Gauge -from uw_sws.section import get_section_by_url +from scheduler.course import Course +from scheduler.reservations import Reservations from panopto_client.session import SessionManagement from panopto_client.remote_recorder import RemoteRecorderManagement -from uw_r25.events import get_event_by_alien_id -from scheduler.utils import ( - r25_alien_uid, panopto_course_session, get_sws_section, canvas_course_id) from scheduler.utils.monitor import email_addresses_from_group -from scheduler.utils.validation import Validation import logging @@ -81,48 +78,22 @@ def handle(self, *args, **options): recorder.Id, recorder.Name)) for course_id, recorders in self.sessions().items(): - validation_api = Validation() - course = validation_api.course_id(course_id) - event = get_event_by_alien_id(r25_alien_uid(course)) - - # cross listed? - if len(event.binding_reservations): - joint = [] - - # default to Canvas course that student - # sections are provisioned - section = get_sws_section(course) - if not section.is_withdrawn and len( - section.joint_section_urls): - joint_course_ids = [canvas_course_id(course)] - for url in section.joint_section_urls: - try: - joint_section = get_section_by_url(url) - if not joint_section.is_withdrawn: - joint_course_id = \ - joint_section.canvas_course_sis_id() - joint_course_ids.append(joint_course_id) - c = validation_api.course_id(joint_course_id) - joint.append("{} {} {}".format( - c.curriculum, c.number, c.section)) - except Exception: - continue + course = Course(course_id) + event = Reservations().get_event_by_course(course) - if len(joint_course_ids): - joint_course_ids.sort() - course = validation_api.course_id(joint_course_ids[0]) + if event.is_crosslisted: + course = course.get_crosslisted_course() self.note("COURSE {}: validating {} meetings".format( course_id, len(event.reservations))) for rsv in event.reservations: if rsv.space_reservation: - name, external_id = panopto_course_session( - course, rsv.start_datetime) + name, external_id = course.panopto_course_session( + rsv.start_datetime) try: - space_id = rsv.space_reservation.space_id - course_recorder = self.recorders(space_id) + course_recorder = self.recorders(rsv.space_id) except UnassignedRecorder: recorder = self.get_recorder(course_id, external_id) if recorder: diff --git a/scheduler/migrations/0003_auto_20221128_1504.py b/scheduler/migrations/0003_auto_20221128_1504.py new file mode 100644 index 0000000..49ccb14 --- /dev/null +++ b/scheduler/migrations/0003_auto_20221128_1504.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.16 on 2022-11-28 23:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('scheduler', '0002_auto_20200226_2237'), + ] + + operations = [ + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('is_crosslisted', models.BooleanField(null=True)), + ], + ), + migrations.CreateModel( + name='Reservation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('event_name', models.CharField(max_length=64)), + ('profile_name', models.CharField(max_length=32)), + ('contact_name', models.CharField(max_length=64)), + ('contact_email', models.CharField(max_length=128)), + ('space_id', models.CharField(max_length=64, null=True)), + ('space_name', models.CharField(max_length=128, null=True)), + ('space_formal_name', models.CharField(max_length=128, null=True)), + ('is_instruction', models.BooleanField(null=True)), + ('is_course', models.BooleanField(null=True)), + ('start_datetime', models.DateTimeField()), + ('end_datetime', models.DateTimeField()), + ], + ), + migrations.DeleteModel( + name='Course', + ), + ] diff --git a/scheduler/models.py b/scheduler/models.py index 2d2e152..7cdfe1d 100644 --- a/scheduler/models.py +++ b/scheduler/models.py @@ -2,21 +2,39 @@ # SPDX-License-Identifier: Apache-2.0 from django.db import models - -# Create your models here. - - -class Course(models.Model): - year = models.CharField(max_length=4) - quarter = models.CharField(max_length=6) - curriculum = models.CharField(max_length=12) - number = models.CharField(max_length=3) - section = models.CharField(max_length=2) - - def __str__(self): - return "{}-{}-{}-{}-{}".format( - self.year, self.quarter, self.curriculum, - self.number, self.section) +from scheduler.user import User + + +class Event(models.Model): + is_crosslisted = models.BooleanField(null=True) + + +class Reservation(models.Model): + event_name = models.CharField(max_length=64) + profile_name = models.CharField(max_length=32) + contact_name = models.CharField(max_length=64) + contact_email = models.CharField(max_length=128) + space_id = models.CharField(max_length=64, null=True) + space_name = models.CharField(max_length=128, null=True) + space_formal_name = models.CharField(max_length=128, null=True) + is_instruction = models.BooleanField(null=True) + 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 { + 'name': self.contact_name, + 'loginid': User().validate_login_id(self.contact_email), + '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): diff --git a/scheduler/org/uw/course.py b/scheduler/org/uw/course.py new file mode 100644 index 0000000..fcb090a --- /dev/null +++ b/scheduler/org/uw/course.py @@ -0,0 +1,218 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from scheduler.course import BaseCourse +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_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 ( + MissingParamException, InvalidParamException, PanoptoFolderDoesNotExist) + +from restclients_core.exceptions import DataFailureException +from dateutil import parser +from nameparser import HumanName +import logging +import re + + +logger = logging.getLogger(__name__) + + +class Course(BaseCourse): + year = "" + quarter = "" + curriculum = "" + number = "" + section_id = "" + external_id = "" + canvas_course_id = None + section_data = None + UW_CAMPUS = ['seattle', 'bothell', 'tacoma'] + UW_TERMS = ['spring', 'summer', 'autumn', 'winter'] + + def __init__(self, courseId): + if not courseId: + raise MissingParamException('missing course id') + + course_parts = re.match( + r'^(\d{{4}})-({})-([\w& ]+)-(\d{{3}})-([A-Z][A-Z0-9]?)$'.format( + '|'.join(self.UW_TERMS)), courseId, re.I) + + if not course_parts: + raise InvalidParamException( + 'invalid course id: {}'.format(courseId)) + + self.year = course_parts.group(1) + self.quarter = course_parts.group(2).lower() + self.curriculum = self._is_curriculum(course_parts.group(3)) + self.number = self._is_course_number(course_parts.group(4)) + self.section_id = self._is_course_section(course_parts.group(5)) + + def _is_curriculum(self, curriculum): + if not curriculum: + raise MissingParamException('missing curriculum') + + if not re.match(r'^[a-z \&]{2,}$', curriculum, re.I): + raise InvalidParamException( + 'Invalid Curriculum: {}'.format(curriculum)) + + return curriculum.upper() + + def _is_course_number(self, course_number): + if not course_number: + raise MissingParamException('missing course number') + + if not re.match(r'^\d+$', course_number): + raise InvalidParamException( + 'Invalid Course Number: {}'.format(course_number)) + + return course_number + + def _is_course_section(self, course_section): + if not course_section: + raise MissingParamException('missing course section') + + return course_section.upper() + + def reservation_uid(self): + # OLD: r25 alien_id: 2014-4 0-MATH 124 A + # NEW: r25live alien_uid: LYNX-EV-104-20232-PHYS114A + return "LYNX-EV-104-{}{}-{}{}{}".format( + self.year, self._quarter_ordinal(), + self.curriculum.replace(' ', ''), + self.number, self.section_id) + + def canvas_sis_id(self): + return "{}".format('-'.join([ + self.year, self.quarter, self.curriculum, + self.number, self.section_id])) + + def panopto_course_external_id(self, start_datetime): + # session_external_id: 2014-autumn-BIOL-404-A-2014-10-27 + start_dt = parser.parse(start_datetime) + start_date = start_dt.strftime('%Y-%m-%d') + return "{}-{}-{}-{}-{}-{}".format( + self.year, self.quarter, self.curriculum, + self.number, self.section_id, start_date) + + 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: + 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': 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_by_external_id( + [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_id, + local_ymd_from_utc_date_string(start_datetime)) + external_id = self.panopto_course_external_id( + local_ymd_from_utc_date_string(start_datetime)) + return (name, external_id) + + @property + def sws_section(self): + if not self.section_data: + self.section_data = get_sws_section_for_course(self) + + return self.section_data + + def get_crosslisted_course(self): + # default to Canvas course that SIS provisioning selects + # for student sections + if (not self.sws_section.is_withdrawn + and len(self.sws_section.joint_section_urls)): + joint_course_ids = [self.canvas_sis_id()] + for url in self.sws_section.joint_section_urls: + try: + joint_section = get_sws_section_by_url(url) + if not joint_section.is_withdrawn: + joint_course_id = joint_section.canvas_course_sis_id() + joint_course_ids.append(joint_course_id) + c = Course(joint_course_id) + joint_course_ids.append(c.canvas_sis_id()) + except Exception: + continue + + if len(joint_course_ids): + joint_course_ids.sort() + return Course(joint_course_ids[0]) + + return self + + def _quarter_ordinal(self): + quarters = ['winter', 'spring', 'summer', 'autumn'] + return quarters.index(self.quarter.lower()) + 1 + + def _campus_ordinal(self): + try: + return Curriculum.objects.get( + curriculum_abbr=self.curriculum).campus_code + except Curriculum.DoesNotExist: + campus_code = self.UW_CAMPUS.index( + self.sws_section.course_campus.lower()) + curriculum = Curriculum(curriculum_abbr=self.curriculum, + campus_code=campus_code) + curriculum.save() + return campus_code diff --git a/scheduler/org/uw/reservations.py b/scheduler/org/uw/reservations.py new file mode 100644 index 0000000..767396e --- /dev/null +++ b/scheduler/org/uw/reservations.py @@ -0,0 +1,55 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + + +from scheduler.reservations.r25 import R25Reservations +import logging +import re + + +logger = logging.getLogger(__name__) + + +class Reservations(R25Reservations): + """ + University of Washington reservation profile definitions + + The general profile_name format is "MWF 1050-1150 LC 06/17" + where: + - first field holds course meeting days + - second field holds course meeting start and end time + - third field is type of course, which could be: + - LC: lecture + - SM: seminar + - QZ: quiz + - LB: Lab + - ST: Studio + - CL: clinic, usually off site or departmental clinic spaces + - CK: clerkship + - CO: conference + - PR: practicum + - fourth field is course start date + """ + + profile_name_re = re.compile(r'^(?P[A-Z]+)\s' + r'(?P\d{4})-(?P\d{4})\s' + r'(?P[A-Z]{2})\s' + r'(?P\d{2}/\d{2})$') + + @property + def instruction_profiles(self): + return ['LC', 'SM', 'QZ'] + + @property + def course_profiles(self): + return self.instruction_profiles + ['LB'] + + def course_type(self, profile): + try: + fields = self.profile_name_re.match(profile.upper()) + return fields.group('course_type') + except AttributeError: + pass + + logger.info('Unknown reservation profile: {}'.format(profile)) + return '' diff --git a/scheduler/org/uw/user.py b/scheduler/org/uw/user.py new file mode 100644 index 0000000..826dd3a --- /dev/null +++ b/scheduler/org/uw/user.py @@ -0,0 +1,27 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from scheduler.user import BaseUser +import re + + +class User(BaseUser): + + uw_netid_re = r'^\s*([a-z][a-z0-9_]+)(@(uw|(u\.)?washington).edu)?\s*$' + + def validate_login_ids(self, login_ids): + validated_login_ids = [] + for rawid in login_ids: + validated = self.validate_login_id(rawid) + validated_login_ids.append({ + 'loginid': validated + } if validated else { + 'loginid': rawid, + 'error': "Invalid UW Netid" + }) + + return validated_login_ids + + def validate_login_id(self, login_id): + match = re.match(self.uw_netid_re, login_id, re.I) + return match.group(1).lower() if match else None diff --git a/scheduler/panopto/folder.py b/scheduler/panopto/folder.py new file mode 100644 index 0000000..c7acb0c --- /dev/null +++ b/scheduler/panopto/folder.py @@ -0,0 +1,66 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +from django.conf import settings +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_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) + if ('UsersWithCreatorAccess' in folder_access and + folder_access['UsersWithCreatorAccess'] and + len(folder_access['UsersWithCreatorAccess'])): + guids = folder_access['UsersWithCreatorAccess'][0] + if len(guids): + users = get_users_from_guids(guids) + for user in users[0]: + match = re.match( + r'^{}\\(.+)$'.format( + settings.PANOPTO_API_APP_ID), user['UserKey']) + if match: + creators.append( + match.group(1) if match else user['UserKey']) + + return creators + + +def panopto_folder_id(event_folder): + try: + if event_folder['id']: + return event_folder['id'] + + folder_name = event_folder['name'] + if not folder_name: + return None + + except KeyError: + return None + + creators = [] + + 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)) + + return None diff --git a/scheduler/reservations/__init__.py b/scheduler/reservations/__init__.py new file mode 100644 index 0000000..92491dc --- /dev/null +++ b/scheduler/reservations/__init__.py @@ -0,0 +1,46 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +# +# Reservations reflect the course meeting time and location as they +# are represented in the Facilities Scheduling System +# + +from abc import ABC, abstractmethod +from scheduler.utils.loader import load_class_from_module_setting + + +RESERVATIONS_MODULE = 'RESERVATIONS_MODULE' + + +class Reservations(object): + """Indirection class used to load campus-specific meeting schedule""" + def __new__(self, *args, **kwargs): + return load_class_from_module_setting( + RESERVATIONS_MODULE, 'Reservations', *args, **kwargs) + + +class BaseReservations(ABC): + """Base class defines methods necessary for an event and associated + reservations scheduling. + """ + + @abstractmethod + def get_event_by_course(self, course): + """Return facilities schedule for provided course""" + pass + + @abstractmethod + def get_reservations_by_search_params(self, search): + """Return facilities schedule for provided search criteria""" + pass + + @abstractmethod + def get_space_by_id(self, space_id): + """Return specific reservable space by provided id""" + pass + + @abstractmethod + def get_spaces(self, *args, **kwargs): + """Return list of reservable spaces based on supplied filter""" + pass diff --git a/scheduler/reservations/r25.py b/scheduler/reservations/r25.py new file mode 100644 index 0000000..693c636 --- /dev/null +++ b/scheduler/reservations/r25.py @@ -0,0 +1,73 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +# +# Implements Reservations access and model instantiation for +# CollegeNET r25 Facility/Resource Reservations +# + +from scheduler.reservations import BaseReservations +from scheduler.models import Event, Reservation +from scheduler.dao.r25 import ( + get_event_by_course, get_reservations_by_search_params, + get_spaces, get_space_by_id) + + +class R25Reservations(BaseReservations): + def get_event_by_course(self, course): + """ + """ + r25 = get_event_by_course(course) + event = Event(is_crosslisted=(len(r25.binding_reservations) > 0)) + event.reservations = [] + for r in r25.reservations: + event.reservations.append(self._reservation_from_r25(r)) + + return event + + def get_reservations_by_search_params(self, search): + reservations = [] + for r25 in get_reservations_by_search_params(search): + reservations.append(self._reservation_from_r25(r25)) + + return reservations + + def _reservation_from_r25(self, r25): + """Map r25 reservation resourse into a Reservation model""" + return Reservation( + event_name=r25.event_name, + profile_name=r25.profile_name or '', + contact_name=r25.contact_name, + contact_email=r25.contact_email or '', + space_id=str(r25.space_reservation.space_id) if ( + r25.space_reservation) else None, + space_name=r25.space_reservation.name if ( + r25.space_reservation) else None, + space_formal_name=r25.space_reservation.formal_name if ( + r25.space_reservation) else None, + is_course=(self.course_type(r25.profile_name) + in self.course_profiles), + is_instruction=(self.course_type(r25.profile_name) + in self.instruction_profiles), + start_datetime=r25.start_datetime, + end_datetime=r25.end_datetime, + reservation_id=r25.reservation_id) + + @property + def instruction_profiles(self): + """R25 profile names indicating instructor is likely present""" + return [] + + @property + def course_profiles(self): + """R25 profile names indicating course and/or instruction related""" + return [] + + def course_type(self, profile): + return profile or '' + + def get_space_by_id(self, space_id): + return get_space_by_id(space_id) + + def get_spaces(self, *args, **kwargs): + return get_spaces(*args, **kwargs) diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-BIOL-404-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-BIOL-404-A_include_term index f121c24..f52a406 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-BIOL-404-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-BIOL-404-A_include_term @@ -1 +1 @@ -{"account_id":97454,"course_code":"BIOL 404 A","default_view":"wiki","enrollment_term_id":3929,"id":913649,"name":"BIOL 404 A: Animal Physiology: Cellular Aspects","start_at":"2014-09-17T19:41:35Z","end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_ABDEF939lj4lkIjkBt9GSFs5C6lfuz6rgufQY.ics"},"sis_course_id":"2014-autumn-BIOL-404-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":97454,"course_code":"BIOL 404 A","default_view":"wiki","enrollment_term_id":3929,"id":913649,"name":"BIOL 404 A: Animal Physiology: Cellular Aspects","start_at":"2014-09-17T19:41:35Z","end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_ABDEF939lj4lkIjkBt9GSFs5C6lfuz6rgufQY.ics"},"sis_course_id":"2014-autumn-BIOL-404-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-ENGL-102-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-ENGL-102-A_include_term index 53cb20f..ce31f8d 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-ENGL-102-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-ENGL-102-A_include_term @@ -1 +1 @@ -{"account_id":85791,"course_code":"ENGL 102 A","default_view":"feed","enrollment_term_id":3929,"id":915070,"name":"ENGL 102 A: Writing From Sources II","start_at":null,"end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_Valdkj334lfj9Mr7w3Cp6HGylkd55KfBx6xhJ.ics"},"sis_course_id":"2014-autumn-ENGL-102-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"unpublished"} \ No newline at end of file +{"account_id":85791,"course_code":"ENGL 102 A","default_view":"feed","enrollment_term_id":3929,"id":915070,"name":"ENGL 102 A: Writing From Sources II","start_at":null,"end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_Valdkj334lfj9Mr7w3Cp6HGylkd55KfBx6xhJ.ics"},"sis_course_id":"2014-autumn-ENGL-102-A","integration_id":null,"enrollments":[],"workflow_state":"unpublished"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-MATH-124-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-MATH-124-A_include_term index e8bdfc2..68742fa 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-MATH-124-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-MATH-124-A_include_term @@ -1 +1 @@ -{"account_id":97582,"course_code":"MATH 124 A","default_view":"feed","enrollment_term_id":3929,"id":917607,"name":"MATH 124 A: Calculus With Analytic Geometry I","start_at":null,"end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_Gwdza4lkjsdkd33rjfl35j661jfGb7l0UTCWyR.ics"},"sis_course_id":"2014-autumn-MATH-124-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"unpublished"} \ No newline at end of file +{"account_id":97582,"course_code":"MATH 124 A","default_view":"feed","enrollment_term_id":3929,"id":917607,"name":"MATH 124 A: Calculus With Analytic Geometry I","start_at":null,"end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_Gwdza4lkjsdkd33rjfl35j661jfGb7l0UTCWyR.ics"},"sis_course_id":"2014-autumn-MATH-124-A","integration_id":null,"enrollments":[],"workflow_state":"unpublished"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-PSYCH-101-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-PSYCH-101-A_include_term index abeb424..0055192 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-PSYCH-101-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2014-autumn-PSYCH-101-A_include_term @@ -1 +1 @@ -{"account_id":97648,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3929,"id":919124,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2014-09-24T17:30:00Z","end_at":"2014-12-08T18:20:00Z","public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blkjldf3350fjlkb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2014-autumn-PSYCH-101-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":97648,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3929,"id":919124,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2014-09-24T17:30:00Z","end_at":"2014-12-08T18:20:00Z","is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2014-12-12T08:00:00Z","id":3929,"name":"Autumn 2014","start_at":"2014-09-24T07:00:00Z","workflow_state":"active","sis_term_id":"2014-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blkjldf3350fjlkb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2014-autumn-PSYCH-101-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include[]_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include[]_term index 3100555..5e8556a 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include[]_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include[]_term @@ -1 +1 @@ -{"account_id":9999999,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3939,"id":99999,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2015-09-30T17:30:00Z","end_at":"2014-12-08T18:20:00Z","public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2015-12-11T08:00:00Z","id":3329,"name":"Autumn 2015","start_at":"2015-09-30T07:00:00Z","workflow_state":"active","sis_term_id":"2015-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blbbbjjjbjbjbjbb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2015-autumn-PSYCH-101-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":9999999,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3939,"id":99999,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2015-09-30T17:30:00Z","end_at":"2014-12-08T18:20:00Z","is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2015-12-11T08:00:00Z","id":3329,"name":"Autumn 2015","start_at":"2015-09-30T07:00:00Z","workflow_state":"active","sis_term_id":"2015-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blbbbjjjbjbjbjbb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2015-autumn-PSYCH-101-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include_term index 3100555..5e8556a 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-autumn-PSYCH-101-A_include_term @@ -1 +1 @@ -{"account_id":9999999,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3939,"id":99999,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2015-09-30T17:30:00Z","end_at":"2014-12-08T18:20:00Z","public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2015-12-11T08:00:00Z","id":3329,"name":"Autumn 2015","start_at":"2015-09-30T07:00:00Z","workflow_state":"active","sis_term_id":"2015-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blbbbjjjbjbjbjbb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2015-autumn-PSYCH-101-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":9999999,"course_code":"PSYCH 101 A","default_view":"wiki","enrollment_term_id":3939,"id":99999,"name":"PSYCH 101 A: Introduction To Psychology","start_at":"2015-09-30T17:30:00Z","end_at":"2014-12-08T18:20:00Z","is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":true,"term":{"end_at":"2015-12-11T08:00:00Z","id":3329,"name":"Autumn 2015","start_at":"2015-09-30T07:00:00Z","workflow_state":"active","sis_term_id":"2015-autumn"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_blbbbjjjbjbjbjbb3FEQdAMTvsxyFG.ics"},"sis_course_id":"2015-autumn-PSYCH-101-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-ENGL-243-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-ENGL-243-A_include_term index da145c4..1a1b022 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-ENGL-243-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-ENGL-243-A_include_term @@ -1 +1 @@ -{"account_id":85791,"course_code":"ENGL 243 A","default_view":"feed","enrollment_term_id":3970,"id":973054,"is_public":null,"name":"ENGL 243 A: Reading Poetry","start_at":"2015-03-24T22:39:39Z","end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"is_public_to_auth_users":false,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_ZMIvTLfHGwUDpLdXlbK28lijWdlldk3jf114k67kjdJ.ics"},"sis_course_id":"2015-summer-ENGL-243-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":85791,"course_code":"ENGL 243 A","default_view":"feed","enrollment_term_id":3970,"id":973054,"name":"ENGL 243 A: Reading Poetry","start_at":"2015-03-24T22:39:39Z","end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_ZMIvTLfHGwUDpLdXlbK28lijWdlldk3jf114k67kjdJ.ics"},"sis_course_id":"2015-summer-ENGL-243-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-GEMBA-579-B_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-GEMBA-579-B_include_term index b2e8609..177820c 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-GEMBA-579-B_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-GEMBA-579-B_include_term @@ -1 +1 @@ -{"account_id":97524,"course_code":"GEMBA 579 B","default_view":"feed","enrollment_term_id":3970,"id":973219,"is_public":null,"name":"GEMBA 579 B: Special Topics In Global Business","start_at":null,"end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"is_public_to_auth_users":false,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_16P20P7YEZs47z5x3fWt4Eljksdljfk3IQLOf9ESt.ics"},"sis_course_id":"2015-summer-GEMBA-579-B","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"unpublished"} \ No newline at end of file +{"account_id":97524,"course_code":"GEMBA 579 B","default_view":"feed","enrollment_term_id":3970,"id":973219,"name":"GEMBA 579 B: Special Topics In Global Business","start_at":null,"end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_16P20P7YEZs47z5x3fWt4Eljksdljfk3IQLOf9ESt.ics"},"sis_course_id":"2015-summer-GEMBA-579-B","integration_id":null,"enrollments":[],"workflow_state":"unpublished"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-101-B_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-101-B_include_term index 7726d32..3066a55 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-101-B_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-101-B_include_term @@ -1 +1 @@ -{"account_id":97648,"course_code":"PSYCH 101 B","default_view":"syllabus","enrollment_term_id":3970,"id":974379,"is_public":null,"name":"PSYCH 101 B: Introduction To Psychology","start_at":"2015-01-05T18:21:01Z","end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"is_public_to_auth_users":false,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_kldjldjf334vht54cH1kfslkdjsldKgWiLf6in6H7C4Y.ics"},"sis_course_id":"2015-summer-PSYCH-101-B","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":97648,"course_code":"PSYCH 101 B","default_view":"syllabus","enrollment_term_id":3970,"id":974379,"name":"PSYCH 101 B: Introduction To Psychology","start_at":"2015-01-05T18:21:01Z","end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_kldjldjf334vht54cH1kfslkdjsldKgWiLf6in6H7C4Y.ics"},"sis_course_id":"2015-summer-PSYCH-101-B","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-202-A_include_term b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-202-A_include_term index 11f6341..e074fe7 100644 --- a/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-202-A_include_term +++ b/scheduler/resources/canvas/file/api/v1/courses/sis_course_id%3A2015-summer-PSYCH-202-A_include_term @@ -1 +1 @@ -{"account_id":97648,"course_code":"PSYCH 202 A","default_view":"modules","enrollment_term_id":3970,"id":974381,"is_public":false,"name":"PSYCH 202 A: Biopsychology","start_at":"2015-06-17T21:33:00Z","end_at":null,"public_syllabus":false,"storage_quota_mb":1000,"is_public_to_auth_users":false,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_B31Zxcbw2w8fKYXKD6iW9zulkdaaff480r8LJQ71pbwJDAVSU.ics"},"sis_course_id":"2015-summer-PSYCH-202-A","integration_id":null,"is_public":false,"is_public_to_auth_users":false,"enrollments":[],"workflow_state":"available"} \ No newline at end of file +{"account_id":97648,"course_code":"PSYCH 202 A","default_view":"modules","enrollment_term_id":3970,"id":974381,"is_public":false,"name":"PSYCH 202 A: Biopsychology","start_at":"2015-06-17T21:33:00Z","end_at":null,"is_public":false,"is_public_to_auth_users":false,"public_syllabus":false,"storage_quota_mb":1000,"is_public_to_auth_users":false,"hide_final_grades":false,"term":{"end_at":"2015-08-26T07:00:00Z","id":3970,"name":"Summer 2015","start_at":"2015-06-22T07:00:00Z","workflow_state":"active","sis_term_id":"2015-summer"},"apply_assignment_group_weights":false,"calendar":{"ics":"https://uw.test.instructure.com/feeds/calendars/course_B31Zxcbw2w8fKYXKD6iW9zulkdaaff480r8LJQ71pbwJDAVSU.ics"},"sis_course_id":"2015-summer-PSYCH-202-A","integration_id":null,"enrollments":[],"workflow_state":"available"} diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_IRemoteRecorderManagement/ScheduleRecording/4D636EED27CE9A2EACAD798E82C139BF b/scheduler/resources/panopto/file/BasicHttpBinding_IRemoteRecorderManagement/ScheduleRecording/4D636EED27CE9A2EACAD798E82C139BF new file mode 100644 index 0000000..d265694 --- /dev/null +++ b/scheduler/resources/panopto/file/BasicHttpBinding_IRemoteRecorderManagement/ScheduleRecording/4D636EED27CE9A2EACAD798E82C139BF @@ -0,0 +1 @@ +false23012340-0000-0000-0000-ab000001234d \ No newline at end of file diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersByExternalId/DAEB0FCDD492D3F8A120619D33A96D09 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersByExternalId/DAEB0FCDD492D3F8A120619D33A96D09 index b52600e..c338d3a 100644 --- a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersByExternalId/DAEB0FCDD492D3F8A120619D33A96D09 +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersByExternalId/DAEB0FCDD492D3F8A120619D33A96D09 @@ -1 +1 @@ -truefalseitpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&type=mp3http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&type=mp3falsehttps://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444e-0000-0000-0000-ab71234c452dhttps://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&folderID=c9123444e-0000-0000-0000-ab71234c452dtruec9123444e-0000-0000-0000-ab71234c452dfalsehttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444e-0000-0000-0000-ab71234c452dAutumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychologyhttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&folderID=c9123444e-0000-0000-0000-ab71234c452ditpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&type=mp4http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444e-0000-0000-0000-ab71234c452d&type=mp4 +truefalseitpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&type=mp3http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&type=mp3falsehttps://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444-0000-0000-0000-ab71234c452dhttps://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&folderID=c9123444-0000-0000-0000-ab71234c452dtruec9123444-0000-0000-0000-ab71234c452dfalsehttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444-0000-0000-0000-ab71234c452dAutumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychologyhttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&folderID=c9123444-0000-0000-0000-ab71234c452ditpc://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&type=mp4http://server.panopto.com/Panopto/Podcast/Podcast.ashx?courseid=c9123444-0000-0000-0000-ab71234c452d&type=mp4 diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersWithExternalContextByExternalId/6298A683FA88F4AC8518436715C5A45A b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersWithExternalContextByExternalId/6298A683FA88F4AC8518436715C5A45A new file mode 100644 index 0000000..5a6f032 --- /dev/null +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetAllFoldersWithExternalContextByExternalId/6298A683FA88F4AC8518436715C5A45A @@ -0,0 +1 @@ +truefalsefalsehttps://server.panopto.com/Panopto/Pages/Sessions/EmbeddedUpload.aspx?folderID=c9123444-0000-0000-0000-ab71234c452dhttps://server.panopto.com/Panopto/Pages/EmbeddedList.aspx?embedded=1&folderID=c9123444-0000-0000-0000-ab71234c452dtrue696c3acb-71c2-4071-9668-b18f00eecf3bfalsehttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?folderID=c9123444-0000-0000-0000-ab71234c452dAutumnr 2015 - PSYCH 101 A Au 15: Introduction To Psychologyhttps://server.panopto.com/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&folderID=c9123444-0000-0000-0000-ab71234c452dUWNetid-uw-test\\1730776 diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetFoldersList/95CE2D6E8FB2619D93C786F054F29E61 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetFoldersList/95CE2D6E8FB2619D93C786F054F29E61 new file mode 100644 index 0000000..3a109be --- /dev/null +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetFoldersList/95CE2D6E8FB2619D93C786F054F29E61 @@ -0,0 +1 @@ +truefalseitpc://media-amp-server.foo/Panopto/Podcast/Podcast.ashx?courseid=30f1234a-1234-1234-1234-60c12345b78e&type=mp3http://media-amp-server.foo/Panopto/Podcast/Podcast.ashx?courseid=30f1234a-1234-1234-1234-60c12345b78e&type=mp3falsetrue30f1234a-1234-1234-1234-60c12345b78efalsehttp://media-amp-server.foo/Panopto/Pages/Sessions/List.aspx?folderID=30f1234a-1234-1234-1234-60c12345b78eAutumn 2014 - PSYCH 101 A: Introduction To Psychologyhttp://media-amp-server.foo/Panopto/Pages/Sessions/List.aspx?modalPage=FolderSettings&folderID=30f1234a-1234-1234-1234-60c12345b78eitpc://media-amp-server.foo/Panopto/Podcast/Podcast.ashx?courseid=30f1234a-1234-1234-1234-60c12345b78e&type=mp4http://media-amp-server.foo/Panopto/Podcast/Podcast.ashx?courseid=30f1234a-1234-1234-1234-60c12345b78e&type=mp41 diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/0E33841B3F905C740C82794EC9920C48 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/0E33841B3F905C740C82794EC9920C48 index 3a7ece3..d21f62d 100644 --- a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/0E33841B3F905C740C82794EC9920C48 +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/0E33841B3F905C740C82794EC9920C48 @@ -1 +1 @@ -b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-0880456dbb-8350-416c-a97d-36f683ce66dcAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file +b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-08c9123444-0000-0000-0000-ab71234c452dAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/4B8FDE6E65F308A114DF5C05EFE1BA31 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/4B8FDE6E65F308A114DF5C05EFE1BA31 index 3a7ece3..d21f62d 100644 --- a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/4B8FDE6E65F308A114DF5C05EFE1BA31 +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsByExternalId/4B8FDE6E65F308A114DF5C05EFE1BA31 @@ -1 +1 @@ -b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-0880456dbb-8350-416c-a97d-36f683ce66dcAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file +b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-08c9123444-0000-0000-0000-ab71234c452dAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/8B4EF0CEB260E300112EDBF5EF4EE5A6 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/8B4EF0CEB260E300112EDBF5EF4EE5A6 index 1e95825..3fec1ee 100644 --- a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/8B4EF0CEB260E300112EDBF5EF4EE5A6 +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/8B4EF0CEB260E300112EDBF5EF4EE5A6 @@ -1 +1 @@ -b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-0880456dbb-8350-416c-a97d-36f683ce66dcAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file +b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-08c9123444-0000-0000-0000-ab71234c452dAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file diff --git a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/EE5BF1A43C87AC958B88856CA5494F10 b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/EE5BF1A43C87AC958B88856CA5494F10 index 1e95825..3fec1ee 100644 --- a/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/EE5BF1A43C87AC958B88856CA5494F10 +++ b/scheduler/resources/panopto/file/BasicHttpBinding_ISessionManagement/GetSessionsById/EE5BF1A43C87AC958B88856CA5494F10 @@ -1 +1 @@ -b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-0880456dbb-8350-416c-a97d-36f683ce66dcAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file +b23e442b-abff-458e-b09b-d18851f617542100https://uw-test.hosted.panopto.com/Panopto/Pages/Editor/Default.aspx#673fcde2-4d70-4610-89b3-3638684cf90c2015-autumn-PSYCH-101-A-2015-12-08c9123444-0000-0000-0000-ab71234c452dAutumn 2015 - PSYCH 101 A: Introduction To Psychology673fcde2-4d70-4610-89b3-3638684cf90ctruefalsePSYCH 101 A - 2015-12-08https://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&notes=truehttps://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionOutputs53768edb-fa5f-4f36-9a06-253105354811https://uw-test.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c&modalPage=SessionShare2015-12-08T21:40:00ZScheduledScheduled recording/Panopto/Cache/4.9.2.22541/Images/no_thumbnail.pnghttps://uw-test.hosted.panopto.com/Panopto/Pages/Viewer.aspx?id=673fcde2-4d70-4610-89b3-3638684cf90c \ No newline at end of file diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20133-CHEM140A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20133-CHEM140A index 78a3dda..ef91aa6 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20133-CHEM140A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20133-CHEM140A @@ -1,6 +1,6 @@ -" +" 5,r25,/r25ws/servlet/wrd/run/event.xml?alien_uid=LYNX-EV-104-20133-CHEM140A,200,KGRwMAou," @@ -46,7 +46,7 @@ 1876022 -LECTURE +MTWTF 1030-1120 LC 09/30 W1 MO TU WE TH FR 20141205T235900-0800 From 10:30 AM to 11:20 AM. Starting on WED SEP/24/2014. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-BIOL404A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-BIOL404A index 31213ff..20e7c7f 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-BIOL404A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-BIOL404A @@ -43,7 +43,7 @@ 1963698 -LECTURE +MWF 1330-1420 LC 09/24 W1 MO WE FR 20141205T235900-0800 From 01:30 PM to 02:20 PM. Starting on WED SEP/24/2014. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-ENGL102A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-ENGL102A index 8ab3e48..ac5095d 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-ENGL102A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-ENGL102A @@ -43,7 +43,7 @@ 1965877 -LECTURE +MTWTF 0830-0920 LC 09/24 W1 MO TU WE TH FR 20141205T235900-0800 From 08:30 AM to 09:20 AM. Starting on WED SEP/24/2014. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-MATH124A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-MATH124A index 3f57c34..e992091 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-MATH124A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-MATH124A @@ -43,7 +43,7 @@ 1977448 -LECTURE +MWF 0830-0950 LC 09/24 W1 MO WE FR 20141205T235900-0800 From 08:30 AM to 09:20 AM. Starting on WED SEP/24/2014. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-PSYCH101A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-PSYCH101A index 39870fc..1fb6fd7 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-PSYCH101A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20144-PSYCH101A @@ -43,7 +43,7 @@ 1876022 -LECTURE +MTWTF 1030-1120 LC 09/24 W1 MO TU WE TH FR 20141205T235900-0800 From 10:30 AM to 11:20 AM. Starting on WED SEP/24/2014. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20154-PSYCH101A b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20154-PSYCH101A index 3c97054..01e8f78 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20154-PSYCH101A +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/event.xml_alien_uid_LYNX-EV-104-20154-PSYCH101A @@ -43,7 +43,7 @@ 2052966 -LECTURE +MTWTF 1330-1420 LC 09/30 W1 MO TU WE TH FR 20151211T235900-0800 From 01:30 PM to 02:20 PM. Starting on WED SEP/30/2015. diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml?space_id_4991&start_dt_2015-12-02T08_00_00.000Z&session_ids_None&scope_extended b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml?space_id_4991&start_dt_2015-12-02T08_00_00.000Z&session_ids_None&scope_extended index ccab1ed..2df4692 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml?space_id_4991&start_dt_2015-12-02T08_00_00.000Z&session_ids_None&scope_extended +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml?space_id_4991&start_dt_2015-12-02T08_00_00.000Z&session_ids_None&scope_extended @@ -10,7 +10,7 @@ 2015-12-02T09:20:00-08:00 2015-12-02T09:20:00-08:00 2129930 -LECTURE +MWF 0830-0950 LC 09/30 From 08:30 AM to 09:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -88,7 +88,7 @@ Repeat every week 2015-12-02T10:20:00-08:00 2015-12-02T10:20:00-08:00 2130020 -LECTURE +MWF 0830-0950 LC 09/30 From 09:30 AM to 10:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -166,7 +166,7 @@ Repeat every week 2015-12-02T10:20:00-08:00 2015-12-02T10:20:00-08:00 2130019 -LECTURE +MWF 0830-0950 LC 09/30 From 09:30 AM to 10:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -244,7 +244,7 @@ Repeat every week 2015-12-02T12:20:00-08:00 2015-12-02T12:20:00-08:00 2052957 -LECTURE +MWF 0830-0950 LC 09/30 From 10:30 AM to 12:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -322,7 +322,7 @@ Repeat every week 2015-12-02T13:20:00-08:00 2015-12-02T13:20:00-08:00 2130084 -LECTURE +MWF 0830-0950 LC 09/30 From 12:30 PM to 01:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -400,7 +400,7 @@ Repeat every week 2015-12-02T14:20:00-08:00 2015-12-02T14:20:00-08:00 2040380 -LECTURE +MWF 0830-0950 LC 09/30 From 01:30 PM to 02:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -478,7 +478,7 @@ Repeat every week 2015-12-02T15:20:00-08:00 2015-12-02T15:20:00-08:00 2078591 -LECTURE +MWF 0830-0950 LC 09/30 From 02:30 PM to 03:20 PM. Starting on WED SEP/30/2015. Repeat every week diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4635_start_dt_20141201 b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4635_start_dt_20141201 index 04221df..e8de897 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4635_start_dt_20141201 +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4635_start_dt_20141201 @@ -10,7 +10,7 @@ 2014-12-01T09:20:00-08:00 2014-12-01T09:20:00-08:00 1978685 -LECTURE +MWF 0830-0920 LC 09/24 From 08:30 AM to 09:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -88,7 +88,7 @@ Repeat every week 2014-12-01T10:20:00-08:00 2014-12-01T10:20:00-08:00 1953421 -LECTURE +MWF 0830-0920 LC 09/24 From 09:30 AM to 10:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -166,7 +166,7 @@ Repeat every week 2014-12-01T11:20:00-08:00 2014-12-01T11:20:00-08:00 1964455 -LECTURE +MWF 0830-0920 LC 09/24 From 10:30 AM to 11:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -244,7 +244,7 @@ Repeat every week 2014-12-01T12:20:00-08:00 2014-12-01T12:20:00-08:00 1964464 -LECTURE +MWF 0830-0920 LC 09/24 From 11:30 AM to 12:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -322,7 +322,7 @@ Repeat every week 2014-12-01T13:20:00-08:00 2014-12-01T13:20:00-08:00 1918763 -LECTURE +MWF 0830-0920 LC 09/24 From 12:30 PM to 01:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -400,7 +400,7 @@ Repeat every week 2014-12-01T14:20:00-08:00 2014-12-01T14:20:00-08:00 1963698 -LECTURE +MWF 0830-0920 LC 09/24 From 01:30 PM to 02:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -478,7 +478,7 @@ Repeat every week 2014-12-01T15:20:00-08:00 2014-12-01T15:20:00-08:00 1964310 -LECTURE +MWF 0830-0920 LC 09/24 From 02:30 PM to 03:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -556,7 +556,7 @@ Repeat every week 2014-12-01T17:20:00-08:00 2014-12-01T17:20:00-08:00 1964567 -SEMINAR +M 1530-1720 SM 09/24 From 03:30 PM to 05:20 PM. Starting on MON SEP/29/2014. Repeat every week diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4990_start_dt_20141201 b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4990_start_dt_20141201 index 88bce92..9b9a596 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4990_start_dt_20141201 +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4990_start_dt_20141201 @@ -10,7 +10,7 @@ 2014-12-01T10:20:00-08:00 2014-12-01T10:20:00-08:00 1968985 -LECTURE +MWF 0930-1020 LC 09/30 From 09:30 AM to 10:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -88,7 +88,7 @@ Repeat every week 2014-12-01T11:20:00-08:00 2014-12-01T11:20:00-08:00 1876550 -LECTURE +MWF 0930-1020 LC 09/30 From 10:30 AM to 11:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -166,7 +166,7 @@ Repeat every week 2014-12-01T12:20:00-08:00 2014-12-01T12:20:00-08:00 1966516 -LECTURE +MWF 0930-1020 LC 09/30 From 11:30 AM to 12:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -244,7 +244,7 @@ Repeat every week 2014-12-01T13:20:00-08:00 2014-12-01T13:20:00-08:00 1963273 -LECTURE +MWF 0930-1020 LC 09/30 From 12:30 PM to 01:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -322,7 +322,7 @@ Repeat every week 2014-12-01T14:20:00-08:00 2014-12-01T14:20:00-08:00 1966111 -LECTURE +MWF 0930-1020 LC 09/30 From 01:30 PM to 02:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -400,7 +400,7 @@ Repeat every week 2014-12-01T16:50:00-08:00 2014-12-01T16:50:00-08:00 1893015 -LECTURE +MWF 0930-1020 LC 09/30 From 02:30 PM to 04:50 PM. Starting on WED SEP/24/2014. Repeat every week diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4991_start_dt_2015-12-02T08%3A00%3A00.000Z b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4991_start_dt_2015-12-02T08%3A00%3A00.000Z index bc1edf6..978cdd4 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4991_start_dt_2015-12-02T08%3A00%3A00.000Z +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_4991_start_dt_2015-12-02T08%3A00%3A00.000Z @@ -10,7 +10,7 @@ 2015-12-02T09:20:00-08:00 2015-12-02T09:20:00-08:00 2129930 -LECTURE +MWF 0830-0920 LC 09/30 From 08:30 AM to 09:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -88,7 +88,7 @@ Repeat every week 2015-12-02T10:20:00-08:00 2015-12-02T10:20:00-08:00 2130020 -LECTURE +MWF 0830-0920 LC 09/30 From 09:30 AM to 10:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -166,7 +166,7 @@ Repeat every week 2015-12-02T10:20:00-08:00 2015-12-02T10:20:00-08:00 2130019 -LECTURE +MWF 0830-0920 LC 09/30 From 09:30 AM to 10:20 AM. Starting on WED SEP/30/2015. Repeat every week @@ -244,7 +244,7 @@ Repeat every week 2015-12-02T12:20:00-08:00 2015-12-02T12:20:00-08:00 2052957 -LECTURE +MWF 0830-0920 LC 09/30 From 10:30 AM to 12:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -322,7 +322,7 @@ Repeat every week 2015-12-02T13:20:00-08:00 2015-12-02T13:20:00-08:00 2130084 -LECTURE +MWF 0830-0920 LC 09/30 From 12:30 PM to 01:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -400,7 +400,7 @@ Repeat every week 2015-12-02T14:20:00-08:00 2015-12-02T14:20:00-08:00 2040380 -LECTURE +MWF 0830-0920 LC 09/30 From 01:30 PM to 02:20 PM. Starting on WED SEP/30/2015. Repeat every week @@ -478,7 +478,7 @@ Repeat every week 2015-12-02T15:20:00-08:00 2015-12-02T15:20:00-08:00 2078591 -LECTURE +MWF 0830-0920 LC 09/30 From 02:30 PM to 03:20 PM. Starting on WED SEP/30/2015. Repeat every week diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6063_start_dt_20141201 b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6063_start_dt_20141201 index 458dc74..e5d1a09 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6063_start_dt_20141201 +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6063_start_dt_20141201 @@ -1 +1 @@ -4051171812014-12-01T08:30:00-08:002014-12-01T08:30:00-08:002014-12-01T08:30:00-08:002014-12-01T09:20:00-08:002014-12-01T09:20:00-08:002014-12-01T09:20:00-08:001968645LECTUREFrom 08:30 AM to 09:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.200194import2014-10-04T13:38:22-07:000601115LING 100 AFUND OF GRAMMARF2014-ABIFFV341UG_SECTION1Tentative4328TLING-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium200F4036076512014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962958LECTUREFrom 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:48:38-07:000596940ARCH 350 DARCH ANCIENT WORLDF2014-ABHZBG341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036081012014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962959LECTUREFrom 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:48:59-07:000596941ARCH 350 AARCH ANCIENT WORLDF2014-ABHZBH341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036085512014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962960LECTUREFrom 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:49:19-07:000596942ARCH 350 BARCH ANCIENT WORLDF2014-ABHZBI341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036090012014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962961LECTUREFrom 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:49:38-07:000596943ARCH 350 CARCH ANCIENT WORLDF2014-ABHZBJ341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036752612014-12-01T10:30:00-08:002014-12-01T10:30:00-08:002014-12-01T10:30:00-08:002014-12-01T11:20:00-08:002014-12-01T11:20:00-08:002014-12-01T11:20:00-08:001963210LECTUREFrom 10:30 AM to 11:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.299288import2014-10-04T02:24:39-07:000597096ASTR 101 AASTRONOMYF2014-ABHZHG341UG_SECTION1Tentative5180TASTR-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium299F3833115012014-12-01T11:30:00-08:002014-12-01T11:30:00-08:002014-12-01T11:30:00-08:002014-12-01T12:20:00-08:002014-12-01T12:20:00-08:002014-12-01T12:20:00-08:001876570LECTUREFrom 11:30 AM to 12:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.290import2014-02-18T23:55:29-08:000603224SOC 271 ASOC DEVE/SOC CNTRLF2014-ABIIIY341UG_SECTION1Tentative4443TSOC-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium290F4039772912014-12-01T12:30:00-08:002014-12-01T12:30:00-08:002014-12-01T12:30:00-08:002014-12-01T13:20:00-08:002014-12-01T13:20:00-08:002014-12-01T13:20:00-08:001964413LECTUREFrom 12:30 PM to 01:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.300300import2014-10-04T04:29:07-07:000597909CHEM 237 CORGANIC CHEMISTRYF2014-ABIAMN341UG_SECTION1Tentative4203TCHEM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium300F3855153612014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:001885368LECTUREFrom 01:30 PM to 02:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON TUE WED THU FRI until FRI DEC/05/2014.305import2014-03-05T15:59:53-08:000602918PSYCH 101 CINTRO TO PSYCHF2014-ABIHXE341UG_SECTION1Tentative4409TPSYCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F3855160912014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:001885369LECTUREFrom 01:30 PM to 02:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON TUE WED THU FRI until FRI DEC/05/2014.305import2014-03-05T16:00:08-08:000602919PSYCH 101 FINTRO TO PSYCHF2014-ABIHXF341UG_SECTION1Tentative4409TPSYCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4051196912014-12-01T14:30:00-08:002014-12-01T14:30:00-08:002014-12-01T14:30:00-08:002014-12-01T15:20:00-08:002014-12-01T15:20:00-08:002014-12-01T15:20:00-08:001968655LECTUREFrom 02:30 PM to 03:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.270270import2014-10-04T13:39:08-07:000601128LING 200 AINTRO LING THOUGHTF2014-ABIFGI341UG_SECTION1Tentative4328TLING-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium270F4039739412014-12-01T15:30:00-08:002014-12-01T15:30:00-08:002014-12-01T15:30:00-08:002014-12-01T16:20:00-08:002014-12-01T16:20:00-08:002014-12-01T16:20:00-08:001964399LECTUREFrom 03:30 PM to 04:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.300295import2014-10-04T04:28:21-07:000597896CHEM 237 BORGANIC CHEMISTRYF2014-ABIAMA341UG_SECTION1Tentative4203TCHEM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium300F4032779712014-12-01T16:30:00-08:002014-12-01T16:30:00-08:002014-12-01T16:30:00-08:002014-12-01T17:20:00-08:002014-12-01T17:20:00-08:002014-12-01T17:20:00-08:001961675MWF 430-520PFrom 04:30 PM to 05:20 PM.Starting on FRI OCT/03/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.mgary2014-10-02T14:25:41-07:000627757CHEM 237 B EXTRA TIME FOR LECTURECHEM 237 B EXTRA TIME FOR LECTUREF2014-ABJSQN327Lecture2Confirmed4203TCHEM-2Scheduler5086GARY, MARIE3Room Assignments/Time Schedule, Box 355850248 Schmitz Hall mgary@u.washington.edu206 543-1081206 616-3222-1Requestor10108CHAN, JERRY3206-543-46586063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium02AuditoriumF4040702812014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:001964747LECTUREFrom 06:00 PM to 08:50 PM.Starting on MON SEP/29/2014.Repeat every week on MON until FRI DEC/05/2014.9147import2014-10-04T05:14:31-07:000598149CM 500 BDESIGN & CONST LAWF2014-ABIAVT340Grad_section1Tentative4223TCM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium91F4040707412014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:001964749LECTUREFrom 06:00 PM to 08:50 PM.Starting on MON SEP/29/2014.Repeat every week on MON until FRI DEC/05/2014.9147import2014-10-04T05:14:44-07:000598151ARCH 574 BDESIGN & CONST LAWF2014-ABIAVV340Grad_section1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium91F +4051171812014-12-01T08:30:00-08:002014-12-01T08:30:00-08:002014-12-01T08:30:00-08:002014-12-01T09:20:00-08:002014-12-01T09:20:00-08:002014-12-01T09:20:00-08:001968645MTWTF 0830-0920 LC 09/30From 08:30 AM to 09:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.200194import2014-10-04T13:38:22-07:000601115LING 100 AFUND OF GRAMMARF2014-ABIFFV341UG_SECTION1Tentative4328TLING-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium200F4036076512014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962958MTWTF 0830-0920 LC 09/30From 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:48:38-07:000596940ARCH 350 DARCH ANCIENT WORLDF2014-ABHZBG341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036081012014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962959MTWTF 0830-0920 LC 09/30From 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:48:59-07:000596941ARCH 350 AARCH ANCIENT WORLDF2014-ABHZBH341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036085512014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962960MTWTF 0830-0920 LC 09/30From 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:49:19-07:000596942ARCH 350 BARCH ANCIENT WORLDF2014-ABHZBI341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036090012014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T09:30:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:002014-12-01T10:20:00-08:001962961MTWTF 0830-0920 LC 09/30From 09:30 AM to 10:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.305146import2014-10-04T01:49:38-07:000596943ARCH 350 CARCH ANCIENT WORLDF2014-ABHZBJ341UG_SECTION1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4036752612014-12-01T10:30:00-08:002014-12-01T10:30:00-08:002014-12-01T10:30:00-08:002014-12-01T11:20:00-08:002014-12-01T11:20:00-08:002014-12-01T11:20:00-08:001963210MTWTF 0830-0920 LC 09/30From 10:30 AM to 11:20 AM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.299288import2014-10-04T02:24:39-07:000597096ASTR 101 AASTRONOMYF2014-ABHZHG341UG_SECTION1Tentative5180TASTR-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium299F3833115012014-12-01T11:30:00-08:002014-12-01T11:30:00-08:002014-12-01T11:30:00-08:002014-12-01T12:20:00-08:002014-12-01T12:20:00-08:002014-12-01T12:20:00-08:001876570MTWTF 0830-0920 LC 09/30From 11:30 AM to 12:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.290import2014-02-18T23:55:29-08:000603224SOC 271 ASOC DEVE/SOC CNTRLF2014-ABIIIY341UG_SECTION1Tentative4443TSOC-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium290F4039772912014-12-01T12:30:00-08:002014-12-01T12:30:00-08:002014-12-01T12:30:00-08:002014-12-01T13:20:00-08:002014-12-01T13:20:00-08:002014-12-01T13:20:00-08:001964413MTWTF 0830-0920 LC 09/30From 12:30 PM to 01:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.300300import2014-10-04T04:29:07-07:000597909CHEM 237 CORGANIC CHEMISTRYF2014-ABIAMN341UG_SECTION1Tentative4203TCHEM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium300F3855153612014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:001885368MTWTF 0830-0920 LC 09/30From 01:30 PM to 02:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON TUE WED THU FRI until FRI DEC/05/2014.305import2014-03-05T15:59:53-08:000602918PSYCH 101 CINTRO TO PSYCHF2014-ABIHXE341UG_SECTION1Tentative4409TPSYCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F3855160912014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T13:30:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:002014-12-01T14:20:00-08:001885369MTWTF 0830-0920 LC 09/30From 01:30 PM to 02:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON TUE WED THU FRI until FRI DEC/05/2014.305import2014-03-05T16:00:08-08:000602919PSYCH 101 FINTRO TO PSYCHF2014-ABIHXF341UG_SECTION1Tentative4409TPSYCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium305F4051196912014-12-01T14:30:00-08:002014-12-01T14:30:00-08:002014-12-01T14:30:00-08:002014-12-01T15:20:00-08:002014-12-01T15:20:00-08:002014-12-01T15:20:00-08:001968655MTWTF 0830-0920 LC 09/30From 02:30 PM to 03:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.270270import2014-10-04T13:39:08-07:000601128LING 200 AINTRO LING THOUGHTF2014-ABIFGI341UG_SECTION1Tentative4328TLING-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium270F4039739412014-12-01T15:30:00-08:002014-12-01T15:30:00-08:002014-12-01T15:30:00-08:002014-12-01T16:20:00-08:002014-12-01T16:20:00-08:002014-12-01T16:20:00-08:001964399MTWTF 0830-0920 LC 09/30From 03:30 PM to 04:20 PM.Starting on WED SEP/24/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.300295import2014-10-04T04:28:21-07:000597896CHEM 237 BORGANIC CHEMISTRYF2014-ABIAMA341UG_SECTION1Tentative4203TCHEM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium300F4032779712014-12-01T16:30:00-08:002014-12-01T16:30:00-08:002014-12-01T16:30:00-08:002014-12-01T17:20:00-08:002014-12-01T17:20:00-08:002014-12-01T17:20:00-08:001961675MWF 430-520PFrom 04:30 PM to 05:20 PM.Starting on FRI OCT/03/2014.Repeat every week on MON WED FRI until FRI DEC/05/2014.mgary2014-10-02T14:25:41-07:000627757CHEM 237 B EXTRA TIME FOR LECTURECHEM 237 B EXTRA TIME FOR LECTUREF2014-ABJSQN327Lecture2Confirmed4203TCHEM-2Scheduler5086GARY, MARIE3Room Assignments/Time Schedule, Box 355850248 Schmitz Hall mgary@u.washington.edu206 543-1081206 616-3222-1Requestor10108CHAN, JERRY3206-543-46586063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium02AuditoriumF4040702812014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:001964747MTWTF 0830-0920 LC 09/30From 06:00 PM to 08:50 PM.Starting on MON SEP/29/2014.Repeat every week on MON until FRI DEC/05/2014.9147import2014-10-04T05:14:31-07:000598149CM 500 BDESIGN & CONST LAWF2014-ABIAVT340Grad_section1Tentative4223TCM-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium91F4040707412014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T18:00:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:002014-12-01T20:50:00-08:001964749MTWTF 0830-0920 LC 09/30From 06:00 PM to 08:50 PM.Starting on MON SEP/29/2014.Repeat every week on MON until FRI DEC/05/2014.9147import2014-10-04T05:14:44-07:000598151ARCH 574 BDESIGN & CONST LAWF2014-ABIAVV340Grad_section1Tentative5176TARCH-2Scheduler3310IMPORT PROCESS3times@u.washington.edu6063ARC 147Architecture Hall305135Southwest Campus Auditorium2Auditorium91F diff --git a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6283_start_dt_20141201 b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6283_start_dt_20141201 index 1c5e706..53298cb 100644 --- a/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6283_start_dt_20141201 +++ b/scheduler/resources/r25/file/r25ws/servlet/wrd/run/reservations.xml_scope_extended_space_id_6283_start_dt_20141201 @@ -10,7 +10,7 @@ 2014-12-01T08:50:00-08:00 2014-12-01T08:50:00-08:00 1873972 -LECTURE +MWF 0730-080 LC 09/24 From 07:30 AM to 08:50 AM. Starting on WED SEP/24/2014. Repeat every week @@ -88,7 +88,7 @@ Repeat every week 2014-12-01T10:20:00-08:00 2014-12-01T10:20:00-08:00 1963005 -LECTURE +MWF 0730-0850 LC 09/24 From 09:30 AM to 10:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -166,7 +166,7 @@ Repeat every week 2014-12-01T11:20:00-08:00 2014-12-01T11:20:00-08:00 1875346 -LECTURE +MWF 0730-0850 LC 09/24 From 10:30 AM to 11:20 AM. Starting on WED SEP/24/2014. Repeat every week @@ -244,7 +244,7 @@ Repeat every week 2014-12-01T12:20:00-08:00 2014-12-01T12:20:00-08:00 1891312 -LECTURE +MWF 0730-0850 LC 09/24 From 11:30 AM to 12:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -322,7 +322,7 @@ Repeat every week 2014-12-01T13:20:00-08:00 2014-12-01T13:20:00-08:00 1963852 -LECTURE +MWF 0730-0850 LC 09/24 From 12:30 PM to 01:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -400,7 +400,7 @@ Repeat every week 2014-12-01T13:20:00-08:00 2014-12-01T13:20:00-08:00 1963853 -LECTURE +MWF 0730-0850 LC 09/24 From 12:30 PM to 01:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -478,7 +478,7 @@ Repeat every week 2014-12-01T14:20:00-08:00 2014-12-01T14:20:00-08:00 1962793 -LECTURE +MWF 0730-0850 LC 09/24 From 01:30 PM to 02:20 PM. Starting on WED SEP/24/2014. Repeat every week @@ -556,7 +556,7 @@ Repeat every week 2014-12-01T15:20:00-08:00 2014-12-01T15:20:00-08:00 1965087 -LECTURE +MWF 0730-0850 LC 09/24 From 02:30 PM to 03:20 PM. Starting on WED SEP/24/2014. Repeat every week diff --git a/scheduler/schedule.py b/scheduler/schedule.py new file mode 100644 index 0000000..317c921 --- /dev/null +++ b/scheduler/schedule.py @@ -0,0 +1,351 @@ +# Copyright 2024 UW-IT, University of Washington +# SPDX-License-Identifier: Apache-2.0 + +# +# "schedule" refers to the combined result of a course offering's +# meeting times, the physical reservation system's locations for +# those meetings, and the space's panopto recorder's recording +# sessions. +# + +from django.conf import settings +from scheduler.course import Course +from scheduler.reservations import Reservations +from scheduler.utils import schedule_key +from scheduler.panopto.folder import ( + 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) +from scheduler.dao.panopto.access import get_session_access_details +from scheduler.exceptions import ( + MissingParamException, CourseReservationsException) +from restclients_core.exceptions import DataFailureException +from panopto_client import PanoptoAPIException +from dateutil import parser, tz +import datetime +import pytz +import logging + + +reservations = Reservations() +logger = logging.getLogger(__name__) + + +def course_location_and_recordings(course_id): + try: + course = Course(course_id) + event = reservations.get_event_by_course(course) + return course_recording_sessions(course, event) + except PanoptoAPIException as err: + raise CourseReservationsException( + "There was a problem connecting to the Panopto server. {}".format( + err)) + except MissingParamException as ex: + raise + except Exception as ex: + logger.exception(ex) + raise CourseReservationsException("Data Failure: {}".format(ex)) + + +def course_recording_sessions(course, event): + event_sessions = [] + recorders = {} + session_external_ids = [] + joint = [] + + if event.is_crosslisted: + course = course.get_crosslisted_course() + + folder = course.panopto_course_folder() + + for rsv in event.reservations: + if not rsv.is_instruction: + continue + + event_session = event_session_from_reservation(rsv) + + 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 + + space_id = rsv.space_id + + if space_id and space_id not in recorders: + recorders[space_id] = get_recorder_id_for_space_id(space_id) + + session_external_ids.append(external_id) + + 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) + + mash_in_panopto_sessions(event_sessions, session_external_ids, recorders) + + return event_sessions + + +def space_events_and_recordings(params): + search = { + 'space_id': params.get('space_id'), + 'start_dt': params.get('start_dt'), + 'session_ids': params.get('session_ids'), + } + + event_sessions = [] + event_external_ids = [] + recorders = { + search['space_id']: params.get('recorder_id') + } + + if search['session_ids']: + ids = search['session_ids'] if isinstance( + search['session_ids'], list) else search['session_ids'].split(',') + sessions = get_sessions_by_session_ids(ids) + + for s in sessions: + event_session = event_session_from_scheduled_recording(s) + event_sessions.append(event_session) + + return event_sessions + + if search['space_id'] and search['start_dt']: + try: + rsvs = reservations.get_reservations_by_search_params(search) + except DataFailureException as ex: + if ex.status == 404: + rsvs = [] + else: + raise + + # build event sessions, accounting for joint class reservations + for r in rsvs: + if r.is_course: + continue + + event_session = event_session_from_reservation(r) + + for s in event_sessions: + if event_session['recording'][ + 'start'] == s['recording']['start']: + if isinstance(s['name'], list): + s['name'].append(event_session['name']) + s['name'].sort() + else: + s['name'] = [s['name'], event_session['name']] + + event_session = None + break + + 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): + 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): + """ + 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': getattr(r, 'space_id'), + 'name': getattr(r, 'space_name'), + 'formal_name': getattr(r, 'space_formal_name'), + }, + 'recording': { + 'name': None, + 'id': None, + 'external_id': r.default_session_external_id(), + 'recorder_id': None, + 'start': start_utc.isoformat(), + 'end': end_utc.isoformat(), + 'folder': { + 'name': r.default_folder_name(), + 'id': None, + 'external_id': None + } + }, + 'contact': r.contact_info() + } + + +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)) + + session = { + 'profile': '', + 'name': s.Name, + 'schedulable': True, + 'space': { + 'id': None, + 'name': None, + 'formal_name': None + }, + 'recording': { + 'name': s.Name, + 'id': s.Id, + 'external_id': s.ExternalId, + 'recorder_id': s.RemoteRecorderIds.guid[0], + 'start': start_utc.isoformat(), + 'end': end_utc.isoformat(), + 'folder': { + 'name': s.FolderName, + 'id': s.FolderId, + 'external_id': None, + }, + 'is_broadcast': s.IsBroadcast, + 'is_public': False, + }, + 'contact': { + 'name': '', + 'loginid': '', + 'email': '', + }, + 'event': { + 'start': start_utc.isoformat(), + 'end': end_utc.isoformat(), + } + } + + session['key'] = schedule_key( + session['recording']['external_id'], + session['recording']['recorder_id'], + session['event']['start'], + session['event']['end']) + + return session + + +def mash_in_panopto_sessions(event_sessions, session_external_ids, recorders): + """ + overlay event details with any matching scheduled recordings + """ + session_access = {} + if len(session_external_ids): + sessions = get_sessions_by_external_ids(session_external_ids) + for session in sessions if sessions else []: + for e in event_sessions: + e_r = e['recording'] + + # only do the work of getting details if they're requested + if 'is_public' in e_r and session.Id not in session_access: + details = get_session_access_details(session.Id) + session_access[session.Id] = details + + if session.ExternalId == e_r['external_id']: + start_time = parser.parse(e_r['start']) + end_time = parser.parse(e_r['end']) + if not (start_time <= session.StartTime and + end_time >= session.StartTime): + continue + + e_r['recorder_id'] = session.RemoteRecorderIds.guid[0] if ( + hasattr(session.RemoteRecorderIds, 'guid')) else None + + # validate recorder id + e_r['relocated_recorder_id'] = None + if (e['space']['id'] and e['space']['id'] in recorders and + recorders[e['space']['id']] != e_r['recorder_id']): + e_r['relocated_recorder_id'] = recorders[ + e['space']['id']] + logger.info( + ('Stale session recorder id: {}, ' + 'session: "{}", ' + 'space id: {}, ' + 'new recorder id: {}').format( + e_r['recorder_id'], + session.ExternalId, + e['space']['id'], + e_r['relocated_recorder_id'])) + + e_r['id'] = session.Id + e_r['folder']['name'] = session.FolderName + e_r['folder']['id'] = session.FolderId + e_r['is_broadcast'] = session.IsBroadcast + if 'is_public' in e_r: + e_r['is_public'] = session_access[session.Id].IsPublic + + # actual recording start and duration + start_utc = session.StartTime.astimezone(pytz.utc) + end_utc = start_utc + \ + datetime.timedelta(seconds=int(session.Duration)) + e_r['start'] = start_utc.isoformat() + e_r['end'] = end_utc.isoformat() + + if 'auth' in e_r['folder']: + e_r['folder']['auth'] = { + 'creators': get_panopto_folder_creators( + session.FolderId) + } + + # fill in unscheduled event ids + for e in event_sessions: + e_r = e['recording'] + if (e['space']['id'] and not ( + 'recorder_id' in e_r and e_r['recorder_id'])): + space_id = e['space']['id'] + if not (space_id in recorders and recorders[space_id]): + recorders[space_id] = get_recorder_id_for_space_id(space_id) + + if recorders[space_id]: + e_r['recorder_id'] = recorders[space_id] + + e['key'] = schedule_key( + e_r['external_id'], e_r['recorder_id'], + e['event']['start'], e['event']['end']) + + +def get_recorder_id_for_space_id(space_id): + try: + return get_recorder_details(space_id)[0].Id + except Exception as ex: + logger.error("Cannot fetch recorder details for space {}: {}".format( + space_id, ex)) + pass + + return None diff --git a/scheduler/static/scheduler/css/mobile.less b/scheduler/static/scheduler/css/mobile.less index 3beda0c..b45fb98 100644 --- a/scheduler/static/scheduler/css/mobile.less +++ b/scheduler/static/scheduler/css/mobile.less @@ -414,6 +414,101 @@ span.loading { .visit-folder.hidden + span { display: inline-block !important; } + .placeholder { + font-style: italic; + } + .event-folder { + div.loading { font-size: 5em; height: 175px; padding-top: 40px; padding-left: 350px; } + .folder-list { + // collapsable cues + .folder[data-folder-level="1"] { padding-left: 8px; } + .folder[data-folder-level="2"] { padding-left: 16px; } + .folder[data-folder-level="3"] { padding-left: 24px; } + .folder[data-folder-level="4"] { padding-left: 32px; } + .folder[data-folder-level="5"] { padding-left: 40px; } + .folder[data-folder-level="6"] { padding-left: 48px; } + .folder[data-folder-level="7"] { padding-left: 56px; } + .folder[data-folder-level="8"] { padding-left: 64px; } + .folder[data-folder-level="9"] { padding-left: 72px; } + .folder[data-folder-level="10"] { padding-left: 80px; } + .folder[data-folder-level="11"] { padding-left: 88px; } + .folder[data-folder-level="12"] { padding-left: 96px; } + .folder.collapsable > i:hover { cursor: pointer; } + .folder:not(.root):has(+ .folder:not(.hidden)), + .folder.parent:not(.root):last-child { + i.opened { display: inline !important; } + } + .folder:not(.root):has(+ .folder.hidden) { i.closed { display: inline !important; }} + .folder.root:has(+ .folder.hidden):nth-child(1):after { + content: "..."; + font-weight: 900; + } + + // folder indenting + div.folder:not(.parent):not(.root) { + margin-left: 18px; + } + } + .folder-editor { + input.folder ~ .folder-search { + position: absolute; + margin-left: -1.75em; + line-height: 2.5em; + i:hover { + cursor: pointer; + } + } + input.folder ~ .folder-clear { + position: absolute; + margin-left: -1.75em; + line-height: 2.5em; + i:hover { + cursor: pointer; + } + } + > .folder-list { + padding-bottom: 5px; + } + .editor-options { + margin-top: 5px; + > a i { font-size: smaller; } + } + .folder-search-result { + height: 300px; + width: 625px; + background-color: white; + border: 1px solid @gray-light; + position: absolute; + z-index: 1000; + padding-left: 8px; + padding-top: 4px; + white-space: nowrap; + overflow: auto; + } + .folder-search-result ~ i { + display: none; + } + .folder-search-result.loading ~ i { + display: inline !important; + position: absolute; + z-index: 1001; + font-size: 5em; + margin-left: -350px; + margin-top: 150px; + } + .folder-search-result ~ span { + display: none; + } + .folder-search-result.no-result ~ span { + display: inline !important; + position: absolute; + z-index: 1001; + font-size: 1.3em; + line-height: 200px; + margin-left: -490px; + } + } + } div.container { margin-bottom: 16px; padding: 16px; width: 800px; div.row { margin-bottom: 10px; > div i { font-size: 20px; } @@ -425,28 +520,21 @@ span.loading { width: 90%; } } - .field-editor { - .field { - .placeholder { - font-style: italic; - display: none; + .form-group { + a.disabled { pointer-events: none; cursor: default; color: @gray-light; } + input { + width: 625px; + } + .clear-field, .field-search { + position: absolute; + margin-left: -20px; + margin-top: 7px; + cursor: pointer; + i { + font-size: 15px; + color: @gray-light; } } - .form-group { - input { - width: 700px; - } - .clear-field, .field-search { - position: absolute; - margin-left: -20px; - margin-top: 7px; - cursor: pointer; - i { - font-size: 15px; - color: @gray-light; - } - } - } } .creators { > span { @@ -515,15 +603,11 @@ span.loading { } } } + .schedule-event.loading + span, .modify-event.loading + span { + display: inline !important; + } .schedule-event + span, .modify-event + span { display: none; - position: absolute; - margin-top: 5px; - margin-left: -105px; - font-size: 30px; - } - .schedule-event:disabled + span, .modify-event:disabled + span { - display: inline !important; } .visit-folder.inactive { display: none; diff --git a/scheduler/static/scheduler/js/main.js b/scheduler/static/scheduler/js/main.js index 6e6b6a1..fa3ef7f 100644 --- a/scheduler/static/scheduler/js/main.js +++ b/scheduler/static/scheduler/js/main.js @@ -1,4 +1,4 @@ -/*jslint browser: true, plusplus: true */ +/*jslint browser: true, plusplus: true, scripturl: true */ /*global jQuery, Handlebars, moment, confirm */ var PanoptoScheduler = (function ($) { @@ -114,8 +114,7 @@ var PanoptoScheduler = (function ($) { function panopto_folder_url(panopto_folder_id) { return 'http://' + window.scheduler.panopto_server + - '/Panopto/Pages/Sessions/List.aspx#folderID=%22' + - panopto_folder_id + '%22&status=%5B4%2C0%2C5%2C3%2C2%2C1%5D'; + '/Panopto/Pages/Sessions/List.aspx#folderID=%22' + panopto_folder_id + '%22'; } function panopto_recording_url(panopto_recording_id) { @@ -369,8 +368,8 @@ var PanoptoScheduler = (function ($) { name: this.space.name, name_index: (this.space.name) ? context.rooms.indexOf(this.space.name) : null, space_id: this.space.id, - contact: this.contact.name, - contact_email: this.contact.email, + contact: this.contact.hasOwnProperty('name') ? this.contact.name : null, + contact_email: this.contact.hasOwnProperty('email') ? this.contact.email : null, recording_name: this.recording.name, recording_id: this.recording.id, recording_is_broadcast: this.recording.is_broadcast, @@ -436,10 +435,10 @@ var PanoptoScheduler = (function ($) { event_end_time: event_end_date.format('h:mm'), ampm: event_end_date.format('a'), name: ($.isArray(event.name)) ? event.name : [event.name], - contact: event.contact.name, - contact_email: event.contact.email, - contact_netids: (event.contact.uwnetid && event.contact.uwnetid.length) ? - [event.contact.uwnetid] : [], + contact: event.contact.hasOwnProperty('name') ? event.contact.name : null, + contact_email: event.contact.hasOwnProperty('email') ? event.contact.email : null, + contact_netids: (event.contact.hasOwnProperty('loginid') && event.contact.loginid && event.contact.loginid.length) ? + [event.contact.loginid] : [], recording_name: event.recording.name, recording_is_broadcast: event.recording.is_broadcast, recording_is_public: event.recording.is_public, @@ -535,6 +534,12 @@ var PanoptoScheduler = (function ($) { xhr); } + function validate_login_ids_failure(xhr) { + failure_modal('Error validating login ids', + 'Please try again.', + xhr); + } + function recorder_select_failure(xhr) { $(".event-search-result").empty(); failure_modal('Recorder Search Failure', @@ -768,6 +773,14 @@ var PanoptoScheduler = (function ($) { } } + function disable_event_schedule_button() { + $("button.schedule-event").attr('disabled', 'disabled'); + } + + function enable_event_schedule_button() { + $("button.schedule-event").removeAttr('disabled'); + } + function enable_event_scheduler(pe) { var $settings = $('.reservation-settings'); @@ -780,11 +793,13 @@ var PanoptoScheduler = (function ($) { function gather_event_recording($node, pe) { var changes = panopto_schedule_change(pe, $node), - folder_name = $node.find('.foldername .field a').text(), - fields = ['foldername', 'creators'], + $recording_name_input = $('.reservation-settings input.recording-name'), + recording_name = $recording_name_input.val().trim(), + $editor = $('.reservation-settings .folder-editor'), + $folder_input = $editor.find('input.folder'), + folder_name = $folder_input.attr('data-folder-name').trim(), + folder_id = $folder_input.attr('data-folder-id').trim(), creators = [], - $field, - $input, start, now, i; @@ -809,34 +824,24 @@ var PanoptoScheduler = (function ($) { } pe.recording.folder.external_id = ''; - if ($('.foldername .field a').text() === $('.foldername input.original-folder').val()) { - pe.recording.folder.id = $('.foldername input.folder-id').val(); + + if (recording_name.length) { + pe.recording.name = recording_name; } else { - pe.recording.folder.id = ''; + alert("Please provide recording name"); + return null; } - for (i = 0; i < fields.length; i += 1) { - $field = $node.find('.' + fields[i] + ' .form-group'); - if (!$field.hasClass('hidden')) { - panopto_event_field_editor_input_finish($field.find('input')); - } + if (folder_id.length && folder_id.length) { + pe.recording.folder.name = folder_name; + pe.recording.folder.id = folder_id; + } else { + alert("Please confirm that the recording's folder exists"); + return null; } - $node.find('ul li').each(function () { - var $li = $(this); - - if ($li.is(':visible') && !$li.hasClass('placeholder')) { - creators.push($li.text()); - } - }); - pe.recording.folder.creators = creators; - if (!pe.recording.folder.name || (pe.recording.folder.name !== folder_name)) { - pe.recording.folder.name = folder_name; - pe.recording.folder_id = null; - } - return pe; } @@ -941,8 +946,8 @@ var PanoptoScheduler = (function ($) { method = 'PUT'; } - if (panopto_event.contact.hasOwnProperty('uwnetid') && panopto_event.contact.uwnetid) { - request_data.uwnetid = panopto_event.contact.uwnetid; + if (panopto_event.contact.hasOwnProperty('loginid') && panopto_event.contact.loginid) { + request_data.loginid = panopto_event.contact.loginid; } if (now.isAfter(end_time)) { @@ -1011,9 +1016,9 @@ var PanoptoScheduler = (function ($) { type: 'DELETE', url: panopto_api_path('session/' + panopto_event.recording.id, { key: panopto_event.key, - uwnetid: (panopto_event.contact.hasOwnProperty('uwnetid') && - panopto_event.contact.uwnetid) ? - panopto_event.contact.uwnetid : '', + loginid: (panopto_event.contact.hasOwnProperty('loginid') && + panopto_event.contact.loginid) ? + panopto_event.contact.loginid : '', name: panopto_event.recording.name, eid: panopto_event.recording.external_id, rid: panopto_event.recording.recorder_id, @@ -1503,15 +1508,6 @@ var PanoptoScheduler = (function ($) { pe.slider = slider_instance; - $('.reservation-settings .foldername .folder-picker .search input').data( - 'finish', search_panopto_folders); - - $('.reservation-settings .foldername .form-group > input').data( - 'finish', validate_new_folder); - - $('.reservation-settings .creators .form-group > input').data( - 'finish', panopto_folder_string_to_creators); - slider_instance.on('slideStop', function (e) { var v = slider_instance.slider('getValue'), start = parseInt(v[0], 10), @@ -1528,8 +1524,9 @@ var PanoptoScheduler = (function ($) { checked = (pe && pe.recording.id && pe.recording.is_broadcast) ? '1' : '0'; $('input[name^=webcast_][value="' + checked + '"]').prop('checked', true); + disable_event_scheduler(); - validate_panopto_folder($('.original-folder').val()); + display_event_folder(pe.recording.folder.name, pe.recording.folder.id, ''); } function event_scheduler_cancel(e) { @@ -1560,290 +1557,315 @@ var PanoptoScheduler = (function ($) { } } - function search_panopto_folders(substring) { - var term = substring.trim(); + function display_event_folder(folder, folder_id, parent_folder_id) { + var $folder_div = $(".reservation-settings .event-folder"), + tpl = Handlebars.compile($('#event-folder-tmpl').html()), + context = { + path: [], + folders: [{ + name: folder, + id: folder_id, + parent_folder_id: parent_folder_id + }], + folder: { + name: folder, + id: folder_id, + parent_folder_id: parent_folder_id + }, + creator_netids: [] + }, + found_folder = []; - if (term.length > 3) { - search_in_progress('.folder-picker .result'); - do_panopto_folder_search(term, search_panopto_folders_complete); - } + enable_event_scheduler(); + $('button.schedule-event').removeClass('loading'); + $folder_div.html(tpl(context)); + update_event_editor_cues(); + disable_event_schedule_button(); } - function validate_new_folder(substring) { - var folder = substring.trim(); - - if (folder.length < 1 || folder === $('.foldername .field a').text()) { - return; + /* flatten out the returned folder structure */ + function panopto_folders_from_search(folder_data, folder_list, parent_count) { + if (typeof parent_count === 'undefined') { + parent_count = 0; } - validate_panopto_folder(folder); - } - - function validate_panopto_folder(folder) { - /*jshint validthis: true */ - var creators; - - $('.foldername .field a').text(folder); - panopto_folder_string_to_creators(); - close_panopto_event_field_editors(); + for (var i = 0; i < folder_data.length; i += 1) { + var folder = folder_data[i]; - $('.foldername input.original-folder').val(''); - $('.foldername input.folder-id').val(''); - $('a.visit-folder').addClass('hidden'); - - disable_event_scheduler(); - - // fix up creators and links - $('.folder-exists').addClass('hidden'); - $('a.visit-folder').attr('disabled', 'disabled'); - do_panopto_folder_search(folder, function (data) { - enable_event_scheduler(); - if ($.isArray(data)) { - $('button.modify-event').removeAttr('disabled'); - $.each(data, function () { - if (this.name === folder) { - $('.folder-exists').removeClass('hidden'); - creators = []; - $.each(this.auth.creators, function () { - creators.push(this.key); - }); - - panopto_folder_string_to_creators(creators.join(',')); - $('.foldername input.original-folder').val(this.name); - $('.foldername input.folder-id').val(this.id); - $('a.visit-folder'). - attr('href', panopto_folder_url(this.id)). - removeClass('hidden'); - return false; - } + if (folder.hasOwnProperty('name')) { + folder_list.push({ + name: folder.name.trim(), + id: folder.id, + parent_folder_id: folder.parent_folder_id, + parent_count: parent_count, + child_count: folder.child_folders.length }); + + panopto_folders_from_search(folder.child_folders, folder_list, parent_count + 1); } - }); + } } - function do_panopto_folder_search(folder, finished) { + function do_panopto_folder_search(folder, parent_folder_id, finished) { $.ajax({ type: 'GET', - url: panopto_api_path('folder/', { search: folder }) + url: panopto_api_path('folder/', { + search: folder, + parent_folder_id: (parent_folder_id) ? parent_folder_id : '' + }) + }) + .fail(function (jqXHR, textStatus) { + alert('Folder search failed: ' + (jqXHR.responseJSON ? jqXHR.responseJSON.error : jqXHR.responseText)); + display_event_folder(folder, '', parent_folder_id); + }) + .done(finished); + } + + function do_panopto_folder_create(folder_name, parent_folder_id, finished) { + $.ajax({ + type: 'POST', + url: panopto_api_path('folder/'), + contentType: 'application/json', + data: JSON.stringify({ + 'folder_name': folder_name, + 'parent_folder_id': (parent_folder_id) ? parent_folder_id : '' + })//, + //waitIndicatator: event_folder_search_in_progress, + //complete: event_folder_search_complete }) - .fail(function () { - $('.folder-picker .result').empty(); + .fail(function (jqXHR, textStatus) { + alert('Folder create failed: ' + (jqXHR.responseJSON ? jqXHR.responseJSON.error : jqXHR.responseText)); }) .done(finished); } - function search_panopto_folders_complete(data) { - var html = '', - $result = $('.folder-picker .result'), - $div, - creators; + function panopto_event_folder_search(e) { + var $form = $('.event-folder .form-group'), + $result = $('.folder-search-result', $form), + $input = $('.folder-editor input.folder', $form), + folder = $input.val().trim(), + parent_folder_id = $input.attr('data-parent-folder-id'); - if ($.isArray(data)) { - $result.empty(); - $.each(data, function () { - $div = $('
'); - $div.html(this.name); - $div.attr('data-folder-id', this.id); + if (folder.length <= 4) { + alert('Search term must be at least 4 characters long'); + return; + } - creators = []; - $.each(this.auth.creators, function () { - creators.push(this.key); - }); + $result.empty().addClass('loading').removeClass('hidden no-result'); + do_panopto_folder_search(folder, parent_folder_id, function (data) { + var tpl = Handlebars.compile($('#folder-list-tmpl').html()), + folder_list = []; - if (creators.length) { - $div.attr('data-folder-creators', creators.join(',')); - } + $result.removeClass('loading'); - $result.append($div); - }); - } + if (data.length == 0) { + $result.addClass('no-result'); + return; + } + + panopto_folders_from_search(data, folder_list); + $result.append(tpl({folder_list: folder_list})); + }); } - function select_panopto_folder() { - /*jshint validthis: true */ - var $this = $(this), - text = $this.text(), - folder_id = $this.attr('data-folder-id'), - creators = $this.attr('data-folder-creators'); + function panopto_event_folder_create(e) { + var $form = $('.event-folder .form-group'), + $editor = $('.reservation-settings .folder-editor'), + $input = $editor.find('input.folder'), + folder_name = $input.val().trim(), + parent_folder_id = $input.attr('data-parent-folder-id'); - $('.foldername .field a').text(text); - $('.foldername input.original-folder').val(text); - $('.foldername input.folder-id').val(folder_id); - $('a.visit-folder').attr('href', panopto_folder_url(folder_id)); - panopto_folder_string_to_creators(($.type(creators) === 'string') ? creators : ''); - close_panopto_event_field_editors(); - } + if (folder_name.length > 0) { + do_panopto_folder_create(folder_name, parent_folder_id, function (data) { + var folder_list_tpl = Handlebars.compile($('#folder-list-tmpl').html()), + folder_list = [], + folder; - function open_panopto_folder_search() { - if ($('.folder-picker').hasClass('hidden')) { - $('.folder-picker .search span').addClass('hidden'); - $('.folder-picker .search input').val(''); - $('.folder-picker .result').empty(); - } + panopto_folders_from_search(data, folder_list); - $('.folder-picker').removeClass('hidden'); - $('.folder-picker input').focus(); - } + folder = folder_list[folder_list.length - 1]; - function close_panopto_folder_search() { - $('.folder-picker').addClass('hidden'); + display_event_folder(folder.name, folder.id, folder.parent_folder_id); + + $('.event-folder .folder-details .folder-editor > .folder-list') + .append(folder_list_tpl({folder_list: folder_list.slice(0, -1)})); + update_event_editor_cues(); + }); + } else { + alert('Folder name required'); + } } - function clear_panopto_folder_search_input() { - /*jshint validthis: true */ - var $input = $('.folder-picker .search input'); - $input.val(''); - $(this).addClass('hidden'); - $input.focus(); + function panopto_event_folder_clear(e) { + var $form = $('.event-folder .form-group'), + $result = $('.folder-search-result', $form), + $input = $('.folder-editor input.folder', $form), + $folder_path = $('.folder-editor .folder-path', $form); + + $result.empty().addClass('hidden'); + update_event_editor_cues(); + $input.val('') + .attr('placeholder', 'Search for or create folder name (at least 8 chars)') + .attr('data-parent-folder-id', ''); + $folder_path.empty(); } - function open_panopto_event_field_editor(e) { + function select_panopto_event_folder_from_search(e) { /*jshint validthis: true */ - var $field = $(this).closest('.field'), - $a = $field.find('a'), - $child = $a.find('> :first-child'), - $edit = $field.next(), - $input = $edit.find('input'); + var $a = $(this), + folder_name = $a.text().trim(), + folder_id = $a.attr('data-folder-id'), + parent_folder_id = $a.attr('data-folder-parent-id'), + $folder = $a.parent(), + $editor_input = $a.closest('.folder-editor').find('input.folder'), + $editor_folder_list = $('.event-folder .folder-details .folder-editor > .folder-list'), + $root = $folder.prev('.folder.root'); e.preventDefault(); e.stopPropagation(); - $field.parent().trigger('scheduler.field_edit_opening'); + // put folder name in editor + if ($folder.hasClass('parent')) { + $editor_input + .val('') + .attr('data-folder-name', '') + .attr('data-folder-id', '') + .attr('data-parent-folder-id', folder_id); + } else { + $editor_input + .val(folder_name) + .attr('data-folder-name', folder_name) + .attr('data-folder-id', folder_id) + .attr('data-parent-folder-id', parent_folder_id); + } + + // add path to editor + $editor_folder_list.empty(); + if (!$folder.hasClass('root')) { + $folder.prevUntil($root, '.folder.parent').each(function () { + var $folder = $(this).clone(); + + if ($folder.hasClass('root')) { + $folder + .addClass('collapsable') + .removeClass('hidden'); + } else { + $folder + .removeClass('collapsable') + .addClass('hidden'); + } - if ($child.length) { - if ($child.is('ul')) { - var values = []; + $editor_folder_list.prepend($folder); + }); + } - $child.find('li').each(function () { - var $li = $(this); + update_event_editor_cues(); + close_panopto_folder_search_result(); + } - if ($li.is(':visible') && !$li.hasClass('placeholder')) { - values.push($li.text()); - } - }); + function select_panopto_event_folder_from_editor(e) { + /*jshint validthis: true */ + var $a = $(this), + folder_name = $a.text().trim(), + folder_id = $a.attr('data-folder-id'), + parent_folder_id = $a.attr('data-folder-parent-id'), + $folder = $a.parent(), + $editor_input = $a.closest('.folder-editor').find('input.folder'), + $root = $folder.prev('.folder.root'); - $input.val(values.join(', ')); - } - } else { - $input.val($a.text()); - } + e.preventDefault(); + e.stopPropagation(); - $field.addClass('hidden'); - $edit.removeClass('hidden'); - $input.focus(); + $editor_input + .val('') + .attr('data-folder-name', '') + .attr('data-folder-id', '') + .attr('data-parent-folder-id', folder_id); + + $folder.nextUntil($('.folder.root'), '.folder').remove(); + update_event_editor_cues(); } - function valid_netid(s) { - var valid = s.toLowerCase().trim().match(/^([a-z][a-z0-9_]+)(@(uw|washington|u\.washington).edu)?$/); + function close_panopto_folder_search_result() { + var $editor = $('.event-folder .form-group .folder-editor'); - return (valid) ? valid[1] : null; + $('.folder-search-result', $editor) + .addClass('hidden') + .removeClass('loading no-result') + .empty(); + $('input.folder', $editor).focus(); } - function panopto_folder_string_to_creators(val) { - var values = (val && val.length) ? val.split(/[ ,]+/) : [], - $ul = $('.creators .field ul'), - $li = $ul.find('li'), - $group = $('.creators .form-group'), - netids = []; + function collapse_expand_event_folder_path_element(e) { + /*jshint validthis: true */ + var $i = $(this), + $folder = $i.parent('.folder'), + expand = $folder.next().hasClass('hidden'), + level = $folder.attr('data-folder-level'); - // UWNetid\netid - $.each(values, function () { - var netid = valid_netid(this); - if (netid) { - if (netids.indexOf(netid) < 0) { - netids.push(netid); + $folder.nextAll().each(function () { + var $this = $(this); + + if ($this.attr('data-folder-level') > level) { + if (expand) { + $this.removeClass('hidden'); + } else { + $this.addClass('hidden'); } } else { - $group.addClass('has-error'); return false; } - - return true; - }); - - if ($group.hasClass('has-error')) { - return; - } - - if ($li.length > 1) { - var x = $li.slice($li.length - 1), - $newul = $('
    ').append($li.slice($li.length - 1)); - - $ul.replaceWith($newul); - $ul = $('.creators .field ul'); - } - - $.each(netids, function () { - $li = $('
  • '); - $li.text(this); - $ul.find('li:nth-last-child(1)').before($li); }); - - close_panopto_event_field_editors(); } - function panopto_event_field_editor_input_finish($input) { - var $field = $input.parent().prev('.field'), - val = $input.val().trim(), - finish = $input.data('finish'); - - if (finish && $.isFunction(finish)) { - finish(val); + function update_event_editor_cues() { + var $recording_name_input = $('.reservation-settings input.recording-name'), + recording_name = $recording_name_input.val().trim(), + $editor = $('.reservation-settings .folder-editor'), + $folder_input = $editor.find('input.folder'), + folder_name = $folder_input.val().trim(), + folder_id = $folder_input.attr('data-folder-id'), + parent_folder_id = $folder_input.attr('data-parent-folder-id'), + $create_link = $editor.find('a.event-folder-create'), + $visit_link = $editor.find('a.event-folder-visit'), + $search_link = $editor.find('span.folder-search'), + parent_folder_name = parent_folder_id ? $('.folder > a[data-folder-id="' + parent_folder_id + '"]').first().text() : "top level directory"; + + $folder_input + .attr('placeholder', 'Create or Search in "' + parent_folder_name.trim() + '"') + .attr('data-parent-folder-id', parent_folder_id); + $search_link.attr('title', 'Search for folder in ' + parent_folder_name); + $create_link.text('Create folder in ' + parent_folder_name); + + disable_event_schedule_button(); + if (folder_name.length) { + if (folder_name == $folder_input.attr('data-folder-name')) { + if (folder_id.length) { + $create_link.addClass('hidden'); + $visit_link.removeClass('hidden').attr('href', panopto_folder_url(folder_id)); + + if (recording_name.length) { + enable_event_schedule_button(); + } + } else { + $create_link.removeClass('hidden'); + $visit_link.addClass('hidden'); + } + } else { + $create_link.removeClass('hidden disabled'); + $visit_link.addClass('hidden'); + } } else { - $field.find('a').text(val); - close_panopto_event_field_editors(); + $create_link.addClass('disabled').removeClass('hidden'); + $visit_link.addClass('hidden'); } } - function panopto_event_field_editor_input(e) { - /*jshint validthis: true */ - var $input = $(this); - + function panopto_event_editor_input(e) { e.preventDefault(); e.stopPropagation(); - if (e.keyCode === 13) { - panopto_event_field_editor_input_finish($input); - } else if (e.keyCode === 27) { - close_panopto_event_field_editors(); - } else { - $input.parent('.form-group').removeClass('has-error'); - $input.next('.clear-field').removeClass('hidden'); - } - } - - function panopto_event_field_editor_input_clear(e) { - /*jshint validthis: true */ - var $this = $(this), - $input = $this.prev(); - - $this.closest('.field-editor').trigger('scheduler.field_edit_clearing'); - $input.val(''); - $this.addClass('hidden'); - $input.focus(); - } - - function close_and_save_panopto_event_field_editors() { - var $shown = $('.field-editor .form-group:not(.hidden)'); - - if ($shown.length) { - panopto_event_field_editor_input_finish( - $shown.parent().find('.form-group > input')); - } - - close_panopto_event_field_editors(); - } - - function close_panopto_event_field_editors() { - var $hidden = $('.field-editor .field.hidden'), - $fields; - - if ($hidden.length) { - $fields = $hidden.parent(); - $fields.trigger('scheduler.field_edit_closing'); - $fields.find('.form-group').addClass('hidden').removeClass('has-error'); - $fields.find('.field').removeClass('hidden'); - } + update_event_editor_cues(); } function init_slider_range(event_start, event_end, rec_start, rec_end) { @@ -2173,30 +2195,32 @@ var PanoptoScheduler = (function ($) { event_scheduler) .on('click', '.reservation-settings .cancel', event_scheduler_cancel) - .on('click', '.reservation-settings .field a', - open_panopto_event_field_editor) - .on('keyup', '.reservation-settings .foldername .form-group > input', - panopto_event_field_editor_input) - .on('scheduler.field_edit_closing', '.reservation-settings .foldername', - function () { - $('.foldername .form-group .folder-picker').addClass('hidden'); - }) - .on('click', '.reservation-settings .foldername .open-folder-picker', - open_panopto_folder_search) - .on('click', '.reservation-settings .foldername .folder-picker .close', - close_panopto_folder_search) - .on('click', '.reservation-settings .foldername .folder-picker .result div', - select_panopto_folder) - .on('keyup', '.reservation-settings .foldername .folder-picker .search input', - panopto_event_field_editor_input) - .on('keyup', '.reservation-settings .creators .form-group > input', - panopto_event_field_editor_input) - .on('click', '.reservation-settings .field-editor .form-group .clear-field', - panopto_event_field_editor_input_clear) - .on('scheduler.field_edit_opening', '.reservation-settings', function () { - close_and_save_panopto_event_field_editors(); - //close_panopto_event_field_editors(); + .on('mouseup', function(e) { + var $result = $('.event-folder .form-group .folder-editor .folder-search-result'); + + if (!($result.hasClass('hidden') || $(e.target).closest('.folder-search-result').length)) { + close_panopto_folder_search_result(); + } + }) + .on('click', '.reservation-settings .folder-editor .event-folder-create', + panopto_event_folder_create) + .on('keyup', '.reservation-settings input.recording-name', + panopto_event_editor_input) + .on('keyup', '.reservation-settings .folder-editor input.folder', function (e) { + if (e.which === 13) { + panopto_event_folder_search(e); + } else { + panopto_event_editor_input(e); + } }) + .on('click', '.reservation-settings .folder-editor input.folder ~ .folder-search', + panopto_event_folder_search) + .on('click', '.reservation-settings .folder-editor input.folder ~ .folder-clear', + panopto_event_folder_clear) + .on('click', '.reservation-settings .folder-editor .folder-search-result .folder a', + select_panopto_event_folder_from_search) + .on('click', '.reservation-settings .folder-editor > .folder-list .folder a', + select_panopto_event_folder_from_editor) .on('scheduler.field_edit_clearing', '.foldername', function () { $('.reservation-settings .foldername .folder-picker .result').html(''); @@ -2223,10 +2247,18 @@ var PanoptoScheduler = (function ($) { }) .on('click', '.modify-event', modify_panopto_event_recording) - .on('click', function () { - close_and_save_panopto_event_field_editors(); - //close_panopto_event_field_editors(); - }); + .on('click', '.folder-list .folder.parent.collapsable > i', + collapse_expand_event_folder_path_element); + + Handlebars.registerHelper({ + 'indent': function (value) { return value * 8; }, + 'eq': function(a, b) { return (a === b); }, + 'gt': function(a, b) { return (a > b); }, + 'lte': function(a, b) { return (a <= b); }, + 'and': function(a, b) { return (a && b); }, + 'or': function(a, b) { return (a || b); }, + 'not': function(a) { return (!a); } + }); } function initialize() { @@ -2277,8 +2309,6 @@ var PanoptoScheduler = (function ($) { find_event_from_search(search); }); } - - //history.replaceState({}, '', search_param ? search_param : '/scheduler/'); } else { // if blti loaded if (window.scheduler.hasOwnProperty('blti')) { diff --git a/scheduler/static/vendor/js/handlebars-4.7.8.min.js b/scheduler/static/vendor/js/handlebars-4.7.8.min.js new file mode 100644 index 0000000..e9b863f --- /dev/null +++ b/scheduler/static/vendor/js/handlebars-4.7.8.min.js @@ -0,0 +1,29 @@ +/**! + + @license + handlebars v4.7.8 + +Copyright (C) 2011-2019 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +!function(a,b){"object"==typeof exports&&"object"==typeof module?module.exports=b():"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?exports.Handlebars=b():a.Handlebars=b()}(this,function(){return function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={exports:{},id:d,loaded:!1};return a[d].call(e.exports,e,e.exports,b),e.loaded=!0,e.exports}var c={};return b.m=a,b.c=c,b.p="",b(0)}([function(a,b,c){"use strict";function d(){var a=r();return a.compile=function(b,c){return k.compile(b,c,a)},a.precompile=function(b,c){return k.precompile(b,c,a)},a.AST=i["default"],a.Compiler=k.Compiler,a.JavaScriptCompiler=m["default"],a.Parser=j.parser,a.parse=j.parse,a.parseWithoutProcessing=j.parseWithoutProcessing,a}var e=c(1)["default"];b.__esModule=!0;var f=c(2),g=e(f),h=c(84),i=e(h),j=c(85),k=c(90),l=c(91),m=e(l),n=c(88),o=e(n),p=c(83),q=e(p),r=g["default"].create,s=d();s.create=d,q["default"](s),s.Visitor=o["default"],s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){return a&&a.__esModule?a:{"default":a}},b.__esModule=!0},function(a,b,c){"use strict";function d(){var a=new h.HandlebarsEnvironment;return n.extend(a,h),a.SafeString=j["default"],a.Exception=l["default"],a.Utils=n,a.escapeExpression=n.escapeExpression,a.VM=p,a.template=function(b){return p.template(b,a)},a}var e=c(3)["default"],f=c(1)["default"];b.__esModule=!0;var g=c(4),h=e(g),i=c(77),j=f(i),k=c(6),l=f(k),m=c(5),n=e(m),o=c(78),p=e(o),q=c(83),r=f(q),s=d();s.create=d,r["default"](s),s["default"]=s,b["default"]=s,a.exports=b["default"]},function(a,b){"use strict";b["default"]=function(a){if(a&&a.__esModule)return a;var b={};if(null!=a)for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&(b[c]=a[c]);return b["default"]=a,b},b.__esModule=!0},function(a,b,c){"use strict";function d(a,b,c){this.helpers=a||{},this.partials=b||{},this.decorators=c||{},i.registerDefaultHelpers(this),j.registerDefaultDecorators(this)}var e=c(1)["default"];b.__esModule=!0,b.HandlebarsEnvironment=d;var f=c(5),g=c(6),h=e(g),i=c(10),j=c(70),k=c(72),l=e(k),m=c(73),n="4.7.8";b.VERSION=n;var o=8;b.COMPILER_REVISION=o;var p=7;b.LAST_COMPATIBLE_COMPILER_REVISION=p;var q={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};b.REVISION_CHANGES=q;var r="[object Object]";d.prototype={constructor:d,logger:l["default"],log:l["default"].log,registerHelper:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple helpers");f.extend(this.helpers,a)}else this.helpers[a]=b},unregisterHelper:function(a){delete this.helpers[a]},registerPartial:function(a,b){if(f.toString.call(a)===r)f.extend(this.partials,a);else{if("undefined"==typeof b)throw new h["default"]('Attempting to register a partial called "'+a+'" as undefined');this.partials[a]=b}},unregisterPartial:function(a){delete this.partials[a]},registerDecorator:function(a,b){if(f.toString.call(a)===r){if(b)throw new h["default"]("Arg not supported with multiple decorators");f.extend(this.decorators,a)}else this.decorators[a]=b},unregisterDecorator:function(a){delete this.decorators[a]},resetLoggedPropertyAccesses:function(){m.resetLoggedProperties()}};var s=l["default"].log;b.log=s,b.createFrame=f.createFrame,b.logger=l["default"]},function(a,b){"use strict";function c(a){return k[a]}function d(a){for(var b=1;b":">",'"':""","'":"'","`":"`","=":"="},l=/[&<>"'`=]/g,m=/[&<>"'`=]/,n=Object.prototype.toString;b.toString=n;var o=function(a){return"function"==typeof a};o(/x/)&&(b.isFunction=o=function(a){return"function"==typeof a&&"[object Function]"===n.call(a)}),b.isFunction=o;var p=Array.isArray||function(a){return!(!a||"object"!=typeof a)&&"[object Array]"===n.call(a)};b.isArray=p},function(a,b,c){"use strict";function d(a,b){var c=b&&b.loc,g=void 0,h=void 0,i=void 0,j=void 0;c&&(g=c.start.line,h=c.end.line,i=c.start.column,j=c.end.column,a+=" - "+g+":"+i);for(var k=Error.prototype.constructor.call(this,a),l=0;l0?(c.ids&&(c.ids=[c.name]),a.helpers.each(b,c)):e(this);if(c.data&&c.ids){var g=d.createFrame(c.data);g.contextPath=d.appendContextPath(c.data.contextPath,c.name),c={data:g}}return f(b,c)})},a.exports=b["default"]},function(a,b,c){"use strict";var d=c(13)["default"],e=c(43)["default"],f=c(55)["default"],g=c(60)["default"],h=c(1)["default"];b.__esModule=!0;var i=c(5),j=c(6),k=h(j);b["default"]=function(a){a.registerHelper("each",function(a,b){function c(b,c,d){n&&(n.key=b,n.index=c,n.first=0===c,n.last=!!d,o&&(n.contextPath=o+b)),m+=h(a[b],{data:n,blockParams:i.blockParams([a[b],b],[o+b,null])})}if(!b)throw new k["default"]("Must pass iterator to #each");var h=b.fn,j=b.inverse,l=0,m="",n=void 0,o=void 0;if(b.data&&b.ids&&(o=i.appendContextPath(b.data.contextPath,b.ids[0])+"."),i.isFunction(a)&&(a=a.call(this)),b.data&&(n=i.createFrame(b.data)),a&&"object"==typeof a)if(i.isArray(a))for(var p=a.length;le;)M(a,c=d[e++],b[c]);return a},O=function(a,b){return void 0===b?x(a):N(x(a),b)},P=function(a){var b=E.call(this,a);return!(b||!f(this,a)||!f(G,a)||f(this,D)&&this[D][a])||b},Q=function(a,b){var c=v(a=t(a),b);return!c||!f(G,b)||f(a,D)&&a[D][b]||(c.enumerable=!0),c},R=function(a){for(var b,c=y(t(a)),d=[],e=0;c.length>e;)f(G,b=c[e++])||b==D||d.push(b);return d},S=function(a){for(var b,c=y(t(a)),d=[],e=0;c.length>e;)f(G,b=c[e++])&&d.push(G[b]);return d},T=function(a){if(void 0!==a&&!L(a)){for(var b,c,d=[a],e=1,f=arguments;f.length>e;)d.push(f[e++]);return b=d[1],"function"==typeof b&&(c=b),!c&&r(b)||(b=function(a,b){if(c&&(b=c.call(this,a,b)),!L(b))return b}),d[1]=b,B.apply(A,d)}},U=j(function(){var a=z();return"[null]"!=B([a])||"{}"!=B({a:a})||"{}"!=B(Object(a))});H||(z=function(){if(L(this))throw TypeError("Symbol is not a constructor");return K(m(arguments.length>0?arguments[0]:void 0))},i(z.prototype,"toString",function(){return this._k}),L=function(a){return a instanceof z},d.create=O,d.isEnum=P,d.getDesc=Q,d.setDesc=M,d.setDescs=N,d.getNames=p.get=R,d.getSymbols=S,g&&!c(41)&&i(I,"propertyIsEnumerable",P,!0));var V={"for":function(a){return f(F,a+="")?F[a]:F[a]=z(a)},keyFor:function(a){return o(F,a)},useSetter:function(){C=!0},useSimple:function(){C=!1}};d.each.call("hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),function(a){var b=n(a);V[a]=H?b:K(b)}),C=!0,h(h.G+h.W,{Symbol:z}),h(h.S,"Symbol",V),h(h.S+h.F*!H,"Object",{create:O,defineProperty:M,defineProperties:N,getOwnPropertyDescriptor:Q,getOwnPropertyNames:R,getOwnPropertySymbols:S}),A&&h(h.S+h.F*(!H||U),"JSON",{stringify:T}),l(z,"Symbol"),l(Math,"Math",!0),l(e.JSON,"JSON",!0)},function(a,b){var c=a.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=c)},function(a,b){var c={}.hasOwnProperty;a.exports=function(a,b){return c.call(a,b)}},function(a,b,c){a.exports=!c(19)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(a,b){a.exports=function(a){try{return!!a()}catch(b){return!0}}},function(a,b,c){var d=c(16),e=c(21),f=c(22),g="prototype",h=function(a,b,c){var i,j,k,l=a&h.F,m=a&h.G,n=a&h.S,o=a&h.P,p=a&h.B,q=a&h.W,r=m?e:e[b]||(e[b]={}),s=m?d:n?d[b]:(d[b]||{})[g];m&&(c=b);for(i in c)j=!l&&s&&i in s,j&&i in r||(k=j?s[i]:c[i],r[i]=m&&"function"!=typeof s[i]?c[i]:p&&j?f(k,d):q&&s[i]==k?function(a){var b=function(b){return this instanceof a?new a(b):a(b)};return b[g]=a[g],b}(k):o&&"function"==typeof k?f(Function.call,k):k,o&&((r[g]||(r[g]={}))[i]=k))};h.F=1,h.G=2,h.S=4,h.P=8,h.B=16,h.W=32,a.exports=h},function(a,b){var c=a.exports={version:"1.2.6"};"number"==typeof __e&&(__e=c)},function(a,b,c){var d=c(23);a.exports=function(a,b,c){if(d(a),void 0===b)return a;switch(c){case 1:return function(c){return a.call(b,c)};case 2:return function(c,d){return a.call(b,c,d)};case 3:return function(c,d,e){return a.call(b,c,d,e)}}return function(){return a.apply(b,arguments)}}},function(a,b){a.exports=function(a){if("function"!=typeof a)throw TypeError(a+" is not a function!");return a}},function(a,b,c){a.exports=c(25)},function(a,b,c){var d=c(9),e=c(26);a.exports=c(18)?function(a,b,c){return d.setDesc(a,b,e(1,c))}:function(a,b,c){return a[b]=c,a}},function(a,b){a.exports=function(a,b){return{enumerable:!(1&a),configurable:!(2&a),writable:!(4&a),value:b}}},function(a,b,c){var d=c(16),e="__core-js_shared__",f=d[e]||(d[e]={});a.exports=function(a){return f[a]||(f[a]={})}},function(a,b,c){var d=c(9).setDesc,e=c(17),f=c(29)("toStringTag");a.exports=function(a,b,c){a&&!e(a=c?a:a.prototype,f)&&d(a,f,{configurable:!0,value:b})}},function(a,b,c){var d=c(27)("wks"),e=c(30),f=c(16).Symbol;a.exports=function(a){return d[a]||(d[a]=f&&f[a]||(f||e)("Symbol."+a))}},function(a,b){var c=0,d=Math.random();a.exports=function(a){return"Symbol(".concat(void 0===a?"":a,")_",(++c+d).toString(36))}},function(a,b,c){var d=c(9),e=c(32);a.exports=function(a,b){for(var c,f=e(a),g=d.getKeys(f),h=g.length,i=0;h>i;)if(f[c=g[i++]]===b)return c}},function(a,b,c){var d=c(33),e=c(35);a.exports=function(a){return d(e(a))}},function(a,b,c){var d=c(34);a.exports=Object("z").propertyIsEnumerable(0)?Object:function(a){return"String"==d(a)?a.split(""):Object(a)}},function(a,b){var c={}.toString;a.exports=function(a){return c.call(a).slice(8,-1)}},function(a,b){a.exports=function(a){if(void 0==a)throw TypeError("Can't call method on "+a);return a}},function(a,b,c){var d=c(32),e=c(9).getNames,f={}.toString,g="object"==typeof window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],h=function(a){try{return e(a)}catch(b){return g.slice()}};a.exports.get=function(a){return g&&"[object Window]"==f.call(a)?h(a):e(d(a))}},function(a,b,c){var d=c(9);a.exports=function(a){var b=d.getKeys(a),c=d.getSymbols;if(c)for(var e,f=c(a),g=d.isEnum,h=0;f.length>h;)g.call(a,e=f[h++])&&b.push(e);return b}},function(a,b,c){var d=c(34);a.exports=Array.isArray||function(a){return"Array"==d(a)}},function(a,b,c){var d=c(40);a.exports=function(a){if(!d(a))throw TypeError(a+" is not an object!");return a}},function(a,b){a.exports=function(a){return"object"==typeof a?null!==a:"function"==typeof a}},function(a,b){a.exports=!0},function(a,b){},function(a,b,c){a.exports={"default":c(44),__esModule:!0}},function(a,b,c){c(45),c(51),a.exports=c(29)("iterator")},function(a,b,c){"use strict";var d=c(46)(!0);c(48)(String,"String",function(a){this._t=String(a),this._i=0},function(){var a,b=this._t,c=this._i;return c>=b.length?{value:void 0,done:!0}:(a=d(b,c),this._i+=a.length,{value:a,done:!1})})},function(a,b,c){var d=c(47),e=c(35);a.exports=function(a){return function(b,c){var f,g,h=String(e(b)),i=d(c),j=h.length;return i<0||i>=j?a?"":void 0:(f=h.charCodeAt(i),f<55296||f>56319||i+1===j||(g=h.charCodeAt(i+1))<56320||g>57343?a?h.charAt(i):f:a?h.slice(i,i+2):(f-55296<<10)+(g-56320)+65536)}}},function(a,b){var c=Math.ceil,d=Math.floor;a.exports=function(a){return isNaN(a=+a)?0:(a>0?d:c)(a)}},function(a,b,c){"use strict";var d=c(41),e=c(20),f=c(24),g=c(25),h=c(17),i=c(49),j=c(50),k=c(28),l=c(9).getProto,m=c(29)("iterator"),n=!([].keys&&"next"in[].keys()),o="@@iterator",p="keys",q="values",r=function(){return this};a.exports=function(a,b,c,s,t,u,v){j(c,b,s);var w,x,y=function(a){if(!n&&a in C)return C[a];switch(a){case p:return function(){return new c(this,a)};case q:return function(){return new c(this,a)}}return function(){return new c(this,a)}},z=b+" Iterator",A=t==q,B=!1,C=a.prototype,D=C[m]||C[o]||t&&C[t],E=D||y(t);if(D){var F=l(E.call(new a));k(F,z,!0),!d&&h(C,o)&&g(F,m,r),A&&D.name!==q&&(B=!0,E=function(){return D.call(this)})}if(d&&!v||!n&&!B&&C[m]||g(C,m,E),i[b]=E,i[z]=r,t)if(w={values:A?E:y(q),keys:u?E:y(p),entries:A?y("entries"):E},v)for(x in w)x in C||f(C,x,w[x]);else e(e.P+e.F*(n||B),b,w);return w}},function(a,b){a.exports={}},function(a,b,c){"use strict";var d=c(9),e=c(26),f=c(28),g={};c(25)(g,c(29)("iterator"),function(){return this}),a.exports=function(a,b,c){a.prototype=d.create(g,{next:e(1,c)}),f(a,b+" Iterator")}},function(a,b,c){c(52);var d=c(49);d.NodeList=d.HTMLCollection=d.Array},function(a,b,c){"use strict";var d=c(53),e=c(54),f=c(49),g=c(32);a.exports=c(48)(Array,"Array",function(a,b){this._t=g(a),this._i=0,this._k=b},function(){var a=this._t,b=this._k,c=this._i++;return!a||c>=a.length?(this._t=void 0,e(1)):"keys"==b?e(0,c):"values"==b?e(0,a[c]):e(0,[c,a[c]])},"values"),f.Arguments=f.Array,d("keys"),d("values"),d("entries")},function(a,b){a.exports=function(){}},function(a,b){a.exports=function(a,b){return{value:b,done:!!a}}},function(a,b,c){a.exports={"default":c(56),__esModule:!0}},function(a,b,c){c(51),c(45),a.exports=c(57)},function(a,b,c){var d=c(39),e=c(58);a.exports=c(21).getIterator=function(a){var b=e(a);if("function"!=typeof b)throw TypeError(a+" is not iterable!");return d(b.call(a))}},function(a,b,c){var d=c(59),e=c(29)("iterator"),f=c(49);a.exports=c(21).getIteratorMethod=function(a){if(void 0!=a)return a[e]||a["@@iterator"]||f[d(a)]}},function(a,b,c){var d=c(34),e=c(29)("toStringTag"),f="Arguments"==d(function(){return arguments}());a.exports=function(a){var b,c,g;return void 0===a?"Undefined":null===a?"Null":"string"==typeof(c=(b=Object(a))[e])?c:f?d(b):"Object"==(g=d(b))&&"function"==typeof b.callee?"Arguments":g}},function(a,b,c){a.exports={"default":c(61),__esModule:!0}},function(a,b,c){c(62),a.exports=c(21).Object.keys},function(a,b,c){var d=c(63);c(64)("keys",function(a){return function(b){return a(d(b))}})},function(a,b,c){var d=c(35);a.exports=function(a){return Object(d(a))}},function(a,b,c){var d=c(20),e=c(21),f=c(19);a.exports=function(a,b){var c=(e.Object||{})[a]||Object[a],g={};g[a]=b(c),d(d.S+d.F*f(function(){c(1)}),"Object",g)}},function(a,b,c){"use strict";var d=c(1)["default"];b.__esModule=!0;var e=c(6),f=d(e);b["default"]=function(a){a.registerHelper("helperMissing",function(){if(1!==arguments.length)throw new f["default"]('Missing helper: "'+arguments[arguments.length-1].name+'"')})},a.exports=b["default"]},function(a,b,c){"use strict";var d=c(1)["default"];b.__esModule=!0;var e=c(5),f=c(6),g=d(f);b["default"]=function(a){a.registerHelper("if",function(a,b){if(2!=arguments.length)throw new g["default"]("#if requires exactly one argument");return e.isFunction(a)&&(a=a.call(this)),!b.hash.includeZero&&!a||e.isEmpty(a)?b.inverse(this):b.fn(this)}),a.registerHelper("unless",function(b,c){if(2!=arguments.length)throw new g["default"]("#unless requires exactly one argument");return a.helpers["if"].call(this,b,{fn:c.inverse,inverse:c.fn,hash:c.hash})})},a.exports=b["default"]},function(a,b){"use strict";b.__esModule=!0,b["default"]=function(a){a.registerHelper("log",function(){for(var b=[void 0],c=arguments[arguments.length-1],d=0;d=0?b:parseInt(a,10)}return a},log:function(a){if(a=e.lookupLevel(a),"undefined"!=typeof console&&e.lookupLevel(e.level)<=a){var b=e.methodMap[a];console[b]||(b="log");for(var c=arguments.length,d=Array(c>1?c-1:0),f=1;f=v.LAST_COMPATIBLE_COMPILER_REVISION&&b<=v.COMPILER_REVISION)){if(b2&&v.push("'"+this.terminals_[s]+"'");x=this.lexer.showPosition?"Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+v.join(", ")+", got '"+(this.terminals_[n]||n)+"'":"Parse error on line "+(i+1)+": Unexpected "+(1==n?"end of input":"'"+(this.terminals_[n]||n)+"'"),this.parseError(x,{text:this.lexer.match,token:this.terminals_[n]||n,line:this.lexer.yylineno,loc:l,expected:v})}}if(q[0]instanceof Array&&q.length>1)throw new Error("Parse Error: multiple actions possible at state: "+p+", token: "+n);switch(q[0]){case 1:d.push(n),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(q[1]),n=null,o?(n=o,o=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,l=this.lexer.yylloc,k>0&&k--);break;case 2:if(t=this.productions_[q[1]][1],w.$=e[e.length-t],w._$={first_line:f[f.length-(t||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(t||1)].first_column,last_column:f[f.length-1].last_column},m&&(w._$.range=[f[f.length-(t||1)].range[0],f[f.length-1].range[1]]),r=this.performAction.call(w,h,j,i,this.yy,q[1],e,f),"undefined"!=typeof r)return r;t&&(d=d.slice(0,-1*t*2),e=e.slice(0,-1*t),f=f.slice(0,-1*t)),d.push(this.productions_[q[1]][0]),e.push(w.$),f.push(w._$),u=g[d[d.length-2]][d[d.length-1]],d.push(u);break;case 3:return!0}}return!0}},c=function(){var a={EOF:1,parseError:function(a,b){if(!this.yy.parser)throw new Error(a);this.yy.parser.parseError(a,b)},setInput:function(a){return this._input=a,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var a=this._input[0];this.yytext+=a,this.yyleng++,this.offset++,this.match+=a,this.matched+=a;var b=a.match(/(?:\r\n?|\n).*/g);return b?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),a},unput:function(a){var b=a.length,c=a.split(/(?:\r\n?|\n)/g);this._input=a+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-b-1),this.offset-=b;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var e=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===d.length?this.yylloc.first_column:0)+d[d.length-c.length].length-c[0].length:this.yylloc.first_column-b},this.options.ranges&&(this.yylloc.range=[e[0],e[0]+this.yyleng-b]),this},more:function(){return this._more=!0,this},less:function(a){this.unput(this.match.slice(a))},pastInput:function(){var a=this.matched.substr(0,this.matched.length-this.match.length);return(a.length>20?"...":"")+a.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var a=this.match;return a.length<20&&(a+=this._input.substr(0,20-a.length)),(a.substr(0,20)+(a.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var a=this.pastInput(),b=new Array(a.length+1).join("-");return a+this.upcomingInput()+"\n"+b+"^"},next:function(){if(this.done)return this.EOF;this._input||(this.done=!0);var a,b,c,d,e;this._more||(this.yytext="",this.match="");for(var f=this._currentRules(),g=0;gb[0].length)||(b=c,d=g,this.options.flex));g++);return b?(e=b[0].match(/(?:\r\n?|\n).*/g),e&&(this.yylineno+=e.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:e?e[e.length-1].length-e[e.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+b[0].length},this.yytext+=b[0],this.match+=b[0],this.matches=b,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(b[0].length),this.matched+=b[0],a=this.performAction.call(this,this.yy,this,f[d],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),a?a:void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var a=this.next();return"undefined"!=typeof a?a:this.lex()},begin:function(a){this.conditionStack.push(a)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(a){this.begin(a)}};return a.options={},a.performAction=function(a,b,c,d){function e(a,c){return b.yytext=b.yytext.substring(a,b.yyleng-c+a)}switch(c){case 0:if("\\\\"===b.yytext.slice(-2)?(e(0,1),this.begin("mu")):"\\"===b.yytext.slice(-1)?(e(0,1),this.begin("emu")):this.begin("mu"),b.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(e(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:return this.popState(),44;case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(b.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:return 72;case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return b.yytext=e(1,2).replace(/\\"/g,'"'),80;case 32:return b.yytext=e(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:return 82;case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return b.yytext=b.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},a.rules=[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],a.conditions={mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}},a}();return b.lexer=c,a.prototype=b,b.Parser=a,new a}();b["default"]=c,a.exports=b["default"]},function(a,b,c){"use strict";function d(){var a=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=a}function e(a,b,c){void 0===b&&(b=a.length);var d=a[b-1],e=a[b-2];return d?"ContentStatement"===d.type?(e||!c?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(d.original):void 0:c}function f(a,b,c){void 0===b&&(b=-1);var d=a[b+1],e=a[b+2];return d?"ContentStatement"===d.type?(e||!c?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(d.original):void 0:c}function g(a,b,c){var d=a[null==b?0:b+1];if(d&&"ContentStatement"===d.type&&(c||!d.rightStripped)){var e=d.value;d.value=d.value.replace(c?/^\s+/:/^[ \t]*\r?\n?/,""),d.rightStripped=d.value!==e}}function h(a,b,c){var d=a[null==b?a.length-1:b-1];if(d&&"ContentStatement"===d.type&&(c||!d.leftStripped)){var e=d.value;return d.value=d.value.replace(c?/\s+$/:/[ \t]+$/,""),d.leftStripped=d.value!==e,d.leftStripped}}var i=c(1)["default"];b.__esModule=!0;var j=c(88),k=i(j);d.prototype=new k["default"],d.prototype.Program=function(a){var b=!this.options.ignoreStandalone,c=!this.isRootSeen;this.isRootSeen=!0;for(var d=a.body,i=0,j=d.length;i0)throw new q["default"]("Invalid path: "+d,{loc:c});".."===i&&f++}}return{type:"PathExpression",data:a,depth:f,parts:e,original:d,loc:c}}function j(a,b,c,d,e,f){var g=d.charAt(3)||d.charAt(2),h="{"!==g&&"&"!==g,i=/\*/.test(d);return{type:i?"Decorator":"MustacheStatement",path:a,params:b,hash:c,escaped:h,strip:e,loc:this.locInfo(f)}}function k(a,b,c,e){d(a,c),e=this.locInfo(e);var f={type:"Program",body:b,strip:{},loc:e};return{type:"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:f,openStrip:{},inverseStrip:{},closeStrip:{},loc:e}}function l(a,b,c,e,f,g){e&&e.path&&d(a,e);var h=/\*/.test(a.open);b.blockParams=a.blockParams;var i=void 0,j=void 0;if(c){if(h)throw new q["default"]("Unexpected inverse block on decorator",c);c.chain&&(c.program.body[0].closeStrip=e.strip),j=c.strip,i=c.program}return f&&(f=i,i=b,b=f),{type:h?"DecoratorBlock":"BlockStatement",path:a.path,params:a.params,hash:a.hash,program:b,inverse:i,openStrip:a.strip,inverseStrip:j,closeStrip:e&&e.strip,loc:this.locInfo(g)}}function m(a,b){if(!b&&a.length){var c=a[0].loc,d=a[a.length-1].loc;c&&d&&(b={source:c.source,start:{line:c.start.line,column:c.start.column},end:{line:d.end.line,column:d.end.column}})}return{type:"Program",body:a,strip:{},loc:b}}function n(a,b,c,e){return d(a,c),{type:"PartialBlockStatement",name:a.path,params:a.params,hash:a.hash,program:b,openStrip:a.strip,closeStrip:c&&c.strip,loc:this.locInfo(e)}}var o=c(1)["default"];b.__esModule=!0,b.SourceLocation=e,b.id=f,b.stripFlags=g,b.stripComment=h,b.preparePath=i,b.prepareMustache=j,b.prepareRawBlock=k,b.prepareBlock=l,b.prepareProgram=m,b.preparePartialBlock=n;var p=c(6),q=o(p)},function(a,b,c){"use strict";function d(){}function e(a,b,c){if(null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+a);b=b||{},"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var d=c.parse(a,b),e=(new c.Compiler).compile(d,b);return(new c.JavaScriptCompiler).compile(e,b)}function f(a,b,c){function d(){var d=c.parse(a,b),e=(new c.Compiler).compile(d,b),f=(new c.JavaScriptCompiler).compile(e,b,void 0,!0);return c.template(f)}function e(a,b){return f||(f=d()),f.call(this,a,b)}if(void 0===b&&(b={}),null==a||"string"!=typeof a&&"Program"!==a.type)throw new l["default"]("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+a);b=m.extend({},b),"data"in b||(b.data=!0),b.compat&&(b.useDepths=!0);var f=void 0;return e._setup=function(a){return f||(f=d()),f._setup(a)},e._child=function(a,b,c,e){return f||(f=d()),f._child(a,b,c,e)},e}function g(a,b){if(a===b)return!0;if(m.isArray(a)&&m.isArray(b)&&a.length===b.length){for(var c=0;c1)throw new l["default"]("Unsupported number of partial arguments: "+c.length,a);c.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):c.push({type:"PathExpression",parts:[],depth:0}));var d=a.name.original,e="SubExpression"===a.name.type;e&&this.accept(a.name),this.setupFullMustacheParams(a,b,void 0,!0);var f=a.indent||"";this.options.preventIndent&&f&&(this.opcode("appendContent",f),f=""),this.opcode("invokePartial",e,d,f),this.opcode("append")},PartialBlockStatement:function(a){this.PartialStatement(a)},MustacheStatement:function(a){this.SubExpression(a),a.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(a){this.DecoratorBlock(a)},ContentStatement:function(a){a.value&&this.opcode("appendContent",a.value)},CommentStatement:function(){},SubExpression:function(a){h(a);var b=this.classifySexpr(a);"simple"===b?this.simpleSexpr(a):"helper"===b?this.helperSexpr(a):this.ambiguousSexpr(a)},ambiguousSexpr:function(a,b,c){var d=a.path,e=d.parts[0],f=null!=b||null!=c;this.opcode("getContext",d.depth),this.opcode("pushProgram",b),this.opcode("pushProgram",c),d.strict=!0,this.accept(d),this.opcode("invokeAmbiguous",e,f)},simpleSexpr:function(a){var b=a.path;b.strict=!0,this.accept(b),this.opcode("resolvePossibleLambda")},helperSexpr:function(a,b,c){var d=this.setupFullMustacheParams(a,b,c),e=a.path,f=e.parts[0];if(this.options.knownHelpers[f])this.opcode("invokeKnownHelper",d.length,f);else{if(this.options.knownHelpersOnly)throw new l["default"]("You specified knownHelpersOnly, but used the unknown helper "+f,a);e.strict=!0,e.falsy=!0,this.accept(e),this.opcode("invokeHelper",d.length,e.original,o["default"].helpers.simpleId(e))}},PathExpression:function(a){this.addDepth(a.depth),this.opcode("getContext",a.depth);var b=a.parts[0],c=o["default"].helpers.scopedId(a),d=!a.depth&&!c&&this.blockParamIndex(b);d?this.opcode("lookupBlockParam",d,a.parts):b?a.data?(this.options.data=!0,this.opcode("lookupData",a.depth,a.parts,a.strict)):this.opcode("lookupOnContext",a.parts,a.falsy,a.strict,c):this.opcode("pushContext")},StringLiteral:function(a){this.opcode("pushString",a.value)},NumberLiteral:function(a){this.opcode("pushLiteral",a.value)},BooleanLiteral:function(a){this.opcode("pushLiteral",a.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(a){var b=a.pairs,c=0,d=b.length;for(this.opcode("pushHash");c=0)return[b,e]}}}},function(a,b,c){"use strict";function d(a){this.value=a}function e(){}function f(a,b,c,d,e){var f=b.popStack(),g=c.length;for(a&&g--;d0&&(c+=", "+d.join(", "));var e=0;g(this.aliases).forEach(function(a){var d=b.aliases[a];d.children&&d.referenceCount>1&&(c+=", alias"+ ++e+"="+a,d.children[0]="alias"+e)}),this.lookupPropertyFunctionIsUsed&&(c+=", "+this.lookupPropertyFunctionVarDeclaration());var f=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&f.push("blockParams"),this.useDepths&&f.push("depths");var h=this.mergeSource(c);return a?(f.push(h),Function.apply(this,f)):this.source.wrap(["function(",f.join(","),") {\n ",h,"}"])},mergeSource:function(a){var b=this.environment.isSimple,c=!this.forceBuffer,d=void 0,e=void 0,f=void 0,g=void 0;return this.source.each(function(a){a.appendToBuffer?(f?a.prepend(" + "):f=a,g=a):(f&&(e?f.prepend("buffer += "):d=!0,g.add(";"),f=g=void 0),e=!0,b||(c=!1))}),c?f?(f.prepend("return "),g.add(";")):e||this.source.push('return "";'):(a+=", buffer = "+(d?"":this.initializeBuffer()),f?(f.prepend("return buffer + "),g.add(";")):this.source.push("return buffer;")),a&&this.source.prepend("var "+a.substring(2)+(d?"":";\n")),this.source.merge()},lookupPropertyFunctionVarDeclaration:function(){return"\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n ".trim()},blockValue:function(a){var b=this.aliasable("container.hooks.blockHelperMissing"),c=[this.contextName(0)];this.setupHelperArgs(a,0,c);var d=this.popStack();c.splice(1,0,d),this.push(this.source.functionCall(b,"call",c))},ambiguousBlockValue:function(){var a=this.aliasable("container.hooks.blockHelperMissing"),b=[this.contextName(0)];this.setupHelperArgs("",0,b,!0),this.flushInline();var c=this.topStack();b.splice(1,0,c),this.pushSource(["if (!",this.lastHelper,") { ",c," = ",this.source.functionCall(a,"call",b),"}"])},appendContent:function(a){this.pendingContent?a=this.pendingContent+a:this.pendingLocation=this.source.currentLocation,this.pendingContent=a},append:function(){if(this.isInline())this.replaceStack(function(a){return[" != null ? ",a,' : ""']}),this.pushSource(this.appendToBuffer(this.popStack()));else{var a=this.popStack();this.pushSource(["if (",a," != null) { ",this.appendToBuffer(a,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(a){this.lastContext=a},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(a,b,c,d){var e=0;d||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(a[e++])),this.resolvePath("context",a,e,b,c)},lookupBlockParam:function(a,b){this.useBlockParams=!0,this.push(["blockParams[",a[0],"][",a[1],"]"]),this.resolvePath("context",b,1)},lookupData:function(a,b,c){a?this.pushStackLiteral("container.data(data, "+a+")"):this.pushStackLiteral("data"),this.resolvePath("data",b,0,!0,c)},resolvePath:function(a,b,c,d,e){var g=this;if(this.options.strict||this.options.assumeObjects)return void this.push(f(this.options.strict&&e,this,b,c,a));for(var h=b.length;cthis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var a=this.inlineStack;this.inlineStack=[];for(var b=0,c=a.length;b - + diff --git a/scheduler/templates/scheduler/events.html b/scheduler/templates/scheduler/events.html index 1c3a78a..bee3571 100644 --- a/scheduler/templates/scheduler/events.html +++ b/scheduler/templates/scheduler/events.html @@ -13,7 +13,6 @@

    Lecture Capture Scheduler

    Find an Event