From d1bc546d7b2a636f0354726782e1e6a8cea1ba8a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Nov 2023 18:13:18 +0100 Subject: [PATCH] WIP: Affichage de l'emploi du temps du semestre. --- app/api/formsemestres.py | 16 + app/models/formsemestre.py | 16 + app/models/groups.py | 14 +- app/models/moduleimpls.py | 6 + app/models/modules.py | 8 + app/scodoc/sco_edt_cal.py | 318 +++++++++--------- app/scodoc/sco_formsemestre_custommenu.py | 5 - app/scodoc/sco_formsemestre_status.py | 29 +- app/scodoc/sco_groups.py | 6 +- app/scodoc/sco_groups_edit.py | 6 +- app/scodoc/sco_preferences.py | 15 +- app/static/css/edt.css | 16 + .../tui.calendar/toastui-calendar.min.css | 6 + .../tui.calendar/toastui-calendar.min.js | 9 + app/templates/formsemestre/edt.j2 | 96 ++++++ app/views/notes_formsemestre.py | 13 + sco_version.py | 2 +- 17 files changed, 393 insertions(+), 188 deletions(-) create mode 100644 app/static/css/edt.css create mode 100644 app/static/libjs/tui.calendar/toastui-calendar.min.css create mode 100644 app/static/libjs/tui.calendar/toastui-calendar.min.js create mode 100644 app/templates/formsemestre/edt.j2 diff --git a/app/api/formsemestres.py b/app/api/formsemestres.py index 40fc81bb7a..87a6401940 100644 --- a/app/api/formsemestres.py +++ b/app/api/formsemestres.py @@ -33,6 +33,7 @@ from app.models import ( ) from app.models.formsemestre import GROUPS_AUTO_ASSIGNMENT_DATA_MAX from app.scodoc.sco_bulletins import get_formsemestre_bulletin_etud_json +from app.scodoc import sco_edt_cal from app.scodoc import sco_groups from app.scodoc.sco_permissions import Permission from app.scodoc.sco_utils import ModuleType @@ -555,3 +556,18 @@ def save_groups_auto_assignment(formsemestre_id: int): formsemestre.groups_auto_assignment_data = request.data db.session.add(formsemestre) db.session.commit() + + +@bp.route("/formsemestre//edt") +@api_web_bp.route("/formsemestre//edt") +@login_required +@scodoc +@permission_required(Permission.ScoView) +@as_json +def formsemestre_edt(formsemestre_id: int): + """l'emploi du temps du semestre""" + query = FormSemestre.query.filter_by(id=formsemestre_id) + if g.scodoc_dept: + query = query.filter_by(dept_id=g.scodoc_dept_id) + formsemestre: FormSemestre = query.first_or_404(formsemestre_id) + return sco_edt_cal.formsemestre_edt_dict(formsemestre) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index d020eb92a4..705e262487 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -259,6 +259,22 @@ class FormSemestre(db.Model): d["session_id"] = self.session_id() return d + def get_default_group(self) -> GroupDescr: + """default ('tous') group. + Le groupe par défaut contient tous les étudiants et existe toujours. + C'est l'unique groupe de la partition sans nom. + """ + default_partition = self.partitions.filter_by(partition_name=None).first() + if default_partition: + return default_partition.groups.first() + raise ScoValueError("Le semestre n'a pas de groupe par défaut") + + def get_edt_id(self) -> str: + "l'id pour l'emploi du temps: à défaut, le 1er code étape Apogée" + return ( + self.edt_id or "" or (self.etapes[0].etape_apo if len(self.etapes) else "") + ) + def get_infos_dict(self) -> dict: """Un dict avec des informations sur le semestre pour les bulletins et autres templates diff --git a/app/models/groups.py b/app/models/groups.py index 8d445eee4f..33eac26044 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -231,8 +231,12 @@ class GroupDescr(db.Model): f"""<{self.__class__.__name__} {self.id} "{self.group_name or '(tous)'}">""" ) - def get_nom_with_part(self) -> str: - "Nom avec partition: 'TD A'" + def get_nom_with_part(self, default="-") -> str: + """Nom avec partition: 'TD A' + Si groupe par défaut (tous), utilise default ou "-" + """ + if self.partition.partition_name is None: + return default return f"{self.partition.partition_name or ''} {self.group_name or '-'}" def to_dict(self, with_partition=True) -> dict: @@ -243,10 +247,14 @@ class GroupDescr(db.Model): d["partition"] = self.partition.to_dict(with_groups=False) return d + def get_edt_id(self) -> str: + "l'id pour l'emploi du temps: à défaut, le nom scodoc du groupe" + return self.edt_id or self.group_name or "" + def get_nb_inscrits(self) -> int: """Nombre inscrits à ce group et au formsemestre. C'est nécessaire car lors d'une désinscription, on conserve l'appartenance - aux groupes pour facilier une éventuelle ré-inscription. + aux groupes pour faciliter une éventuelle ré-inscription. """ from app.models.formsemestre import FormSemestreInscription diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 75672c1b9b..741abda1bd 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -45,6 +45,12 @@ class ModuleImpl(db.Model): def __repr__(self): return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>" + def get_edt_id(self) -> str: + "l'id pour l'emploi du temps: actuellement celui du module" + return ( + self.module.get_edt_id() + ) # TODO à décliner pour autoriser des codes différents ? + def get_evaluations_poids(self) -> pd.DataFrame: """Les poids des évaluations vers les UE (accès via cache)""" evaluations_poids = df_cache.EvaluationsPoidsCache.get(self.id) diff --git a/app/models/modules.py b/app/models/modules.py index fe297ebd78..5a96de3a9a 100644 --- a/app/models/modules.py +++ b/app/models/modules.py @@ -285,6 +285,14 @@ class Module(db.Model): return {x.strip() for x in self.code_apogee.split(",") if x} return set() + def get_edt_id(self) -> str: + "l'id pour l'emploi du temps: à défaut, le 1er code Apogée" + return ( + self.edt_id + or (self.code_apogee.split(",")[0] if self.code_apogee else "") + or "" + ) + def get_parcours(self) -> list[ApcParcours]: """Les parcours utilisant ce module. Si tous les parcours, liste vide (!). diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 2174f25805..cd46014089 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -29,175 +29,191 @@ XXX usage uniquement experimental pour tests implémentations -XXX incompatible avec les ics HyperPlanning Paris 13 (était pour GPU). - """ - +import re import icalendar -import urllib - -import app.scodoc.sco_utils as scu +from flask import flash from app import log -from app.scodoc import sco_formsemestre -from app.scodoc import sco_groups -from app.scodoc import sco_preferences +from app.models import FormSemestre, GroupDescr, ModuleImpl, ScoDocSiteConfig +import app.scodoc.sco_utils as scu -def formsemestre_get_ics_url(sem): - """ - edt_sem_ics_url est un template - utilisé avec .format(sem=sem) - Par exemple: - https://example.fr/agenda/{sem[etapes][0]} - """ - ics_url_tmpl = sco_preferences.get_preference( - "edt_sem_ics_url", sem["formsemestre_id"] - ) - if not ics_url_tmpl: - return None - try: - ics_url = ics_url_tmpl.format(sem=sem) - except: - log( - f"""Exception in formsemestre_get_ics_url(formsemestre_id={sem["formsemestre_id"]}) - ics_url_tmpl='{ics_url_tmpl}' - """ +def formsemestre_load_calendar( + formsemestre: FormSemestre, +) -> icalendar.cal.Calendar | None: + """Load ics data, return calendar or None if not configured or not available""" + edt_id = formsemestre.get_edt_id() + if not edt_id: + flash( + "accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)" ) - log(traceback.format_exc()) return None - return ics_url + edt_ics_path = ScoDocSiteConfig.get("edt_ics_path") + if not edt_ics_path.strip(): + return None + ics_filename = edt_ics_path.format(edt_id=edt_id) + try: + with open(ics_filename, "rb") as file: + log(f"Loading edt from {ics_filename}") + calendar = icalendar.Calendar.from_ical(file.read()) + except FileNotFoundError: + flash("erreur chargement du calendrier") + log( + f"formsemestre_load_calendar: ics not found for {formsemestre}\npath='{ics_filename}'" + ) + return None + + return calendar -def formsemestre_load_ics(sem): - """Load ics data, from our cache or, when necessary, from external provider""" - # TODO: cacher le résultat - ics_url = formsemestre_get_ics_url(sem) - if not ics_url: - ics_data = "" - else: - log(f"Loading edt from {ics_url}") - # 5s TODO: add config parameter, eg for slow networks - f = urllib.request.urlopen(ics_url, timeout=5) - ics_data = f.read() - f.close() - - cal = icalendar.Calendar.from_ical(ics_data) - return cal +_COLOR_PALETTE = [ + "#ff6961", + "#ffb480", + "#f8f38d", + "#42d6a4", + "#08cad1", + "#59adf6", + "#9d94ff", + "#c780e8", +] -def get_edt_transcodage_groups(formsemestre_id): - """-> { nom_groupe_edt : nom_groupe_scodoc }""" - # TODO: valider ces données au moment où on enregistre les préférences - edt2sco = {} - sco2edt = {} - msg = "" # message erreur, '' si ok - txt = sco_preferences.get_preference("edt_groups2scodoc", formsemestre_id) - if not txt: - return edt2sco, sco2edt, msg - - line_num = 1 - for line in txt.split("\n"): - fs = [s.strip() for s in line.split(";")] - if len(fs) == 1: # groupe 'tous' - edt2sco[fs[0]] = None - sco2edt[None] = fs[0] - elif len(fs) == 2: - edt2sco[fs[0]] = fs[1] - sco2edt[fs[1]] = fs[0] - else: - msg = f"ligne {line_num} invalide" - line_num += 1 - - log(f"sco2edt={pprint.pformat(sco2edt)}") - return edt2sco, sco2edt, msg - - -def group_edt_json(group_id, start="", end=""): # actuellement inutilisé - """EDT complet du semestre, au format JSON - TODO: indiquer un groupe - TODO: utiliser start et end (2 dates au format ISO YYYY-MM-DD) - TODO: cacher +def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]: + """EDT complet du semestre, comme une liste de dict serialisable en json. + Fonction appellée par l'API /formsemestre//edt + TODO: spécifier intervalle de dates start et end + TODO: cacher ? """ - group = sco_groups.get_group(group_id) - sem = sco_formsemestre.get_formsemestre(group["formsemestre_id"]) - edt2sco, sco2edt, msg = get_edt_transcodage_groups(group["formsemestre_id"]) + # Correspondances id edt -> id scodoc pour groupes, modules et enseignants + edt2group = formsemestre_retreive_groups_from_edt_id(formsemestre) + group_colors = { + group_name: _COLOR_PALETTE[i % (len(_COLOR_PALETTE) - 1) + 1] + for i, group_name in enumerate(edt2group) + } + default_group = formsemestre.get_default_group() + edt2modimpl = formsemestre_retreive_modimpls_from_edt_id(formsemestre) - edt_group_name = sco2edt.get(group["group_name"], group["group_name"]) - log("group scodoc=%s : edt=%s" % (group["group_name"], edt_group_name)) - - cal = formsemestre_load_ics(sem) - events = [e for e in cal.walk() if e.name == "VEVENT"] - J = [] - for e in events: - # if e['X-GROUP-ID'].strip() == edt_group_name: - if "DESCRIPTION" in e: + # Chargement du calendier ics + calendar = formsemestre_load_calendar(formsemestre) + if not calendar: + return [] + # Génération des événements, avec titre et champs utiles pour l'affichage dans ScoDoc + events = [e for e in calendar.walk() if e.name == "VEVENT"] + events_dict = [] + for event in events: + if "DESCRIPTION" in event: + # --- Group + edt_group = extract_event_group(event) + # si pas de groupe dans l'event, prend toute la promo ("tous") + group: GroupDescr = ( + edt2group.get(edt_group, None) if edt_group else default_group + ) + background_color = ( + group_colors.get(edt_group, "rgb(214, 233, 248)") + if group + else "lightgrey" + ) + group_disp = ( + f"""
{group.get_nom_with_part(default="promo")}
""" + if group + else f"""
{edt_group} + + {scu.EMO_WARNING} non reconnu +
""" + ) + # --- ModuleImpl + edt_module = extract_event_module(event) + modimpl: ModuleImpl = edt2modimpl.get(edt_module, None) + mod_disp = ( + f"""
{modimpl.module.code} +
""" + if modimpl + else f"""
{scu.EMO_WARNING} {edt_module}
""" + ) d = { - "title": e.decoded("DESCRIPTION"), # + '/' + e['X-GROUP-ID'], - "start": e.decoded("dtstart").isoformat(), - "end": e.decoded("dtend").isoformat(), + # Champs utilisés par tui.calendar + "calendarId": "cal1", + "title": extract_event_title(event) + group_disp + mod_disp, + "start": event.decoded("dtstart").isoformat(), + "end": event.decoded("dtend").isoformat(), + "backgroundColor": background_color, + # Infos brutes pour usage API éventuel + "group_id": group.id if group else None, + "group_edt_id": edt_group, + "moduleimpl_id": modimpl.id if modimpl else None, } - J.append(d) + events_dict.append(d) - return scu.sendJSON(J) + return events_dict -# def experimental_calendar(group_id=None, formsemestre_id=None): # inutilisé -# """experimental page""" -# return "\n".join( -# [ -# html_sco_header.sco_header( -# javascripts=[ -# "libjs/purl.js", -# "libjs/moment.min.js", -# "libjs/fullcalendar/fullcalendar.min.js", -# ], -# cssstyles=[ -# # 'libjs/bootstrap-3.1.1-dist/css/bootstrap.min.css', -# # 'libjs/bootstrap-3.1.1-dist/css/bootstrap-theme.min.css', -# # 'libjs/bootstrap-multiselect/bootstrap-multiselect.css' -# "libjs/fullcalendar/fullcalendar.css", -# # media='print' 'libjs/fullcalendar/fullcalendar.print.css' -# ], -# ), -# """ -# """, -# """
-# Emplois du temps du groupe""", -# sco_groups_view.menu_group_choice( -# group_id=group_id, formsemestre_id=formsemestre_id -# ), -# """
loading...
-#
-# """, -# html_sco_header.sco_footer(), -# """ -# """, -# ] -# ) +def extract_event_module(event: icalendar.cal.Event) -> str: + """Extrait le code module de l'emplois du temps. + Chaine vide si ne le trouve pas. + Par exemple, à l'USPN, Hyperplanning nous donne le code 'VRETR113' dans DESCRIPTION + 'Matière : VRETR113 - Mathematiques du sig (VRETR113\nEnseignant : 1234 - M. DUPONT PIERRE\nTD : TDB\nSalle : L112 (IUTV) - L112\n' + """ + # TODO: fonction ajustée à l'USPN, devra être paramétrable d'une façon ou d'une autre: regexp ? + if not event.has_key("DESCRIPTION"): + return "-" + description = event.decoded("DESCRIPTION").decode("utf-8") # assume ics in utf8 + # extraction du code: + m = re.search(r"Matière : ([A-Z][A-Z0-9]+)", description) + if m and len(m.groups()) > 0: + return m.group(1) + return "" + + +def extract_event_group(event: icalendar.cal.Event) -> str: + """Extrait le nom du groupe (TD, ...). "" si pas de match.""" + # TODO: fonction ajustée à l'USPN, devra être paramétrable d'une façon ou d'une autre: regexp ? + # Utilise ici le SUMMARY + # qui est de la forme + # SUMMARY;LANGUAGE=fr:TP2 GPR1 - VCYR303 - Services reseaux ava (VCYR303) - 1234 - M. VIENNET EMMANUEL - V2ROM - BUT2 RT pa. ROM - Groupe 1 + if not event.has_key("SUMMARY"): + return "-" + summary = event.decoded("SUMMARY").decode("utf-8") # assume ics in utf8 + # extraction du code: + m = re.search(r".*- ([\w\s]+)$", summary) + if m and len(m.groups()) > 0: + return m.group(1).strip() + return "" + + +def formsemestre_retreive_modimpls_from_edt_id( + formsemestre: FormSemestre, +) -> dict[str, ModuleImpl]: + """Construit un dict donnant le moduleimpl de chaque edt_id""" + edt2modimpl = {modimpl.get_edt_id(): modimpl for modimpl in formsemestre.modimpls} + edt2modimpl.pop("", None) + return edt2modimpl + + +def formsemestre_retreive_groups_from_edt_id( + formsemestre: FormSemestre, +) -> dict[str, GroupDescr]: + """Construit un dict donnant le groupe de chaque edt_id""" + edt2group = {} + for partition in formsemestre.partitions: + edt2group.update({g.get_edt_id(): g for g in partition.groups}) + edt2group.pop("", None) + return edt2group diff --git a/app/scodoc/sco_formsemestre_custommenu.py b/app/scodoc/sco_formsemestre_custommenu.py index a98c07ac19..86c5bb3915 100644 --- a/app/scodoc/sco_formsemestre_custommenu.py +++ b/app/scodoc/sco_formsemestre_custommenu.py @@ -86,11 +86,6 @@ def build_context_dict(formsemestre_id: int) -> dict: def formsemestre_custommenu_html(formsemestre_id): "HTML code for custom menu" menu = [] - # Calendrier électronique ? - sem = sco_formsemestre.get_formsemestre(formsemestre_id) - ics_url = sco_edt_cal.formsemestre_get_ics_url(sem) - if ics_url: - menu.append({"title": "Emploi du temps (ics)", "url": ics_url}) # Liens globaux (config. générale) params = build_context_dict(formsemestre_id) for link in ScoDocSiteConfig.get_perso_links(): diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index c716cd0981..767f5a2fac 100755 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -257,6 +257,13 @@ def formsemestre_status_menubar(formsemestre: FormSemestre) -> str: "enabled": current_user.has_permission(Permission.EditFormSemestre), "helpmsg": "", }, + { + "title": "Expérimental: emploi du temps", + "endpoint": "notes.formsemestre_edt", + "args": {"formsemestre_id": formsemestre_id}, + "enabled": True, + "helpmsg": "", + }, ] # debug : if current_app.config["DEBUG"]: @@ -796,10 +803,10 @@ def formsemestre_description( tab.html_before_table = f"""
- indiquer les évaluations - indiquer les parcours BUT """ @@ -836,7 +843,7 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str: 'Tous les étudiants'}
{ - "Gestion de l'assiduité" if not partition_is_empty else "" + "Gestion de l'assiduité" if not partition_is_empty else "" }
""" ) @@ -925,8 +932,8 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str: if formsemestre.can_change_groups(): H.append( f""" (créer)""" ) @@ -937,8 +944,8 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str: H.append( f"""

Ajouter une partition

""" ) @@ -1310,13 +1317,13 @@ def formsemestre_tableau_modules( {mod.code} - {mod.abbrev or mod.titre or ""} {len(mod_inscrits)} { - sco_users.user_info(modimpl["responsable_id"])["prenomnom"] + sco_users.user_info(modimpl["responsable_id"])["prenomnom"] } @@ -1457,8 +1464,8 @@ def formsemestre_warning_etuds_sans_note( "notes.formsemestre_note_etuds_sans_notes", scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, - )}">{"lui" if nb_sans_notes == 1 else "leur"} - {"lui" if nb_sans_notes == 1 else "leur"} + affecter des notes. """ diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index 6e21237574..b0967835d7 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -208,8 +208,10 @@ def get_partition_groups(partition): # OBSOLETE ! ) -def get_default_group(formsemestre_id, fix_if_missing=False): - """Returns group_id for default ('tous') group""" +def get_default_group(formsemestre_id, fix_if_missing=False) -> int: + """Returns group_id for default ('tous') group + XXX remplacé par formsemestre.get_default_group + """ r = ndb.SimpleDictFetch( """SELECT gd.id AS group_id FROM group_descr gd, partition p diff --git a/app/scodoc/sco_groups_edit.py b/app/scodoc/sco_groups_edit.py index 0a9173ec27..792efab2b7 100644 --- a/app/scodoc/sco_groups_edit.py +++ b/app/scodoc/sco_groups_edit.py @@ -96,11 +96,13 @@ def group_rename(group_id): "default": group.edt_id or "", "size": 12, "allow_null": True, - "explanation": "optionnel : identifiant du groupe dans le logiciel d'emploi du temps", + "explanation": """optionnel : identifiant du groupe dans le logiciel + d'emploi du temps, pour le cas où les noms de gropupes ne seraient pas + les mêmes dans ScoDoc et dans l'emploi du temps.""", }, ), ), - submitlabel="Renommer", + submitlabel="Enregistrer", cancelbutton="Annuler", ) dest_url = url_for( diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index cab6feaeed..557b73b0c7 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -2033,24 +2033,13 @@ class BasePreferences: "category": "edt", }, ), - ( - "edt_groups2scodoc", - { - "input_type": "textarea", - "initvalue": "", - "title": "Noms Groupes", - "explanation": "Transcodage: nom de groupe EDT ; non de groupe ScoDoc (sur plusieurs lignes)", - "rows": 8, - "cols": 16, - "category": "edt", - }, - ), + # Divers ( "ImputationDept", { "title": "Département d'imputation", "initvalue": "", - "explanation": "préfixe id de session (optionnel, remplace nom département)", + "explanation": "optionnel: préfixe id de formsemestre (par défaut, le nom du département). Pour usages API avancés.", "size": 10, "category": "edt", }, diff --git a/app/static/css/edt.css b/app/static/css/edt.css new file mode 100644 index 0000000000..79a3430b06 --- /dev/null +++ b/app/static/css/edt.css @@ -0,0 +1,16 @@ + +.toastui-calendar-template-time { + padding: 4px; + word-break: break-all; + white-space: normal !important; + align-items: normal !important; + font-size: 12pt; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} +.group-name { + color:rgb(25, 113, 25); +} +.group-edt { + color: red; + background-color: yellow; +} diff --git a/app/static/libjs/tui.calendar/toastui-calendar.min.css b/app/static/libjs/tui.calendar/toastui-calendar.min.css new file mode 100644 index 0000000000..bd85e12cdf --- /dev/null +++ b/app/static/libjs/tui.calendar/toastui-calendar.min.css @@ -0,0 +1,6 @@ +/*! + * TOAST UI Calendar 2nd Edition + * @version 2.1.3 | Tue Aug 16 2022 + * @author NHN Cloud FE Development Lab + * @license MIT + */.toastui-calendar-holiday{color:red;font-size:15px}.toastui-calendar-layout{box-sizing:border-box;position:relative;white-space:nowrap}.toastui-calendar-layout *{box-sizing:border-box}.toastui-calendar-layout.toastui-calendar-dragging--move-event *{cursor:move}.toastui-calendar-layout.toastui-calendar-dragging--resize-horizontal-event *{cursor:col-resize}.toastui-calendar-layout.toastui-calendar-dragging--resize-vertical-event *{cursor:row-resize}.toastui-calendar-layout .toastui-calendar-panel-resizer{user-select:none}.toastui-calendar-layout .toastui-calendar-panel-resizer:hover{border-color:#999}.toastui-calendar-layout .toastui-calendar-panel-resizer-guide{position:absolute}.toastui-calendar-icon,.toastui-calendar-layout.toastui-calendar-horizontal .toastui-calendar-panel,.toastui-calendar-layout.toastui-calendar-horizontal .toastui-calendar-panel-resizer{display:inline-block;vertical-align:middle}.toastui-calendar-icon{height:14px;width:14px}.toastui-calendar-icon.toastui-calendar-ic-title{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAO5JREFUKBVjYCATMOLSFxkZqfHnz5+1QHktNDVbV69e7cOCJgjmQjXtB3IksMh7g8SY0CXQNTEyMlYD1fBCabhyFI3omkCq/v//PwnotC8gGq4LyIBrxKYJpBBoU15oaCgPiEbWCPYjUEIFGBBY/QS0qRWooRVIg/UBDXgMYoBtBHJSgWxsAQFWjET8BBqQBuLDNM4Can6GpAAb8ydQMADo3x0gSbDGlStX3gVqdMSjGUUTSCNKAggPD1cDOmU/EEuBJKEAQxNIHEUjSABNM1ZNIHXMIAIZXL169a2+vv5moK18QKeXAv20B1meYjYAr7xrljpOzc0AAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-location{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAR1JREFUKBWdUTtPg1AUBiT8CydHJtv/0MTJRWAgcTK1bq0/pO3k4E4IYLo2Me46tS4wumjSpV07kAb6HXLPzaGPRZLL+c73uE/D+OdnHuaCIOhVVTUEf620pWVZ0yRJ3qW3FfR9f1zX9UgaGJumOUnT9Fn3DDzPuwPOuD9TvSzL3kizhOFJ4LnjOJc0wM0FP2Asgx0mEehHUfRHgzDzqF3GOogzbJg8V6XHFqYv4Cvqy7J8DcOwWYmw8Hwy1kHMRjcaKuEGgV82caWbZay3indagJyxcKLOlKeRdJA627YfUVaN0v6tlKbZVjCO4zW2cw91px3AxJEmOONCNoTzPP9xXZfOd6u0Bzz60RGOgmQuiuIb4S3gB0IvaoJW2QMDs1bBoH1CAQAAAABJRU5ErkJggg==) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-date{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAGpJREFUKBVjYKAGCA0N/Q/C6GZhE2cEKQoLC9v6//9/L3QN2PiMjIzbVq1a5c0EkiRWE7JasEZsJhMSI1sjC7LJq1evBvsZWQyZjRxwZNs4hDSiBA6y55EDBRsb7EdQasAmiUNsKw5x4oQBkUAeDPJ53KsAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-state{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAIxJREFUKBVjYCATMKLrCw8P9/z3798soLgMVO4JExNT2sqVK7cjq2VC5oDYME2MjIyNIAwUkoGKoShlQeFBOGCbVq1a1QDihoaG1gMpmO0gITAAOzUsLGzr////vWCC+GigK7YBDfUGO5VYTSADYWox/IjPNmS5UY3IoYHGBgcOKG7QxPFxt+KTJCgHAGcZJbGLRuJ2AAAAAElFTkSuQmCC) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-private{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAKpJREFUKBVjYCATMKLri46Olvn9+3fX////HUByjIyMB1hZWcuWLl36BFktikaQpl+/fl0EKhBCVgRkv2NjY9NH1syErABkE1TTdqBCWRAG8reDxKBycOUoGmHOA2pIA5kOwiA2SDVMDqYTRSNQUBIkgewkJDZYDqYR7sewsLCtQFO9YBLYaGBAbVu1apU3SA5uIyFNIMXIauAaQRKkgBGlERTURATOVpgaABRQQOK46wEAAAAAAElFTkSuQmCC) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-public{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAKdJREFUKBVjYCATMKLri46Olvn9+3fX////HUByjIyMB1hZWcuWLl36BFktikaQpl+/fl0EKhBCVgRkv2NjY9NH1syErABkE1TTdqBCWRAG8reDxKBycOUoGmHOA2pIA5kOwiA2SDVMDq4TmREaGvofhJHFcLHhfgwLC9sKNNULl0KQODCgtq1atcobxIY7lZAmkGJkNXCNIAlSwIjSCApqIgJnK0wNALoOPwSpOcq0AAAAAElFTkSuQmCC) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-close{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAJRJREFUKBXFkNERhCAMREUbuEKohzq0Eq2DDq6Da4B60KezDORkxj+ZwchmX0IYhtdWCGFl9y5g82NtzDnPdzAaudo76ZBS+nrvPxiInMkJcs5tMcZFfqcfxdqIRiELof+BiIJPg+mExmpmvKRn3zKj7OrG9Y79szPL14A1xEP0Hgy4gBZS5R7czHj3ehSgOzkVeyfuGrBw/WLm0hsAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-user-b{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAJpJREFUKBVjYKA1YES3wMTExBMoNgsqnnbmzJntyGqYkDlQ9qz////LgDCQD9MIV4ZNA1wSGwObhjRGRsYnIAzUkIZNE0licE+bm5tr/fnzJx1osjPQBFmoKY+BftnLwsIy8+TJk9dAYmANxsbGoUD2YiBmBwliAT+BYrFnz55dDfNDO1AAl2KQfpAcSA0DTIMyiEMAEKMG0wgAolIjcM7Tjm8AAAAASUVORK5CYII=) no-repeat;top:-4px}.toastui-calendar-icon.toastui-calendar-ic-edit{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAMdJREFUKBVjYCARMOJTb2xsLMfIyBjLysq64Pjx409BapnwaQDKzf7//3/L79+/D1tbW0uB1LJg02BmZqYIVPgdKBf/79+//UC2xs+fP8OB/H4MG0CK//79ewCkEGQYExOTI5DawMnJuQTER/EDTDFQXA4kCQQ3QBpOnz79AsJF8gMWxTA1KDTYBhyKMUwH6WSysrKSB7kZyIY5AySOVTFIggno+5VAmijFYA1AwhzEgAKcJsMUwIMVGKPH2NnZ7ZFDBKYImQYAuO5YIMgk39gAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-delete{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAFhJREFUKBVjYCARMKKrNzEx2fr//38vkDgjI+O2M2fOeCOrAWtAVoQsicyGaWZCFsTHBtr6H588Tjm4H4yNjfGacPbsWbBaop0Es3JYaQBFDMxjWOitMDEA3EEZfFEISwUAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-arrow-solid-top{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAIFJREFUKBVjYBj+gBmXF2NiYiTV1dV5rl279gWbGiZsgg0NDSw/f/5cCZRbBWJjU4PVRjExsR6g4nAgln/z5g3v1atXd6JrxtAYHh4e+v///z4khZa6urrXgJqvIYkxMCJzgJo0//37dwooxoMsDmR/YWJiMlu5cuV1NPFRLrYQAADMVCaUtbG7XwAAAABJRU5ErkJggg==) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-milestone{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAGFJREFUKBVjYCARMILU/3dw+I+hj5FxG+P+/d7o4rg1IKtE0syELI6T/f+/F0yOOA0w1UCa9hpYkGxjYDxwABwIILH/jo5bGWBuZ2TcClOHogEmCKKxBSlInPZ+ANlCEgAA37EU4AHbBQUAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-arrow-left{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAHCAYAAAAvZezQAAAAAXNSR0IArs4c6QAAAFZJREFUCB1jZICCyspK83///hUxgvhVVVV6f//+3c3ExJTMVFNTowqU2cHMzJzf3t6+hen379/zgIp2t7W1rQCpZmJlZU0C0q5AbREgAQwzwAIgGZgtADMCJqH/QyvhAAAAAElFTkSuQmCC) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-arrow-right{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAHCAYAAAAvZezQAAAAAXNSR0IArs4c6QAAAFxJREFUCB1jKC8vX1lZWWnOAAVMQLD4379/m6qqqvRAYowgAsiJAAr2sbCw2IMFQIIVFRUL////r8SCpMKVlZXVnhFooA9Q+VxmZmbXtra2S0xATizQYD8QB6QaAJMLJ9BqE9yTAAAAAElFTkSuQmCC) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-handle-y{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAICAMAAADp7a43AAAACVBMVEX///////////+OSuX+AAAAA3RSTlMrQJG5H4EIAAAAEUlEQVR4AWNgYmRiZABB/CwAAtgAIUTUNkMAAAAASUVORK5CYII=) 50% no-repeat}.toastui-calendar-icon.toastui-calendar-ic-checkbox-normal{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAADpJREFUKBVjPHfu3O5///65MJAAmJiY9jCcOXPmP6kApIeJBItQlI5qRAkOVM5o4KCGBwqPkcxEvhsAbzRE+Jhb9IwAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-checkbox-checked{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAMBJREFUKBWVkjEOwjAMRe2WgZW7IIHEDdhghhuwcQ42rlJugAQS54Cxa5cq1QM5TUpByZfS2j9+dlJVt/tX5ZxbS4ZU9VLkQvSHKTIGRaVJYFmKrBbTCJxE2UgCdDzMZDkHrOV6b95V0US6UmgKodujEZbJg0B0ZgEModO5lrY1TMQf1TpyJGBEjD+E2NPN7ukIUDiF/BfEXgRiGEw8NgkffYGYwCi808fpn/6OvfUfsDr/Vc1IfRf8sKnFVqeiVQfDu0tf/nWH9gAAAABJRU5ErkJggg==) no-repeat}.toastui-calendar-icon.toastui-calendar-ic-dropdown-arrow{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAHlJREFUKBVjYBgFOEOAEVkmPDxc89+/f6eAYjzI4kD2FyYmJrOVK1deh4kzwRggGiQBVJCELAZig8SQNYHEmEEEMrh69eo1HR0dfqCYJUickZGxf9WqVf3IakBsFBthklpaWmVA9mEQhrJhUoTp0NBQCRAmrHL4qgAAuu4cWZOZIGsAAAAASUVORK5CYII=) no-repeat}.toastui-calendar-icon.toastui-calendar-open.toastui-calendar-ic-dropdown-arrow{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAIFJREFUKBVjYBj+gBmXF2NiYiTV1dV5rl279gWbGiZsgg0NDSw/f/5cCZRbBWJjU4PVRjExsR6g4nAgln/z5g3v1atXd6JrxtAYHh4e+v///z4khZa6urrXgJqvIYkxMCJzgJo0//37dwooxoMsDmR/YWJiMlu5cuV1NPFRLrYQAADMVCaUtbG7XwAAAABJRU5ErkJggg==) no-repeat}.toastui-calendar-ic-location-b{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAOZJREFUKBWVUT0KwjAUbkzGTuJWPYNDpV0cXD2ETuIRBK+gs4ubp3BwcXBp6eINBKWDgydoid9X8oKCggYeee/7S9IGwZ9LverTNO3Wdb2y1o6IK6WOWutFlmU30XmDE58hbgvpTA+Y+mJqCemS20jdG2N6LPYMICc6b5BrIG3ONBZ7CoVj7w0cfllGRDj+gKQpjt/iPU0ye/LkROcNANaoCUzjqqquIsBuHddAWoiyLO9RFHUwJ4JxR/qmKIqdYG9vCMNwCeIiJHuHecj/B0GSJBng7ifO+ErDPM8L4b7ucRzPWJ8ET1E7YC7tmi9qAAAAAElFTkSuQmCC) no-repeat;top:-4px}.toastui-calendar-ic-state-b{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAHlJREFUKBVjYCARMCKrNzEx8QTyZ/3//18GJM7IyPgESKWdOXNmO4gPAkwQCk6CFQMVNoIwVOMsuCw6w9jY+D8Iw8TR+SBxsJOATtkKNM0LphAbDbRxG9Bp3mAnEVIMMgCmBt0P2AxHERusGkAhgOJQ7Jyt2IUJiAIAwwIn24FgmhkAAAAASUVORK5CYII=) no-repeat;top:-4px}.toastui-calendar-ic-repeat-b{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAQpJREFUKBVjYCARMCKrNzU1tf/3718lUMwQiP8yMjKeYWJi6j516tRhExOThjNnzjTANQAFqv///98CVHQPSO8A0ixATa5AtjyQPgDETmfPngULMpiZmbn//fu3BSg4B6ggCyjxG8hm8PT0ZH/9+vUJoJgTiA8CTCACqDgXaOJ9Xl7eTJhikDhQcSVQsQGITT8A9rSxsfF/mJVApzWCQgPGd3BwYPny5cstoNOuAZ3rAwoJOAAqviAqKtoOEwAaxPr58+dpQL4iEGeAxJFt2AfkOwA1PQTSu4Em/gGyPYC0EpCuAdraCtIADiWgQCPQOmdmZmYHoNgVoCJfIB0CpG8DI84BphgoRjoAAAzgdELI91E5AAAAAElFTkSuQmCC) no-repeat;top:-4px}.toastui-calendar-timegrid-time-column{font-size:11px;height:100%}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-hour-rows{display:inline-block;height:100%;position:relative}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time{color:#333;position:absolute;right:5px;text-align:right}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time.toastui-calendar-timegrid-time-past{font-weight:400}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time.toastui-calendar-timegrid-time-first{line-height:normal;visibility:hidden}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time.toastui-calendar-timegrid-time-last{height:0;visibility:hidden}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time .toastui-calendar-timegrid-time-label,.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time span{position:absolute;right:0;transform:translateY(-50%)}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time .toastui-calendar-timegrid-day-difference{bottom:100%;position:absolute;right:0}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-time-hidden{visibility:hidden}.toastui-calendar-timegrid-time-column .toastui-calendar-timegrid-current-time{font-size:11px;font-weight:400;position:absolute;right:5px;text-align:right;transform:translateY(-50%)}.toastui-calendar-timezone-labels-slot{background-color:#fff;border-bottom:1px solid #e9e9e9;display:table;height:40px;position:absolute;table-layout:fixed}.toastui-calendar-timezone-labels-slot .toastui-calendar-timegrid-timezone-label{background-color:#fff;border-right:1px solid #e5e5e5;display:table-cell;font-size:11px;padding-right:5px;text-align:right;vertical-align:middle}.toastui-calendar-timezone-labels-slot .toastui-calendar-timegrid-timezone-collapse-button{background:transparent;border:1px solid #ddd;border-left:none;bottom:2px;cursor:pointer;position:absolute;top:2px;width:10px}.toastui-calendar-timezone-labels-slot .toastui-calendar-timegrid-timezone-collapse-button .toastui-calendar-icon{height:7px;transform:translateX(-50%);width:4px}.toastui-calendar-column{position:relative}.toastui-calendar-column .toastui-calendar-gridline-half{position:absolute;width:100%}.toastui-calendar-column .toastui-calendar-grid-selection{left:1px;padding:3px;position:absolute;right:10px}.toastui-calendar-column .toastui-calendar-grid-selection .toastui-calendar-grid-selection-label{font-size:11px;font-weight:700}.toastui-calendar-column .toastui-calendar-events{bottom:0;left:0;position:absolute;right:0;top:0}.toastui-calendar-panel.toastui-calendar-time{overflow-y:auto}.toastui-calendar-timegrid{height:200%;min-height:900px;position:relative;user-select:none}.toastui-calendar-timegrid .toastui-calendar-timegrid-scroll-area{height:100%;position:relative}.toastui-calendar-timegrid .toastui-calendar-columns{bottom:0;overflow:hidden;position:absolute;right:0;top:0}.toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-gridline-half{position:absolute;width:100%}.toastui-calendar-timegrid .toastui-calendar-columns .toastui-calendar-column{display:inline-block;height:100%}.toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator{left:0;position:absolute;right:0}.toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-left{position:absolute}.toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-marker{border-radius:50%;height:9px;margin:-4px 0 0 -5px;position:absolute;width:9px}.toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-today{position:absolute}.toastui-calendar-timegrid .toastui-calendar-timegrid-now-indicator .toastui-calendar-timegrid-now-indicator-right{position:absolute;right:0}.toastui-calendar-event-background{position:absolute}.toastui-calendar-event-time{cursor:pointer;overflow:hidden;position:absolute}.toastui-calendar-event-time .toastui-calendar-event-time-content,.toastui-calendar-event-time .toastui-calendar-travel-time{font-size:12px;overflow:hidden;padding:1px 0 0 3px}.toastui-calendar-resize-handler-x{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAECAMAAACEE47CAAAACVBMVEX///////////+OSuX+AAAAA3RSTlMrQJG5H4EIAAAAEUlEQVR4AWNgYoRABhjEFAEAArQAIcHQcPsAAAAASUVORK5CYII=) no-repeat bottom;bottom:1px;color:#fff;cursor:row-resize;height:8px;left:0;position:absolute;right:0;text-align:center}.toastui-calendar-weekday-event-title{display:block;font-size:12px;font-weight:700;overflow:hidden;padding-left:3px;text-overflow:ellipsis;white-space:nowrap}.toastui-calendar-weekday-event-dot{border-radius:50%;display:inline-block;float:left;height:8px;position:relative;top:8px;width:8px}.toastui-calendar-weekday-event-dot+.toastui-calendar-weekday-event-title{color:#333}.toastui-calendar-weekday-resize-handle{position:absolute;right:5px;top:0}.toastui-calendar-weekday-resize-handle.toastui-calendar-handle-y{cursor:col-resize}.toastui-calendar-grid-cell-date .toastui-calendar-weekday-grid-date.toastui-calendar-weekday-grid-date-decorator{background-color:#135de6;border-radius:50%;display:inline-block;font-weight:700;height:26px;line-height:26px;margin-left:2px;text-align:center;width:26px}.toastui-calendar-panel-title{display:table;float:left;height:100%;padding-right:5px}.toastui-calendar-panel-title .toastui-calendar-left-content{display:table-cell;font-size:11px;text-align:right;vertical-align:middle}.toastui-calendar-panel-grid-wrapper{overflow-y:hidden;position:relative}.toastui-calendar-panel .toastui-calendar-panel-grid-wrapper,.toastui-calendar-panel .toastui-calendar-panel-title{height:100%}.toastui-calendar-allday-panel{height:100%;overflow-y:hidden;position:relative}.toastui-calendar-allday-panel .toastui-calendar-grid-selection{position:absolute;right:10px;top:0;z-index:1}.toastui-calendar-panel-grid{height:100%;position:absolute}.toastui-calendar-panel-event-wrapper{height:100%;left:0;overflow-y:scroll;position:absolute;top:0;width:100%}.toastui-calendar-panel-event-wrapper .toastui-calendar-weekday-event-block{position:absolute}.toastui-calendar-panel-event-wrapper .toastui-calendar-weekday-event{background-color:rgba(218,27,27,.2);border-left:3px solid;border-color:#da1b1b;border-radius:0;color:#9a1313;cursor:pointer;height:18px;margin:0 10px 0 1px;position:relative}.toastui-calendar-panel-event-wrapper .toastui-calendar-weekday-exceed-right .toastui-calendar-weekday-event{margin-right:0}.toastui-calendar-panel-event{border:1px solid #333;position:absolute}.toastui-calendar-weekday-exceed-in-week{background-color:#fff;border:1px solid #ddd;bottom:5px;color:#000;cursor:pointer;font-size:12px;line-height:14px;margin-right:5px;padding:1px 5px;position:absolute;right:5px;z-index:1}.toastui-calendar-collapse-btn-icon{border-bottom:5px solid #4f5959;border-left:4px solid transparent;border-right:4px solid transparent;display:inline-block;height:0;margin:-1px -14px 0 -4px;vertical-align:middle;width:0}.toastui-calendar-day-view .toastui-calendar-panel:not(.toastui-calendar-time),.toastui-calendar-week-view .toastui-calendar-panel:not(.toastui-calendar-time){overflow-y:scroll}.toastui-calendar-floating-layer{z-index:1}.toastui-calendar-floating-layer *{box-sizing:border-box}.toastui-calendar-popup-overlay{height:100%;left:0;position:absolute;top:0;width:100%}.toastui-calendar-popup-container{box-shadow:0 2px 6px 0 rgba(0,0,0,.1);clear:both;font-weight:2.5;position:absolute;z-index:2}.toastui-calendar-popup-section{font-size:0;min-height:40px}.toastui-calendar-popup-button.toastui-calendar-popup-close{background-color:#fff;border:none;padding:0;position:absolute;right:10px;top:10px}.toastui-calendar-popup-button.toastui-calendar-popup-confirm{background-color:#ff6618;border:none;border-radius:40px;color:#fff;float:right;font-size:12px;font-weight:700;height:36px;width:96px}.toastui-calendar-dropdown-menu{background-color:#fff;border:1px solid #d5d5d5;border-radius:0 0 2px 2px;border-top:none;padding:4px 0;position:absolute;top:31px;width:100%;z-index:1}.toastui-calendar-dropdown-menu.toastui-calendar-open{display:block}.toastui-calendar-dropdown-menu-item{border:none;border-radius:2px;cursor:pointer;font-size:0;height:30px;padding:0 9px 0 12px;width:100%}.toastui-calendar-popup-arrow-border,.toastui-calendar-popup-arrow-fill{position:absolute}.toastui-calendar-see-more-container{display:block;position:absolute;z-index:1}.toastui-calendar-see-more{height:inherit;padding:5px}.toastui-calendar-more-title-date{color:#333;font-size:23px}.toastui-calendar-more-title-day{color:#333;font-size:12px}.toastui-calendar-month-more-list{overflow:auto;padding:0 17px}.toastui-calendar-see-more-header{border-bottom:none;position:relative}.toastui-calendar-form-container{background-color:#fff;border:1px solid #d5d5d5;border-radius:2px;box-shadow:0 2px 6px 0 rgba(0,0,0,.1);min-width:474px;padding:17px}.toastui-calendar-form-container .toastui-calendar-hidden-input{display:none}.toastui-calendar-form-container .toastui-calendar-grid-selection{font-size:11px;font-weight:700}.toastui-calendar-popup-section-item{border:1px solid #d5d5d5;border-radius:2px;display:inline-block;font-size:0;height:32px;padding:0 9px 0 12px}.toastui-calendar-popup-section-item input{border:none;display:inline-block;height:30px;outline:none}.toastui-calendar-popup-section-item .toastui-calendar-content{display:inline-block;font-size:12px;padding-left:8px;position:relative;text-align:left;vertical-align:middle}.toastui-calendar-popup-date-picker .toastui-calendar-content{max-width:125px}.toastui-calendar-dropdown-section{position:relative}.toastui-calendar-dropdown-section.toastui-calendar-calendar-section{width:176px}.toastui-calendar-dropdown-section .toastui-calendar-content{line-height:30px}.toastui-calendar-popup-section-title input{width:365px}.toastui-calendar-dot{border-radius:8px;height:12px;margin:1px;width:12px}.toastui-calendar-content.toastui-calendar-event-calendar{overflow:hidden;text-overflow:ellipsis;top:-1px;white-space:nowrap;width:125px}.toastui-calendar-popup-section-location .toastui-calendar-content{width:400px}.toastui-calendar-popup-section-allday{border:none;cursor:pointer;padding:0 0 0 8px}.toastui-calendar-popup-section-allday .toastui-calendar-ic-checkbox-normal{cursor:pointer;display:inline-block;height:14px;line-height:14px;margin:0;vertical-align:middle;width:14px}.toastui-calendar-popup-section-allday .toastui-calendar-content{padding-left:4px}.toastui-calendar-popup-date-picker{width:176px}.toastui-calendar-datepicker-container>div{z-index:1}.toastui-calendar-popup-date-dash{color:#d5d5d5;font-size:12px;height:32px;padding:0 4px;vertical-align:middle}.toastui-calendar-popup-button{background:#fff;border:1px solid #d5d5d5;border-radius:2px;color:#333;cursor:pointer;font-size:12px;outline:none;text-align:center}.toastui-calendar-popup-button.toastui-calendar-popup-section-private{font-size:0;height:32px;margin-left:4px;padding:8px}.toastui-calendar-popup-button .toastui-calendar-event-state{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:58px}.toastui-calendar-dropdown-section.toastui-calendar-state-section{width:109px}.toastui-calendar-dropdown-section.toastui-calendar-state-section .toastui-calendar-popup-button{width:100%}.toastui-calendar-state-section .toastui-calendar-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:58px}.toastui-calendar-popup-section-item.toastui-calendar-dropdown-menu-item{border:none;cursor:pointer;display:block;height:30px}.toastui-calendar-dropdown-menu-item .toastui-calendar-content{display:inline-block;font-size:12px;padding-left:8px;position:relative;text-align:left;vertical-align:middle}.toastui-calendar-popup-section-item.toastui-calendar-popup-button{font-size:0;height:32px;top:-1px}.toastui-calendar-popup-arrow.toastui-calendar-top .toastui-calendar-popup-arrow-border{border:8px solid transparent;border-bottom:8px solid #d5d5d5;border-top:none;left:calc(50% - 8px);top:-7px}.toastui-calendar-popup-arrow.toastui-calendar-top .toastui-calendar-popup-arrow-fill{border:7px solid transparent;border-bottom:7px solid #fff;border-top:none;left:-7px;top:1px}.toastui-calendar-popup-arrow.toastui-calendar-bottom .toastui-calendar-popup-arrow-border{border:8px solid transparent;border-bottom:none;border-top-color:#d5d5d5;bottom:-7px}.toastui-calendar-popup-arrow.toastui-calendar-bottom .toastui-calendar-popup-arrow-fill{border:7px solid transparent;border-bottom:none;border-top-color:#fff;bottom:1px;left:-7px}.toastui-calendar-detail-container{background-color:#fff;border:1px solid #d5d5d5;border-radius:2px;box-shadow:0 2px 6px 0 rgba(0,0,0,.1);min-width:301px;padding:17px 17px 0;width:301px}.toastui-calendar-detail-container .toastui-calendar-section-header{margin-bottom:6px}.toastui-calendar-detail-container .toastui-calendar-section-detail{margin-bottom:16px}.toastui-calendar-detail-container .toastui-calendar-section-button{border-top:1px solid #e5e5e5;font-size:0}.toastui-calendar-detail-container .toastui-calendar-content{font-size:12px;height:24px;line-height:2}.toastui-calendar-detail-container .toastui-calendar-icon{background-size:12px;height:12px;margin-right:8px;position:relative;width:12px}.toastui-calendar-detail-container .toastui-calendar-calendar-dot{border-radius:50%;height:10px;margin-right:10px;top:-4px;width:10px}.toastui-calendar-event-title{font-size:15px;font-weight:700;line-height:1.6;word-break:break-all}.toastui-calendar-detail-item-indent{padding-left:20px;text-indent:-20px}.toastui-calendar-delete-button,.toastui-calendar-edit-button{background:none;border:none;cursor:pointer;display:inline-block;outline:none;padding:7px 9px 11px;width:calc(50% - 1px)}.toastui-calendar-vertical-line{background:#e5e5e5;display:inline-block;height:14px;margin-top:-7px;vertical-align:middle;width:1px}.toastui-calendar-section-button .toastui-calendar-icon{margin-right:4px;top:-3px}.toastui-calendar-section-button .toastui-calendar-content{position:relative;top:2px}.toastui-calendar-popup-top-line{border:none;border-radius:2px 2px 0 0;height:4px;position:absolute;top:0;width:100%}.toastui-calendar-popup-arrow.toastui-calendar-left .toastui-calendar-popup-arrow-border{border:8px solid transparent;border-left:none;border-right-color:#d5d5d5;left:-7px}.toastui-calendar-popup-arrow.toastui-calendar-left .toastui-calendar-popup-arrow-fill{border:7px solid transparent;border-left:none;border-right-color:#fff;left:1px;top:-7px}.toastui-calendar-popup-arrow.toastui-calendar-right .toastui-calendar-popup-arrow-border{border:8px solid transparent;border-left:8px solid #d5d5d5;border-right:none;right:-7px}.toastui-calendar-popup-arrow.toastui-calendar-right .toastui-calendar-popup-arrow-fill{border:7px solid transparent;border-left:7px solid #fff;border-right:none;right:1px;top:-7px}.toastui-calendar-day-name-container,.toastui-calendar-day-names{position:relative}.toastui-calendar-day-name-item{font-size:12px;font-weight:400;padding:0 10px;position:absolute;text-align:left}.toastui-calendar-day-name-item.toastui-calendar-week{height:42px;line-height:38px}.toastui-calendar-day-name-item.toastui-calendar-month{height:31px;line-height:31px}.toastui-calendar-day-view-day-names,.toastui-calendar-week-view-day-names{border-bottom:1px solid #e5e5e5}.toastui-calendar-day-names.toastui-calendar-week{height:42px;padding-left:0;text-align:left}.toastui-calendar-day-names.toastui-calendar-month{font-size:12px;font-weight:400;height:31px;padding:0 10px;text-align:left}.toastui-calendar-day-name__date{font-size:26px}.toastui-calendar-day-name__name{font-size:12px}.toastui-calendar-layout.toastui-calendar-month{height:100%}.toastui-calendar-month .toastui-calendar-day-names{height:31px}.toastui-calendar-month .toastui-calendar-month-daygrid{height:calc(100% - 31px);position:relative}.toastui-calendar-month-week-item{position:relative}.toastui-calendar-weekday-grid{height:100%;min-height:inherit;position:absolute;width:100%}.toastui-calendar-daygrid-cell{height:100%;min-height:inherit;padding:3px 0;position:absolute}.toastui-calendar-daygrid-cell+.toastui-calendar-daygrid-cell{border-left:1px solid #e5e5e5}.toastui-calendar-grid-cell-date{display:inline-block;height:27px;line-height:1.7;text-align:center;width:27px}.toastui-calendar-grid-cell-footer{bottom:0;position:absolute;width:100%}.toastui-calendar-grid-cell-more-events{background-color:transparent;border:none;color:#aaa;cursor:pointer;float:right;font-size:11px;font-weight:700;height:27px;line-height:27px;padding:0 5px;text-align:center}.toastui-calendar-weekday-events{font-size:12px;left:0;position:absolute;top:0;width:100%}.toastui-calendar-weekday-event{cursor:pointer}.toastui-calendar-weekday{height:100%}.toastui-calendar-weekday .toastui-calendar-grid-selection{position:absolute} \ No newline at end of file diff --git a/app/static/libjs/tui.calendar/toastui-calendar.min.js b/app/static/libjs/tui.calendar/toastui-calendar.min.js new file mode 100644 index 0000000000..d3554d5402 --- /dev/null +++ b/app/static/libjs/tui.calendar/toastui-calendar.min.js @@ -0,0 +1,9 @@ +/*! + * TOAST UI Calendar 2nd Edition + * @version 2.1.3 | Tue Aug 16 2022 + * @author NHN Cloud FE Development Lab + * @license MIT + */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("tui-date-picker")):"function"==typeof define&&define.amd?define(["tui-date-picker"],t):"object"==typeof exports?exports.tui=t(require("tui-date-picker")):(e.tui=e.tui||{},e.tui.Calendar=t(e.tui.DatePicker))}(this,(function(e){return function(){var t={7111:function(e,t,n){var r=n(6733),o=n(9821),i=TypeError;e.exports=function(e){if(r(e))return e;throw i(o(e)+" is not a function")}},8505:function(e,t,n){var r=n(6733),o=String,i=TypeError;e.exports=function(e){if("object"==typeof e||r(e))return e;throw i("Can't set "+o(e)+" as a prototype")}},9736:function(e,t,n){var r=n(95),o=n(2391),i=n(1787).f,a=r("unscopables"),l=Array.prototype;null==l[a]&&i(l,a,{configurable:!0,value:o(null)}),e.exports=function(e){l[a][e]=!0}},6637:function(e,t,n){"use strict";var r=n(966).charAt;e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},1176:function(e,t,n){var r=n(5052),o=String,i=TypeError;e.exports=function(e){if(r(e))return e;throw i(o(e)+" is not an object")}},9540:function(e,t,n){var r=n(905),o=n(3231),i=n(9646),a=function(e){return function(t,n,a){var l,c=r(t),s=i(c),u=o(a,s);if(e&&n!=n){for(;s>u;)if((l=c[u++])!=l)return!0}else for(;s>u;u++)if((e||u in c)&&c[u]===n)return e||u||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},7079:function(e,t,n){var r=n(5968),o=r({}.toString),i=r("".slice);e.exports=function(e){return i(o(e),8,-1)}},1589:function(e,t,n){var r=n(1601),o=n(6733),i=n(7079),a=n(95)("toStringTag"),l=Object,c="Arguments"==i(function(){return arguments}());e.exports=r?i:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=l(e),a))?n:c?i(t):"Object"==(r=i(t))&&o(t.callee)?"Arguments":r}},1590:function(e,t,n){var r=n(5968),o=Error,i=r("".replace),a=String(o("zxcasd").stack),l=/\n\s*at [^:]*:[^\n]*/,c=l.test(a);e.exports=function(e,t){if(c&&"string"==typeof e&&!o.prepareStackTrace)for(;t--;)e=i(e,l,"");return e}},7081:function(e,t,n){var r=n(8270),o=n(4826),i=n(7933),a=n(1787);e.exports=function(e,t,n){for(var l=o(t),c=a.f,s=i.f,u=0;u0&&r[0]<4?1:+(r[0]+r[1])),!o&&a&&(!(r=a.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=a.match(/Chrome\/(\d+)/))&&(o=+r[1]),e.exports=o},3837:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},373:function(e,t,n){var r=n(4229),o=n(5358);e.exports=!r((function(){var e=Error("a");return!("stack"in e)||(Object.defineProperty(e,"stack",o(1,7)),7!==e.stack)}))},3103:function(e,t,n){var r=n(9859),o=n(7933).f,i=n(5762),a=n(4768),l=n(8400),c=n(7081),s=n(6541);e.exports=function(e,t){var n,u,d,f,p,h=e.target,m=e.global,g=e.stat;if(n=m?r:g?r[h]||l(h,{}):(r[h]||{}).prototype)for(u in t){if(f=t[u],d=e.dontCallGetSet?(p=o(n,u))&&p.value:n[u],!s(m?u:h+(g?".":"#")+u,e.forced)&&void 0!==d){if(typeof f==typeof d)continue;c(f,d)}(e.sham||d&&d.sham)&&i(f,"sham",!0),a(n,u,f,e)}}},4229:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},4954:function(e,t,n){"use strict";n(7950);var r=n(5968),o=n(4768),i=n(3466),a=n(4229),l=n(95),c=n(5762),s=l("species"),u=RegExp.prototype;e.exports=function(e,t,n,d){var f=l(e),p=!a((function(){var t={};return t[f]=function(){return 7},7!=""[e](t)})),h=p&&!a((function(){var t=!1,n=/a/;return"split"===e&&((n={}).constructor={},n.constructor[s]=function(){return n},n.flags="",n[f]=/./[f]),n.exec=function(){return t=!0,null},n[f](""),!t}));if(!p||!h||n){var m=r(/./[f]),g=t(f,""[e],(function(e,t,n,o,a){var l=r(e),c=t.exec;return c===i||c===u.exec?p&&!a?{done:!0,value:m(t,n,o)}:{done:!0,value:l(n,t,o)}:{done:!1}}));o(String.prototype,e,g[0]),o(u,f,g[1])}d&&c(u[f],"sham",!0)}},3171:function(e,t,n){var r=n(7188),o=Function.prototype,i=o.apply,a=o.call;e.exports="object"==typeof Reflect&&Reflect.apply||(r?a.bind(i):function(){return a.apply(i,arguments)})},7188:function(e,t,n){var r=n(4229);e.exports=!r((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")}))},266:function(e,t,n){var r=n(7188),o=Function.prototype.call;e.exports=r?o.bind(o):function(){return o.apply(o,arguments)}},1805:function(e,t,n){var r=n(7400),o=n(8270),i=Function.prototype,a=r&&Object.getOwnPropertyDescriptor,l=o(i,"name"),c=l&&"something"===function(){}.name,s=l&&(!r||r&&a(i,"name").configurable);e.exports={EXISTS:l,PROPER:c,CONFIGURABLE:s}},5968:function(e,t,n){var r=n(7188),o=Function.prototype,i=o.bind,a=o.call,l=r&&i.bind(a,a);e.exports=r?function(e){return e&&l(e)}:function(e){return e&&function(){return a.apply(e,arguments)}}},1333:function(e,t,n){var r=n(9859),o=n(6733),i=function(e){return o(e)?e:void 0};e.exports=function(e,t){return arguments.length<2?i(r[e]):r[e]&&r[e][t]}},5300:function(e,t,n){var r=n(7111);e.exports=function(e,t){var n=e[t];return null==n?void 0:r(n)}},17:function(e,t,n){var r=n(5968),o=n(2991),i=Math.floor,a=r("".charAt),l=r("".replace),c=r("".slice),s=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,u=/\$([$&'`]|\d{1,2})/g;e.exports=function(e,t,n,r,d,f){var p=n+e.length,h=r.length,m=u;return void 0!==d&&(d=o(d),m=s),l(f,m,(function(o,l){var s;switch(a(l,0)){case"$":return"$";case"&":return e;case"`":return c(t,0,n);case"'":return c(t,p);case"<":s=d[c(l,1,-1)];break;default:var u=+l;if(0===u)return o;if(u>h){var f=i(u/10);return 0===f?o:f<=h?void 0===r[f-1]?a(l,1):r[f-1]+a(l,1):o}s=r[u-1]}return void 0===s?"":s}))}},9859:function(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},8270:function(e,t,n){var r=n(5968),o=n(2991),i=r({}.hasOwnProperty);e.exports=Object.hasOwn||function(e,t){return i(o(e),t)}},5977:function(e){e.exports={}},3777:function(e,t,n){var r=n(1333);e.exports=r("document","documentElement")},4394:function(e,t,n){var r=n(7400),o=n(4229),i=n(2635);e.exports=!r&&!o((function(){return 7!=Object.defineProperty(i("div"),"a",{get:function(){return 7}}).a}))},9337:function(e,t,n){var r=n(5968),o=n(4229),i=n(7079),a=Object,l=r("".split);e.exports=o((function(){return!a("z").propertyIsEnumerable(0)}))?function(e){return"String"==i(e)?l(e,""):a(e)}:a},835:function(e,t,n){var r=n(6733),o=n(5052),i=n(6540);e.exports=function(e,t,n){var a,l;return i&&r(a=t.constructor)&&a!==n&&o(l=a.prototype)&&l!==n.prototype&&i(e,l),e}},8511:function(e,t,n){var r=n(5968),o=n(6733),i=n(5353),a=r(Function.toString);o(i.inspectSource)||(i.inspectSource=function(e){return a(e)}),e.exports=i.inspectSource},9679:function(e,t,n){var r=n(5052),o=n(5762);e.exports=function(e,t){r(t)&&"cause"in t&&o(e,"cause",t.cause)}},6407:function(e,t,n){var r,o,i,a=n(8694),l=n(9859),c=n(5968),s=n(5052),u=n(5762),d=n(8270),f=n(5353),p=n(4399),h=n(5977),m="Object already initialized",g=l.TypeError,v=l.WeakMap;if(a||f.state){var y=f.state||(f.state=new v),w=c(y.get),_=c(y.has),b=c(y.set);r=function(e,t){if(_(y,e))throw new g(m);return t.facade=e,b(y,e,t),t},o=function(e){return w(y,e)||{}},i=function(e){return _(y,e)}}else{var x=p("state");h[x]=!0,r=function(e,t){if(d(e,x))throw new g(m);return t.facade=e,u(e,x,t),t},o=function(e){return d(e,x)?e[x]:{}},i=function(e){return d(e,x)}}e.exports={set:r,get:o,has:i,enforce:function(e){return i(e)?o(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!s(t)||(n=o(t)).type!==e)throw g("Incompatible receiver, "+e+" required");return n}}}},6733:function(e){e.exports=function(e){return"function"==typeof e}},6541:function(e,t,n){var r=n(4229),o=n(6733),i=/#|\.prototype\./,a=function(e,t){var n=c[l(e)];return n==u||n!=s&&(o(t)?r(t):!!t)},l=a.normalize=function(e){return String(e).replace(i,".").toLowerCase()},c=a.data={},s=a.NATIVE="N",u=a.POLYFILL="P";e.exports=a},5052:function(e,t,n){var r=n(6733);e.exports=function(e){return"object"==typeof e?null!==e:r(e)}},4231:function(e){e.exports=!1},9395:function(e,t,n){var r=n(1333),o=n(6733),i=n(1321),a=n(6969),l=Object;e.exports=a?function(e){return"symbol"==typeof e}:function(e){var t=r("Symbol");return o(t)&&i(t.prototype,l(e))}},693:function(e,t,n){"use strict";var r,o,i,a=n(4229),l=n(6733),c=n(2391),s=n(7567),u=n(4768),d=n(95),f=n(4231),p=d("iterator"),h=!1;[].keys&&("next"in(i=[].keys())?(o=s(s(i)))!==Object.prototype&&(r=o):h=!0),null==r||a((function(){var e={};return r[p].call(e)!==e}))?r={}:f&&(r=c(r)),l(r[p])||u(r,p,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:h}},5495:function(e){e.exports={}},9646:function(e,t,n){var r=n(4237);e.exports=function(e){return r(e.length)}},6039:function(e,t,n){var r=n(4229),o=n(6733),i=n(8270),a=n(7400),l=n(1805).CONFIGURABLE,c=n(8511),s=n(6407),u=s.enforce,d=s.get,f=Object.defineProperty,p=a&&!r((function(){return 8!==f((function(){}),"length",{value:8}).length})),h=String(String).split("String"),m=e.exports=function(e,t,n){"Symbol("===String(t).slice(0,7)&&(t="["+String(t).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),n&&n.getter&&(t="get "+t),n&&n.setter&&(t="set "+t),(!i(e,"name")||l&&e.name!==t)&&(a?f(e,"name",{value:t,configurable:!0}):e.name=t),p&&n&&i(n,"arity")&&e.length!==n.arity&&f(e,"length",{value:n.arity});try{n&&i(n,"constructor")&&n.constructor?a&&f(e,"prototype",{writable:!1}):e.prototype&&(e.prototype=void 0)}catch(e){}var r=u(e);return i(r,"source")||(r.source=h.join("string"==typeof t?t:"")),e};Function.prototype.toString=m((function(){return o(this)&&d(this).source||c(this)}),"toString")},917:function(e){var t=Math.ceil,n=Math.floor;e.exports=Math.trunc||function(e){var r=+e;return(r>0?n:t)(r)}},3839:function(e,t,n){var r=n(6358),o=n(4229);e.exports=!!Object.getOwnPropertySymbols&&!o((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},8694:function(e,t,n){var r=n(9859),o=n(6733),i=n(8511),a=r.WeakMap;e.exports=o(a)&&/native code/.test(i(a))},635:function(e,t,n){var r=n(3326);e.exports=function(e,t){return void 0===e?arguments.length<2?"":t:r(e)}},2391:function(e,t,n){var r,o=n(1176),i=n(219),a=n(3837),l=n(5977),c=n(3777),s=n(2635),u=n(4399),d=u("IE_PROTO"),f=function(){},p=function(e){return" + +{% endblock scripts %} diff --git a/app/views/notes_formsemestre.py b/app/views/notes_formsemestre.py index 843370422c..eac987603f 100644 --- a/app/views/notes_formsemestre.py +++ b/app/views/notes_formsemestre.py @@ -105,3 +105,16 @@ def formsemestre_change_formation(formsemestre_id: int): formsemestre=formsemestre, sco=ScoData(formsemestre=formsemestre), ) + + +@bp.route("/formsemestre/edt/") +@scodoc +@permission_required(Permission.ScoView) +def formsemestre_edt(formsemestre_id: int): + """Expérimental: affiche emploi du temps du semestre""" + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + return render_template( + "formsemestre/edt.j2", + formsemestre=formsemestre, + sco=ScoData(formsemestre=formsemestre), + ) diff --git a/sco_version.py b/sco_version.py index 7771d1df9d..08f5d05ebd 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.51" +SCOVERSION = "9.6.52" SCONAME = "ScoDoc"