From 2ff49fc1bd6b20340579f82f555bc8410462d951 Mon Sep 17 00:00:00 2001 From: Iziram Date: Fri, 3 Nov 2023 16:55:26 +0100 Subject: [PATCH 01/17] WIP passage moment - vanilla js --- app/static/js/assiduites.js | 219 ++++++++++++++---- .../assiduites/pages/ajout_justificatif.j2 | 8 +- app/templates/assiduites/pages/bilan_etud.j2 | 6 +- app/templates/assiduites/pages/calendrier.j2 | 17 +- 4 files changed, 191 insertions(+), 59 deletions(-) diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index 90ed21d64..f4f98cc7c 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -32,6 +32,17 @@ Object.defineProperty(String.prototype, "capitalize", { }, enumerable: false, }); + +const DatePrecisions = [ + "year", + "month", + "day", + "hour", + "minute", + "second", + "millisecond", +]; + // <<== Outils ==>> Object.defineProperty(Array.prototype, "reversed", { value: function () { @@ -40,6 +51,132 @@ Object.defineProperty(Array.prototype, "reversed", { enumerable: false, }); +// <= Gestion des dates => + +Object.defineProperty(Date.prototype, "isBetween", { + /** + * Vérifie si la date est comprise dans une période avec une précision et une inclusivité optionnelles + * @param {Date} deb - La date de début de la période + * @param {Date} fin - La date de fin de la période + * @param {String} precision - La précision pour la comparaison (année, mois, jour, etc.) + * @param {String} bornes - L'inclusivité/exclusivité de la comparaison ("[]", "()", "[)", "(]") + */ + value: function (deb, fin, precision, bornes = "[]") { + // Ajuste la date actuelle, la date de début et la date de fin à la précision spécifiée + const thisPrecision = this.toPrecision(precision); + const debPrecision = deb.toPrecision(precision); + const finPrecision = fin.toPrecision(precision); + + // Vérifie les bornes en fonction de l'inclusivité/exclusivité spécifiée dans 'bornes' + const check_deb = + bornes[0] === "(" + ? thisPrecision > debPrecision + : thisPrecision >= debPrecision; + const check_fin = + bornes[1] === ")" + ? finPrecision > thisPrecision + : finPrecision >= thisPrecision; + + return check_deb && check_fin; + }, +}); + +Object.defineProperty(Date.prototype, "toPrecision", { + /** + * Ajuste la date à la précision donnée. + * @param {String} precision - La précision désirée (année, mois, jour, etc.) + */ + value: function (precision) { + const newDate = new Date(this.getTime()); + + // Trouve l'indice de la précision spécifiée dans le tableau des précisions + const precisionsIndex = + precision != undefined + ? DatePrecisions.indexOf(precision) + : DatePrecisions.length - 1; + + // Réinitialise toutes les parties de la date moins significatives que la précision spécifiée + for (let i = precisionsIndex + 1; i < DatePrecisions.length; i++) { + const p = DatePrecisions[i]; + switch (p) { + case "month": + // Les mois en JavaScript sont indexés à partir de 0, donc on met 0 pour janvier + newDate.setMonth(0); + break; + case "day": + // Les jours en JavaScript commencent à 1, donc on met 1 pour le premier jour du mois + newDate.setDate(1); + break; + case "hour": + newDate.setHours(0); + break; + case "minute": + newDate.setMinutes(0); + break; + case "second": + newDate.setSeconds(0); + break; + case "millisecond": + newDate.setMilliseconds(0); + break; + } + } + + return newDate; + }, +}); + +Object.defineProperty(Date.prototype, "isBefore", { + value: function (date) { + return this < date; + }, +}); +Object.defineProperty(Date.prototype, "isAfter", { + value: function (date) { + return this > date; + }, +}); + +Object.defineProperty(Date.prototype, "isSame", { + /** + * Retourne vrai si les dates sont les mêmes + * @param {Date} date + * @returns boolean + */ + value: function (date) { + return this == date; + }, +}); + +Object.defineProperty(Date.prototype, "toIsoUtcString", { + value: function () { + const date = this; + var tzo = -date.getTimezoneOffset(), + dif = tzo >= 0 ? "+" : "-", + pad = function (num) { + return (num < 10 ? "0" : "") + num; + }; + + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + "T" + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + + dif + + pad(Math.floor(Math.abs(tzo) / 60)) + + ":" + + pad(Math.abs(tzo) % 60) + ); + }, +}); + /** * Ajout des évents sur les boutons d'assiduité * @param {Document | HTMLFieldSetElement} parent par défaut le document, un field sinon @@ -126,7 +263,7 @@ function validateSelectors(btn) { } function onlyAbs() { - if (getDate() > moment()) { + if (getDate() > Date.now()) { document .querySelectorAll(".rbtn.present, .rbtn.retard") .forEach((el) => el.remove()); @@ -268,8 +405,8 @@ function executeMassActionQueue() { */ const tlTimes = getTimeLineTimes(); let assiduite = { - date_debut: tlTimes.deb.format(), - date_fin: tlTimes.fin.format(), + date_debut: tlTimes.deb.toIsoUtcString(), + date_fin: tlTimes.fin.toIsoUtcString(), }; assiduite = setModuleImplId(assiduite); @@ -613,19 +750,11 @@ function getNearestWorkDay(date) { } function verifyDateInSemester() { - const date = new moment.tz( - document.querySelector("#tl_date").value, - TIMEZONE - ); + const date = getDate(); const periodSemester = getFormSemestreDates(); - return date.isBetween( - periodSemester.deb, - periodSemester.fin, - undefined, - "[]" - ); + return date.isBetween(periodSemester.deb, periodSemester.fin, "[]"); } /** @@ -664,8 +793,8 @@ function getAssiduitesOnDateChange() { * @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00) * @returns {String} la date intelligible */ -function formatDateModal(str, separator = "·") { - return new moment.tz(str, TIMEZONE).format(`DD/MM/Y ${separator} HH:mm`); +function formatDateModal(str) { + return new Date(str).toLocaleString("fr-FR"); } /** @@ -705,8 +834,8 @@ function verifyNonWorkDays(day, nonWorkdays) { * Fonction qui vérifie si une période est dans un interval * Objet période / interval * { - * deb: moment.tz(), - * fin: moment.tz(), + * deb: Date, + * fin: Date, * } * @param {object} period * @param {object} interval @@ -718,7 +847,7 @@ function hasTimeConflict(period, interval) { /** * On récupère la période de la timeline - * @returns {deb : moment.tz(), fin: moment.tz()} + * @returns {deb : Date, fin: Date)} */ function getTimeLineTimes() { //getPeriodValues() -> retourne la position de la timeline [a,b] avec a et b des number @@ -726,11 +855,11 @@ function getTimeLineTimes() { //On récupère la date const dateiso = document.querySelector("#tl_date").value; - //On génère des objets temps (moment.tz) + //On génère des objets temps values = values.map((el) => { el = toTime(el).replace("h", ":"); el = `${dateiso}T${el}`; - return moment.tz(el, TIMEZONE); + return new Date(el); }); return { deb: values[0], fin: values[1] }; @@ -744,8 +873,8 @@ function getTimeLineTimes() { function isConflictSameAsPeriod(conflict, period = undefined) { const tlTimes = period == undefined ? getTimeLineTimes() : period; const clTimes = { - deb: moment.tz(conflict.date_debut, TIMEZONE), - fin: moment.tz(conflict.date_fin, TIMEZONE), + deb: new Date(conflict.date_debut), + fin: new Date(conflict.date_fin), }; return tlTimes.deb.isSame(clTimes.deb) && tlTimes.fin.isSame(clTimes.fin); } @@ -815,9 +944,9 @@ function toIsoString(date) { } /** - * Transforme un temps numérique en une date moment.tz + * Transforme un temps numérique en une date * @param {number} nb - * @returns {moment.tz} Une date formée du temps donné et de la date courante + * @returns {Date} Une date formée du temps donné et de la date courante */ function numberTimeToDate(nb) { time = toTime(nb).replace("h", ":"); @@ -825,7 +954,7 @@ function numberTimeToDate(nb) { datetime = `${date}T${time}`; - return moment.tz(datetime, TIMEZONE); + return new Date(datetime); } // <<== Gestion des assiduités ==>> @@ -885,8 +1014,8 @@ function getAssiduitesFromEtuds(clear, deb, fin) { function createAssiduite(etat, etudid) { const tlTimes = getTimeLineTimes(); let assiduite = { - date_debut: tlTimes.deb.format(), - date_fin: tlTimes.fin.format(), + date_debut: tlTimes.deb.toIsoUtcString(), + date_fin: tlTimes.fin.toIsoUtcString(), etat: etat, }; @@ -1067,8 +1196,8 @@ function getAssiduitesConflict(etudid, periode) { return etudAssiduites.filter((assi) => { const interval = { - deb: moment.tz(assi.date_debut, TIMEZONE), - fin: moment.tz(assi.date_fin, TIMEZONE), + deb: new Date(assi.date_debut), + fin: new Date(assi.date_debut), }; return hasTimeConflict(periode, interval); }); @@ -1085,21 +1214,21 @@ function getLastAssiduiteOfPrevDate(etudid) { return ""; } const period = { - deb: moment.tz(getPrevDate(), TIMEZONE), - fin: moment.tz(getDate(), TIMEZONE), + deb: getPrevDate(), + fin: getDate(), }; const prevAssiduites = etudAssiduites .filter((assi) => { const interval = { - deb: moment.tz(assi.date_debut, TIMEZONE), - fin: moment.tz(assi.date_fin, TIMEZONE), + deb: new Date(assi.date_debut), + fin: new Date(assi.date_fin), }; return hasTimeConflict(period, interval); }) .sort((a, b) => { - const a_fin = moment.tz(a.date_fin, TIMEZONE); - const b_fin = moment.tz(b.date_fin, TIMEZONE); + const a_fin = new Date(a.date_fin); + const b_fin = new Date(b.date_fin); return b_fin < a_fin; }); @@ -1232,8 +1361,8 @@ function assiduiteAction(element) { assiduites[etudid], getTimeLineTimes(), { - deb: new moment.tz(getDate(), TIMEZONE), - fin: new moment.tz(getNextDate(), TIMEZONE), + deb: getDate(), + fin: getNextDate(), } ); const update = (assi) => { @@ -1545,8 +1674,8 @@ function getFormSemestreDates() { const dateFin = document.getElementById("formsemestre_date_fin").textContent; return { - deb: dateDeb, - fin: dateFin, + deb: new Date(dateDeb), + fin: new Date(dateFin), }; } @@ -1614,7 +1743,9 @@ function getJustificatifFromPeriod(date, etudid, update) { getUrl() + `/api/justificatifs/${etudid}/query?date_debut=${date.deb .add(1, "s") - .format()}&date_fin=${date.fin.subtract(1, "s").format()}`, + .toIsoUtcString()}&date_fin=${date.fin + .subtract(1, "s") + .toIsoUtcString()}`, success: (data) => { update(data); }, @@ -1646,8 +1777,8 @@ function fastJustify(assiduite) { } const period = { - deb: new moment.tz(assiduite.date_debut, TIMEZONE), - fin: new moment.tz(assiduite.date_fin, TIMEZONE), + deb: new Date(assiduite.date_debut), + fin: new Date(assiduite.date_fin), }; const action = (justifs) => { //créer un nouveau justificatif @@ -1660,8 +1791,8 @@ function fastJustify(assiduite) { //créer justificatif const justif = { - date_debut: new moment.tz(assiduite.date_debut, TIMEZONE).format(), - date_fin: new moment.tz(assiduite.date_fin, TIMEZONE).format(), + date_debut: new Date(assiduite.date_debut).toIsoUtcString(), + date_fin: new Date(assiduite.date_fin).toIsoUtcString(), raison: raison, etat: etat, }; diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index c4885b3f8..83e7366c3 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -118,8 +118,8 @@ return false; } - const date_debut = moment.tz(deb, TIMEZONE); - const date_fin = moment.tz(fin, TIMEZONE); + const date_debut = new Date(deb); + const date_fin = new Date(fin); if (date_fin.isBefore(date_debut)) { openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson"); @@ -138,8 +138,8 @@ const raison = field.querySelector('#justi_raison').value; return { - date_debut: moment.tz(deb, TIMEZONE).format(), - date_fin: moment.tz(fin, TIMEZONE).format(), + date_debut: new Date(deb).toIsoUtcString(), + date_fin: new Date(fin).toIsoUtcString(), etat: etat, raison: raison, } diff --git a/app/templates/assiduites/pages/bilan_etud.j2 b/app/templates/assiduites/pages/bilan_etud.j2 index dad94d274..bba1dae0e 100644 --- a/app/templates/assiduites/pages/bilan_etud.j2 +++ b/app/templates/assiduites/pages/bilan_etud.j2 @@ -93,8 +93,8 @@ return; } - const date_debut = new moment.tz(dd_val + "T00:00", TIMEZONE); - const date_fin = new moment.tz(df_val + "T23:59", TIMEZONE); + const date_debut = new Date(dd_val + "T00:00"); + const date_fin = new Date(df_val + "T23:59"); if (date_debut.valueOf() > date_fin.valueOf()) { openAlertModal("Dates invalides", document.createTextNode('La date de début se situe après la date de fin.')); @@ -102,7 +102,7 @@ } - countAssiduites(date_debut.format(), date_fin.format()) + countAssiduites(date_debut.toIsoUtcString()(), date_fin.toIsoUtcString()()) } diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index d976f2cad..e4e793d39 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -126,7 +126,7 @@ let datesByMonth = {}; dates.forEach((date) => { - let month = date.format("MMMM"); // Obtenir le mois + let month = date.toLocaleString('fr-FR', { month: "short" }); // Obtenir le mois if (!datesByMonth[month]) { datesByMonth[month] = []; @@ -135,6 +135,7 @@ datesByMonth[month].push(date); }); + console.log(Object.keys(datesByMonth)) return datesByMonth; } @@ -146,18 +147,18 @@ datesByMonth[month].forEach((date) => { let dayAssiduities = assiduities.filter((assiduity) => { - return moment.tz(date, TIMEZONE).isBetween( - moment.tz(assiduity.date_debut, TIMEZONE), - moment.tz(assiduity.date_fin, TIMEZONE), + return new Date(date).isBetween( + new Date(assiduity.date_debut), + new Date(assiduity.date_fin), "day", "[]" ) }); let dayJustificatifs = justificatifs.filter((justif) => { - return moment.tz(date, TIMEZONE).isBetween( - moment.tz(justif.date_debut, TIMEZONE), - moment.tz(justif.date_fin, TIMEZONE), + return new Date(date).isBetween( + new Date(justif.date_debut), + new Date(justif.date_fin), "day", "[]" ) @@ -250,7 +251,7 @@ } else if (dayJustificatifs.some((j) => j.etat.toLowerCase() !== "valide")) { est_just = "est_just invalide"; } - const momentDate = moment.tz(date, TIMEZONE); + const momentDate = new Date(date); let dayOfMonth = momentDate.format("D"); let dayOfWeek = momentDate.format("ddd"); From a40768e33bbc86a874bb509c111fbe8b5829e36c Mon Sep 17 00:00:00 2001 From: Iziram Date: Wed, 8 Nov 2023 15:12:42 +0100 Subject: [PATCH 02/17] =?UTF-8?q?Assiduit=C3=A9:=20suppression=20momentJS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/js/assiduites.js | 305 +++- app/static/libjs/moment-2.29.4.min.js | 2 - app/static/libjs/moment-timezone.js | 1597 ----------------- app/templates/assiduites/pages/bilan_etud.j2 | 3 +- app/templates/assiduites/pages/calendrier.j2 | 45 +- .../pages/signal_assiduites_diff.j2 | 2 +- app/templates/assiduites/widgets/conflict.j2 | 34 +- app/templates/assiduites/widgets/differee.j2 | 75 +- .../assiduites/widgets/minitimeline.j2 | 27 +- .../widgets/moduleimpl_dynamic_selector.j2 | 8 +- .../assiduites/widgets/tableau_assi.j2 | 10 +- .../assiduites/widgets/tableau_base.j2 | 6 +- .../assiduites/widgets/tableau_justi.j2 | 19 +- app/templates/assiduites/widgets/timeline.j2 | 5 +- app/views/assiduites.py | 23 +- 15 files changed, 391 insertions(+), 1770 deletions(-) delete mode 100644 app/static/libjs/moment-2.29.4.min.js delete mode 100644 app/static/libjs/moment-timezone.js diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index f4f98cc7c..044708249 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -53,34 +53,6 @@ Object.defineProperty(Array.prototype, "reversed", { // <= Gestion des dates => -Object.defineProperty(Date.prototype, "isBetween", { - /** - * Vérifie si la date est comprise dans une période avec une précision et une inclusivité optionnelles - * @param {Date} deb - La date de début de la période - * @param {Date} fin - La date de fin de la période - * @param {String} precision - La précision pour la comparaison (année, mois, jour, etc.) - * @param {String} bornes - L'inclusivité/exclusivité de la comparaison ("[]", "()", "[)", "(]") - */ - value: function (deb, fin, precision, bornes = "[]") { - // Ajuste la date actuelle, la date de début et la date de fin à la précision spécifiée - const thisPrecision = this.toPrecision(precision); - const debPrecision = deb.toPrecision(precision); - const finPrecision = fin.toPrecision(precision); - - // Vérifie les bornes en fonction de l'inclusivité/exclusivité spécifiée dans 'bornes' - const check_deb = - bornes[0] === "(" - ? thisPrecision > debPrecision - : thisPrecision >= debPrecision; - const check_fin = - bornes[1] === ")" - ? finPrecision > thisPrecision - : finPrecision >= thisPrecision; - - return check_deb && check_fin; - }, -}); - Object.defineProperty(Date.prototype, "toPrecision", { /** * Ajuste la date à la précision donnée. @@ -128,12 +100,12 @@ Object.defineProperty(Date.prototype, "toPrecision", { Object.defineProperty(Date.prototype, "isBefore", { value: function (date) { - return this < date; + return this.valueOf() < date.valueOf(); }, }); Object.defineProperty(Date.prototype, "isAfter", { value: function (date) { - return this > date; + return this.valueOf() > date.valueOf(); }, }); @@ -144,11 +116,42 @@ Object.defineProperty(Date.prototype, "isSame", { * @returns boolean */ value: function (date) { - return this == date; + return this.valueOf() == date.valueOf(); + }, +}); + +Object.defineProperty(Date.prototype, "isBetween", { + /** + * Vérifie si la date est comprise dans une période avec une précision et une inclusivité optionnelles + * @param {Date} deb - La date de début de la période + * @param {Date} fin - La date de fin de la période + * @param {String} precision - La précision pour la comparaison (année, mois, jour, etc.) + * @param {String} bornes - L'inclusivité/exclusivité de la comparaison ("[]", "()", "[)", "(]") + */ + value: function (deb, fin, precision, bornes = "[]") { + // Ajuste la date actuelle, la date de début et la date de fin à la précision spécifiée + const thisPrecision = this.toPrecision(precision); + const debPrecision = deb.toPrecision(precision); + const finPrecision = fin.toPrecision(precision); + + // Vérifie les bornes en fonction de l'inclusivité/exclusivité spécifiée dans 'bornes' + const check_deb = + bornes[0] === "(" + ? thisPrecision > debPrecision + : thisPrecision >= debPrecision; + const check_fin = + bornes[1] === ")" + ? finPrecision > thisPrecision + : finPrecision >= thisPrecision; + + return check_deb && check_fin; }, }); Object.defineProperty(Date.prototype, "toIsoUtcString", { + /** + * @returns date au format iso utc (yyyy-mm-ddThh:MM±oo:oo:oo) + */ value: function () { const date = this; var tzo = -date.getTimezoneOffset(), @@ -177,6 +180,231 @@ Object.defineProperty(Date.prototype, "toIsoUtcString", { }, }); +Object.defineProperty(Date.prototype, "clone", { + /** + * @returns Retourne une copie de la date (copie non liée) + */ + value: function () { + return structuredClone(this); + }, +}); + +Object.defineProperty(Date.prototype, "format", { + value: function (formatString) { + switch (formatString) { + case "DD/MM/Y HH:mm": + return this.toLocaleString("fr-FR", { + day: "2-digit", + month: "2-digit", + year: "2-digit", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + case "DD/MM/YYYY HH:mm": + return this.toLocaleString("fr-FR", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }); + + case "YYYY-MM-DDTHH:mm": + let iso = this.toIsoUtcString(); + // slice : YYYY-MM-DDTHH + // slice + 3 : YYYY-MM-DDTHH:mm + return iso.slice(0, iso.indexOf(":") + 3); + default: + return this.toIsoUtcString(); + } + }, +}); + +Object.defineProperty(Date.prototype, "add", { + /** + * Ajoute une valeur spécifiée à un élément de la date. + * @param {number} value - La valeur à ajouter. + * @param {string} type - Le type de la valeur (year, month, day, hours, minutes, seconds). + */ + value: function (value, type) { + switch (type) { + case "years": + this.setFullYear(this.getFullYear() + value); + break; + case "months": + this.setMonth(this.getMonth() + value); + break; + case "days": + this.setDate(this.getDate() + value); + break; + case "hours": + this.setHours(this.getHours() + value); + break; + case "minutes": + this.setMinutes(this.getMinutes() + value); + break; + case "seconds": + this.setSeconds(this.getSeconds() + value); + break; + default: + throw new Error( + `Invalid type for adding to date | type : ${type} value : ${value}` + ); + } + return this; // Return the modified date + }, +}); + +Object.defineProperty(Date.prototype, "startOf", { + /** + * Ajuste la date à la plus petite valeur pour la précision donnée. + * @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds). + * @returns {Date} - Une nouvelle date ajustée. + */ + value: function (precision) { + const newDate = this.clone(); + switch (precision) { + case "year": + newDate.setMonth(0); + case "month": + newDate.setDate(1); + case "day": + newDate.setHours(0); + case "hours": + newDate.setMinutes(0); + case "minutes": + newDate.setSeconds(0); + case "seconds": + newDate.setMilliseconds(0); + break; + case "milliseconds": + break; + default: + throw new Error("Invalid precision for startOf function"); + } + return newDate; + }, +}); + +Object.defineProperty(Date.prototype, "endOf", { + /** + * Ajuste la date à la plus grande valeur pour la précision donnée. + * @param {string} precision - La précision souhaitée (year, month, day, hours, minutes, seconds, milliseconds). + * @returns {Date} - Une nouvelle date ajustée. + */ + value: function (precision) { + const newDate = this.clone(); + switch (precision) { + case "year": + newDate.setMonth(11); // Décembre est le 11ème mois (0-indexé) + case "month": + newDate.setDate(0); // Le jour 0 du mois suivant est le dernier jour du mois courant + newDate.setMonth(newDate.getMonth() + 1); + case "day": + newDate.setHours(23); // 23 heures est la dernière heure de la journée + case "hours": + newDate.setMinutes(59); // 59 minutes est la dernière minute de l'heure + case "minutes": + newDate.setSeconds(59); // 59 secondes est la dernière seconde de la minute + case "seconds": + newDate.setMilliseconds(999); // 999 millisecondes est la dernière milliseconde de la seconde + break; + case "milliseconds": + // Rien à faire pour les millisecondes + break; + default: + throw new Error("Invalid precision for endOf function"); + } + return newDate; + }, +}); + +class Duration { + /** + * Constructeur de la classe Duration. + * @param {Date} start - La date de début de la période. + * @param {Date} end - La date de fin de la période. + */ + constructor(start, end) { + this.start = start; // Stocke la date de début. + this.end = end; // Stocke la date de fin. + this.duration = end - start; // Calcule la durée en millisecondes entre les deux dates. + } + + /** + * Calcule le nombre d'années entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre d'années arrondi à quatre décimales. + */ + get years() { + const startYear = this.start.getFullYear(); // Obtient l'année de la date de début. + const endYear = this.end.getFullYear(); // Obtient l'année de la date de fin. + // Calcule la différence en années et arrondit à quatre décimales. + return parseFloat((endYear - startYear).toFixed(4)); + } + + /** + * Calcule le nombre de mois entre les deux dates, en tenant compte des années et des jours, et arrondit le résultat à quatre décimales. + * @return {number} Le nombre de mois arrondi à quatre décimales. + */ + get months() { + const years = this.years; // Nombre d'années complètes. + // Calcule la différence en mois, en ajoutant la différence en jours divisée par 30 pour une approximation. + const months = + years * 12 + + (this.end.getMonth() - this.start.getMonth()) + + (this.end.getDate() - this.start.getDate()) / 30; + // Arrondit à quatre décimales. + return parseFloat(months.toFixed(4)); + } + + /** + * Calcule le nombre de jours entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre de jours arrondi à quatre décimales. + */ + get days() { + // Convertit la durée en millisecondes en jours et arrondit à quatre décimales. + return parseFloat((this.duration / (24 * 60 * 60 * 1000)).toFixed(4)); + } + + /** + * Calcule le nombre d'heures entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre d'heures arrondi à quatre décimales. + */ + get hours() { + // Convertit la durée en millisecondes en heures et arrondit à quatre décimales. + return parseFloat((this.duration / (60 * 60 * 1000)).toFixed(4)); + } + + /** + * Calcule le nombre de minutes entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre de minutes arrondi à quatre décimales. + */ + get minutes() { + // Convertit la durée en millisecondes en minutes et arrondit à quatre décimales. + return parseFloat((this.duration / (60 * 1000)).toFixed(4)); + } + + /** + * Calcule le nombre de secondes entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre de secondes arrondi à quatre décimales. + */ + get seconds() { + // Convertit la durée en millisecondes en secondes et arrondit à quatre décimales. + return parseFloat((this.duration / 1000).toFixed(4)); + } + + /** + * Obtient le nombre de millisecondes entre les deux dates et arrondit le résultat à quatre décimales. + * @return {number} Le nombre de millisecondes arrondi à quatre décimales. + */ + get milliseconds() { + // Arrondit la durée totale en millisecondes à quatre décimales. + return parseFloat(this.duration.toFixed(4)); + } +} + /** * Ajout des évents sur les boutons d'assiduité * @param {Document | HTMLFieldSetElement} parent par défaut le document, un field sinon @@ -753,7 +981,6 @@ function verifyDateInSemester() { const date = getDate(); const periodSemester = getFormSemestreDates(); - return date.isBetween(periodSemester.deb, periodSemester.fin, "[]"); } @@ -793,8 +1020,8 @@ function getAssiduitesOnDateChange() { * @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00) * @returns {String} la date intelligible */ -function formatDateModal(str) { - return new Date(str).toLocaleString("fr-FR"); +function formatDateModal(str, separator = " ") { + return new Date(str).format("DD/MM/Y HH:mm").replace(" ", separator); } /** @@ -1197,9 +1424,10 @@ function getAssiduitesConflict(etudid, periode) { return etudAssiduites.filter((assi) => { const interval = { deb: new Date(assi.date_debut), - fin: new Date(assi.date_debut), + fin: new Date(assi.date_fin), }; - return hasTimeConflict(periode, interval); + const test = hasTimeConflict(periode, interval); + return test; }); } @@ -1506,7 +1734,6 @@ function insertEtudRow(etud, index, output = false) { date_fin: null, prevAssiduites: prevAssiduite, }; - if (conflict.length > 0) { assiduite.etatAssiduite = conflict[0].etat; @@ -1742,9 +1969,9 @@ function getJustificatifFromPeriod(date, etudid, update) { url: getUrl() + `/api/justificatifs/${etudid}/query?date_debut=${date.deb - .add(1, "s") + .add(1, "seconds") .toIsoUtcString()}&date_fin=${date.fin - .subtract(1, "s") + .add(-1, "seconds") .toIsoUtcString()}`, success: (data) => { update(data); diff --git a/app/static/libjs/moment-2.29.4.min.js b/app/static/libjs/moment-2.29.4.min.js deleted file mode 100644 index 3427886d1..000000000 --- a/app/static/libjs/moment-2.29.4.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var H;function f(){return H.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function F(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function c(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function L(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(var t in e)if(c(e,t))return;return 1}function o(e){return void 0===e}function u(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function V(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function G(e,t){for(var n=[],s=e.length,i=0;i>>0,s=0;sAe(e)?(r=e+1,t-Ae(e)):(r=e,t);return{year:r,dayOfYear:n}}function qe(e,t,n){var s,i,r=ze(e.year(),t,n),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+P(i=e.year()-1,t,n):r>P(e.year(),t,n)?(s=r-P(e.year(),t,n),i=e.year()+1):(i=e.year(),s=r),{week:s,year:i}}function P(e,t,n){var s=ze(e,t,n),t=ze(e+1,t,n);return(Ae(e)-s+t)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),t("week","w"),t("isoWeek","W"),n("week",5),n("isoWeek",5),v("w",p),v("ww",p,w),v("W",p),v("WW",p,w),Te(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=g(e)});function Be(e,t){return e.slice(t,7).concat(e.slice(0,t))}s("d",0,"do","day"),s("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),s("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),s("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),s("e",0,0,"weekday"),s("E",0,0,"isoWeekday"),t("day","d"),t("weekday","e"),t("isoWeekday","E"),n("day",11),n("weekday",11),n("isoWeekday",11),v("d",p),v("e",p),v("E",p),v("dd",function(e,t){return t.weekdaysMinRegex(e)}),v("ddd",function(e,t){return t.weekdaysShortRegex(e)}),v("dddd",function(e,t){return t.weekdaysRegex(e)}),Te(["dd","ddd","dddd"],function(e,t,n,s){s=n._locale.weekdaysParse(e,s,n._strict);null!=s?t.d=s:m(n).invalidWeekday=e}),Te(["d","e","E"],function(e,t,n,s){t[s]=g(e)});var Je="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Qe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Xe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Ke=k,et=k,tt=k;function nt(){function e(e,t){return t.length-e.length}for(var t,n,s,i=[],r=[],a=[],o=[],u=0;u<7;u++)s=l([2e3,1]).day(u),t=M(this.weekdaysMin(s,"")),n=M(this.weekdaysShort(s,"")),s=M(this.weekdays(s,"")),i.push(t),r.push(n),a.push(s),o.push(t),o.push(n),o.push(s);i.sort(e),r.sort(e),a.sort(e),o.sort(e),this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+r.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+i.join("|")+")","i")}function st(){return this.hours()%12||12}function it(e,t){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function rt(e,t){return t._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,st),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)}),s("hmmss",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)+r(this.seconds(),2)}),s("Hmm",0,0,function(){return""+this.hours()+r(this.minutes(),2)}),s("Hmmss",0,0,function(){return""+this.hours()+r(this.minutes(),2)+r(this.seconds(),2)}),it("a",!0),it("A",!1),t("hour","h"),n("hour",13),v("a",rt),v("A",rt),v("H",p),v("h",p),v("k",p),v("HH",p,w),v("hh",p,w),v("kk",p,w),v("hmm",ge),v("hmmss",we),v("Hmm",ge),v("Hmmss",we),D(["H","HH"],x),D(["k","kk"],function(e,t,n){e=g(e);t[x]=24===e?0:e}),D(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),D(["h","hh"],function(e,t,n){t[x]=g(e),m(n).bigHour=!0}),D("hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s)),m(n).bigHour=!0}),D("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i)),m(n).bigHour=!0}),D("Hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s))}),D("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i))});k=de("Hours",!0);var at,ot={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:Ue,week:{dow:0,doy:6},weekdays:Je,weekdaysMin:Xe,weekdaysShort:Qe,meridiemParse:/[ap]\.?m?\.?/i},R={},ut={};function lt(e){return e&&e.toLowerCase().replace("_","-")}function ht(e){for(var t,n,s,i,r=0;r=t&&function(e,t){for(var n=Math.min(e.length,t.length),s=0;s=t-1)break;t--}r++}return at}function dt(t){var e;if(void 0===R[t]&&"undefined"!=typeof module&&module&&module.exports&&null!=t.match("^[^/\\\\]*$"))try{e=at._abbr,require("./locale/"+t),ct(e)}catch(e){R[t]=null}return R[t]}function ct(e,t){return e&&((t=o(t)?mt(e):ft(e,t))?at=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),at._abbr}function ft(e,t){if(null===t)return delete R[e],null;var n,s=ot;if(t.abbr=e,null!=R[e])Q("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=R[e]._config;else if(null!=t.parentLocale)if(null!=R[t.parentLocale])s=R[t.parentLocale]._config;else{if(null==(n=dt(t.parentLocale)))return ut[t.parentLocale]||(ut[t.parentLocale]=[]),ut[t.parentLocale].push({name:e,config:t}),null;s=n._config}return R[e]=new K(X(s,t)),ut[e]&&ut[e].forEach(function(e){ft(e.name,e.config)}),ct(e),R[e]}function mt(e){var t;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return at;if(!a(e)){if(t=dt(e))return t;e=[e]}return ht(e)}function _t(e){var t=e._a;return t&&-2===m(e).overflow&&(t=t[O]<0||11We(t[Y],t[O])?b:t[x]<0||24P(r,u,l)?m(s)._overflowWeeks=!0:null!=h?m(s)._overflowWeekday=!0:(d=$e(r,a,o,u,l),s._a[Y]=d.year,s._dayOfYear=d.dayOfYear)),null!=e._dayOfYear&&(i=bt(e._a[Y],n[Y]),(e._dayOfYear>Ae(i)||0===e._dayOfYear)&&(m(e)._overflowDayOfYear=!0),h=Ze(i,0,e._dayOfYear),e._a[O]=h.getUTCMonth(),e._a[b]=h.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=c[t]=n[t];for(;t<7;t++)e._a[t]=c[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[x]&&0===e._a[T]&&0===e._a[N]&&0===e._a[Ne]&&(e._nextDay=!0,e._a[x]=0),e._d=(e._useUTC?Ze:je).apply(null,c),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[x]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(m(e).weekdayMismatch=!0)}}function Tt(e){if(e._f===f.ISO_8601)St(e);else if(e._f===f.RFC_2822)Ot(e);else{e._a=[],m(e).empty=!0;for(var t,n,s,i,r,a=""+e._i,o=a.length,u=0,l=ae(e._f,e._locale).match(te)||[],h=l.length,d=0;de.valueOf():e.valueOf()"}),i.toJSON=function(){return this.isValid()?this.toISOString():null},i.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},i.unix=function(){return Math.floor(this.valueOf()/1e3)},i.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},i.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},i.eraName=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;nthis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},i.isLocal=function(){return!!this.isValid()&&!this._isUTC},i.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},i.isUtc=At,i.isUTC=At,i.zoneAbbr=function(){return this._isUTC?"UTC":""},i.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},i.dates=e("dates accessor is deprecated. Use date instead.",ke),i.months=e("months accessor is deprecated. Use month instead",Ge),i.years=e("years accessor is deprecated. Use year instead",Ie),i.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?(this.utcOffset(e="string"!=typeof e?-e:e,t),this):-this.utcOffset()}),i.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e,t={};return $(t,this),(t=Nt(t))._a?(e=(t._isUTC?l:W)(t._a),this._isDSTShifted=this.isValid()&&0= 2.6.0. You are using Moment.js " + - moment.version + - ". See momentjs.com" - ); - } - - /************************************ - Unpacking - ************************************/ - - function charCodeToInt(charCode) { - if (charCode > 96) { - return charCode - 87; - } else if (charCode > 64) { - return charCode - 29; - } - return charCode - 48; - } - - function unpackBase60(string) { - var i = 0, - parts = string.split("."), - whole = parts[0], - fractional = parts[1] || "", - multiplier = 1, - num, - out = 0, - sign = 1; - - // handle negative numbers - if (string.charCodeAt(0) === 45) { - i = 1; - sign = -1; - } - - // handle digits before the decimal - for (i; i < whole.length; i++) { - num = charCodeToInt(whole.charCodeAt(i)); - out = 60 * out + num; - } - - // handle digits after the decimal - for (i = 0; i < fractional.length; i++) { - multiplier = multiplier / 60; - num = charCodeToInt(fractional.charCodeAt(i)); - out += num * multiplier; - } - - return out * sign; - } - - function arrayToInt(array) { - for (var i = 0; i < array.length; i++) { - array[i] = unpackBase60(array[i]); - } - } - - function intToUntil(array, length) { - for (var i = 0; i < length; i++) { - array[i] = Math.round((array[i - 1] || 0) + array[i] * 60000); // minutes to milliseconds - } - - array[length - 1] = Infinity; - } - - function mapIndices(source, indices) { - var out = [], - i; - - for (i = 0; i < indices.length; i++) { - out[i] = source[indices[i]]; - } - - return out; - } - - function unpack(string) { - var data = string.split("|"), - offsets = data[2].split(" "), - indices = data[3].split(""), - untils = data[4].split(" "); - - arrayToInt(offsets); - arrayToInt(indices); - arrayToInt(untils); - - intToUntil(untils, indices.length); - - return { - name: data[0], - abbrs: mapIndices(data[1].split(" "), indices), - offsets: mapIndices(offsets, indices), - untils: untils, - population: data[5] | 0, - }; - } - - /************************************ - Zone object - ************************************/ - - function Zone(packedString) { - if (packedString) { - this._set(unpack(packedString)); - } - } - - Zone.prototype = { - _set: function (unpacked) { - this.name = unpacked.name; - this.abbrs = unpacked.abbrs; - this.untils = unpacked.untils; - this.offsets = unpacked.offsets; - this.population = unpacked.population; - }, - - _index: function (timestamp) { - var target = +timestamp, - untils = this.untils, - i; - - for (i = 0; i < untils.length; i++) { - if (target < untils[i]) { - return i; - } - } - }, - - countries: function () { - var zone_name = this.name; - return Object.keys(countries).filter(function (country_code) { - return countries[country_code].zones.indexOf(zone_name) !== -1; - }); - }, - - parse: function (timestamp) { - var target = +timestamp, - offsets = this.offsets, - untils = this.untils, - max = untils.length - 1, - offset, - offsetNext, - offsetPrev, - i; - - for (i = 0; i < max; i++) { - offset = offsets[i]; - offsetNext = offsets[i + 1]; - offsetPrev = offsets[i ? i - 1 : i]; - - if (offset < offsetNext && tz.moveAmbiguousForward) { - offset = offsetNext; - } else if (offset > offsetPrev && tz.moveInvalidForward) { - offset = offsetPrev; - } - - if (target < untils[i] - offset * 60000) { - return offsets[i]; - } - } - - return offsets[max]; - }, - - abbr: function (mom) { - return this.abbrs[this._index(mom)]; - }, - - offset: function (mom) { - logError("zone.offset has been deprecated in favor of zone.utcOffset"); - return this.offsets[this._index(mom)]; - }, - - utcOffset: function (mom) { - return this.offsets[this._index(mom)]; - }, - }; - - /************************************ - Country object - ************************************/ - - function Country(country_name, zone_names) { - this.name = country_name; - this.zones = zone_names; - } - - /************************************ - Current Timezone - ************************************/ - - function OffsetAt(at) { - var timeString = at.toTimeString(); - var abbr = timeString.match(/\([a-z ]+\)/i); - if (abbr && abbr[0]) { - // 17:56:31 GMT-0600 (CST) - // 17:56:31 GMT-0600 (Central Standard Time) - abbr = abbr[0].match(/[A-Z]/g); - abbr = abbr ? abbr.join("") : undefined; - } else { - // 17:56:31 CST - // 17:56:31 GMT+0800 (台北標準時間) - abbr = timeString.match(/[A-Z]{3,5}/g); - abbr = abbr ? abbr[0] : undefined; - } - - if (abbr === "GMT") { - abbr = undefined; - } - - this.at = +at; - this.abbr = abbr; - this.offset = at.getTimezoneOffset(); - } - - function ZoneScore(zone) { - this.zone = zone; - this.offsetScore = 0; - this.abbrScore = 0; - } - - ZoneScore.prototype.scoreOffsetAt = function (offsetAt) { - this.offsetScore += Math.abs( - this.zone.utcOffset(offsetAt.at) - offsetAt.offset - ); - if (this.zone.abbr(offsetAt.at).replace(/[^A-Z]/g, "") !== offsetAt.abbr) { - this.abbrScore++; - } - }; - - function findChange(low, high) { - var mid, diff; - - while ((diff = (((high.at - low.at) / 12e4) | 0) * 6e4)) { - mid = new OffsetAt(new Date(low.at + diff)); - if (mid.offset === low.offset) { - low = mid; - } else { - high = mid; - } - } - - return low; - } - - function userOffsets() { - var startYear = new Date().getFullYear() - 2, - last = new OffsetAt(new Date(startYear, 0, 1)), - offsets = [last], - change, - next, - i; - - for (i = 1; i < 48; i++) { - next = new OffsetAt(new Date(startYear, i, 1)); - if (next.offset !== last.offset) { - change = findChange(last, next); - offsets.push(change); - offsets.push(new OffsetAt(new Date(change.at + 6e4))); - } - last = next; - } - - for (i = 0; i < 4; i++) { - offsets.push(new OffsetAt(new Date(startYear + i, 0, 1))); - offsets.push(new OffsetAt(new Date(startYear + i, 6, 1))); - } - - return offsets; - } - - function sortZoneScores(a, b) { - if (a.offsetScore !== b.offsetScore) { - return a.offsetScore - b.offsetScore; - } - if (a.abbrScore !== b.abbrScore) { - return a.abbrScore - b.abbrScore; - } - if (a.zone.population !== b.zone.population) { - return b.zone.population - a.zone.population; - } - return b.zone.name.localeCompare(a.zone.name); - } - - function addToGuesses(name, offsets) { - var i, offset; - arrayToInt(offsets); - for (i = 0; i < offsets.length; i++) { - offset = offsets[i]; - guesses[offset] = guesses[offset] || {}; - guesses[offset][name] = true; - } - } - - function guessesForUserOffsets(offsets) { - var offsetsLength = offsets.length, - filteredGuesses = {}, - out = [], - i, - j, - guessesOffset; - - for (i = 0; i < offsetsLength; i++) { - guessesOffset = guesses[offsets[i].offset] || {}; - for (j in guessesOffset) { - if (guessesOffset.hasOwnProperty(j)) { - filteredGuesses[j] = true; - } - } - } - - for (i in filteredGuesses) { - if (filteredGuesses.hasOwnProperty(i)) { - out.push(names[i]); - } - } - - return out; - } - - function rebuildGuess() { - // use Intl API when available and returning valid time zone - try { - var intlName = Intl.DateTimeFormat().resolvedOptions().timeZone; - if (intlName && intlName.length > 3) { - var name = names[normalizeName(intlName)]; - if (name) { - return name; - } - logError( - "Moment Timezone found " + - intlName + - " from the Intl api, but did not have that data loaded." - ); - } - } catch (e) { - // Intl unavailable, fall back to manual guessing. - } - - var offsets = userOffsets(), - offsetsLength = offsets.length, - guesses = guessesForUserOffsets(offsets), - zoneScores = [], - zoneScore, - i, - j; - - for (i = 0; i < guesses.length; i++) { - zoneScore = new ZoneScore(getZone(guesses[i]), offsetsLength); - for (j = 0; j < offsetsLength; j++) { - zoneScore.scoreOffsetAt(offsets[j]); - } - zoneScores.push(zoneScore); - } - - zoneScores.sort(sortZoneScores); - - return zoneScores.length > 0 ? zoneScores[0].zone.name : undefined; - } - - function guess(ignoreCache) { - if (!cachedGuess || ignoreCache) { - cachedGuess = rebuildGuess(); - } - return cachedGuess; - } - - /************************************ - Global Methods - ************************************/ - - function normalizeName(name) { - return (name || "").toLowerCase().replace(/\//g, "_"); - } - - function addZone(packed) { - var i, name, split, normalized; - - if (typeof packed === "string") { - packed = [packed]; - } - - for (i = 0; i < packed.length; i++) { - split = packed[i].split("|"); - name = split[0]; - normalized = normalizeName(name); - zones[normalized] = packed[i]; - names[normalized] = name; - addToGuesses(normalized, split[2].split(" ")); - } - } - - function getZone(name, caller) { - name = normalizeName(name); - - var zone = zones[name]; - var link; - - if (zone instanceof Zone) { - return zone; - } - - if (typeof zone === "string") { - zone = new Zone(zone); - zones[name] = zone; - return zone; - } - - // Pass getZone to prevent recursion more than 1 level deep - if ( - links[name] && - caller !== getZone && - (link = getZone(links[name], getZone)) - ) { - zone = zones[name] = new Zone(); - zone._set(link); - zone.name = names[name]; - return zone; - } - - return null; - } - - function getNames() { - var i, - out = []; - - for (i in names) { - if ( - names.hasOwnProperty(i) && - (zones[i] || zones[links[i]]) && - names[i] - ) { - out.push(names[i]); - } - } - - return out.sort(); - } - - function getCountryNames() { - return Object.keys(countries); - } - - function addLink(aliases) { - var i, alias, normal0, normal1; - - if (typeof aliases === "string") { - aliases = [aliases]; - } - - for (i = 0; i < aliases.length; i++) { - alias = aliases[i].split("|"); - - normal0 = normalizeName(alias[0]); - normal1 = normalizeName(alias[1]); - - links[normal0] = normal1; - names[normal0] = alias[0]; - - links[normal1] = normal0; - names[normal1] = alias[1]; - } - } - - function addCountries(data) { - var i, country_code, country_zones, split; - if (!data || !data.length) return; - for (i = 0; i < data.length; i++) { - split = data[i].split("|"); - country_code = split[0].toUpperCase(); - country_zones = split[1].split(" "); - countries[country_code] = new Country(country_code, country_zones); - } - } - - function getCountry(name) { - name = name.toUpperCase(); - return countries[name] || null; - } - - function zonesForCountry(country, with_offset) { - country = getCountry(country); - - if (!country) return null; - - var zones = country.zones.sort(); - - if (with_offset) { - return zones.map(function (zone_name) { - var zone = getZone(zone_name); - return { - name: zone_name, - offset: zone.utcOffset(new Date()), - }; - }); - } - - return zones; - } - - function loadData(data) { - addZone(data.zones); - addLink(data.links); - addCountries(data.countries); - tz.dataVersion = data.version; - } - - function zoneExists(name) { - if (!zoneExists.didShowError) { - zoneExists.didShowError = true; - logError( - "moment.tz.zoneExists('" + - name + - "') has been deprecated in favor of !moment.tz.zone('" + - name + - "')" - ); - } - return !!getZone(name); - } - - function needsOffset(m) { - var isUnixTimestamp = m._f === "X" || m._f === "x"; - return !!(m._a && m._tzm === undefined && !isUnixTimestamp); - } - - function logError(message) { - if (typeof console !== "undefined" && typeof console.error === "function") { - console.error(message); - } - } - - /************************************ - moment.tz namespace - ************************************/ - - function tz(input) { - var args = Array.prototype.slice.call(arguments, 0, -1), - name = arguments[arguments.length - 1], - zone = getZone(name), - out = moment.utc.apply(null, args); - - if (zone && !moment.isMoment(input) && needsOffset(out)) { - out.add(zone.parse(out), "minutes"); - } - - out.tz(name); - - return out; - } - - tz.version = VERSION; - tz.dataVersion = ""; - tz._zones = zones; - tz._links = links; - tz._names = names; - tz._countries = countries; - tz.add = addZone; - tz.link = addLink; - tz.load = loadData; - tz.zone = getZone; - tz.zoneExists = zoneExists; // deprecated in 0.1.0 - tz.guess = guess; - tz.names = getNames; - tz.Zone = Zone; - tz.unpack = unpack; - tz.unpackBase60 = unpackBase60; - tz.needsOffset = needsOffset; - tz.moveInvalidForward = true; - tz.moveAmbiguousForward = false; - tz.countries = getCountryNames; - tz.zonesForCountry = zonesForCountry; - - /************************************ - Interface with Moment.js - ************************************/ - - var fn = moment.fn; - - moment.tz = tz; - - moment.defaultZone = null; - - moment.updateOffset = function (mom, keepTime) { - var zone = moment.defaultZone, - offset; - - if (mom._z === undefined) { - if (zone && needsOffset(mom) && !mom._isUTC) { - mom._d = moment.utc(mom._a)._d; - mom.utc().add(zone.parse(mom), "minutes"); - } - mom._z = zone; - } - if (mom._z) { - offset = mom._z.utcOffset(mom); - if (Math.abs(offset) < 16) { - offset = offset / 60; - } - if (mom.utcOffset !== undefined) { - var z = mom._z; - mom.utcOffset(-offset, keepTime); - mom._z = z; - } else { - mom.zone(offset, keepTime); - } - } - }; - - fn.tz = function (name, keepTime) { - if (name) { - if (typeof name !== "string") { - throw new Error( - "Time zone name must be a string, got " + - name + - " [" + - typeof name + - "]" - ); - } - this._z = getZone(name); - if (this._z) { - moment.updateOffset(this, keepTime); - } else { - logError( - "Moment Timezone has no data for " + - name + - ". See http://momentjs.com/timezone/docs/#/data-loading/." - ); - } - return this; - } - if (this._z) { - return this._z.name; - } - }; - - function abbrWrap(old) { - return function () { - if (this._z) { - return this._z.abbr(this); - } - return old.call(this); - }; - } - - function resetZoneWrap(old) { - return function () { - this._z = null; - return old.apply(this, arguments); - }; - } - - function resetZoneWrap2(old) { - return function () { - if (arguments.length > 0) this._z = null; - return old.apply(this, arguments); - }; - } - - fn.zoneName = abbrWrap(fn.zoneName); - fn.zoneAbbr = abbrWrap(fn.zoneAbbr); - fn.utc = resetZoneWrap(fn.utc); - fn.local = resetZoneWrap(fn.local); - fn.utcOffset = resetZoneWrap2(fn.utcOffset); - - moment.tz.setDefault = function (name) { - if (major < 2 || (major === 2 && minor < 9)) { - logError( - "Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js " + - moment.version + - "." - ); - } - moment.defaultZone = name ? getZone(name) : null; - return moment; - }; - - // Cloning a moment should include the _z property. - var momentProperties = moment.momentProperties; - if (Object.prototype.toString.call(momentProperties) === "[object Array]") { - // moment 2.8.1+ - momentProperties.push("_z"); - momentProperties.push("_a"); - } else if (momentProperties) { - // moment 2.7.0 - momentProperties._z = null; - } - - loadData({ - version: "2022g", - zones: [ - "Africa/Abidjan|GMT|0|0||48e5", - "Africa/Nairobi|EAT|-30|0||47e5", - "Africa/Algiers|CET|-10|0||26e5", - "Africa/Lagos|WAT|-10|0||17e6", - "Africa/Maputo|CAT|-20|0||26e5", - "Africa/Cairo|EET|-20|0||15e6", - "Africa/Casablanca|+00 +01|0 -10|01010101010101010101010101|1T0q0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00|32e5", - "Europe/Paris|CET CEST|-10 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|11e6", - "Africa/Johannesburg|SAST|-20|0||84e5", - "Africa/Juba|EAT CAT|-30 -20|01|24nx0|", - "Africa/Khartoum|EAT CAT|-30 -20|01|1Usl0|51e5", - "Africa/Sao_Tome|GMT WAT|0 -10|010|1UQN0 2q00|", - "Africa/Windhoek|CAT WAT|-20 -10|010|1T3c0 11B0|32e4", - "America/Adak|HST HDT|a0 90|01010101010101010101010|1ST00 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|326", - "America/Anchorage|AKST AKDT|90 80|01010101010101010101010|1SSX0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|30e4", - "America/Santo_Domingo|AST|40|0||29e5", - "America/Fortaleza|-03|30|0||34e5", - "America/Asuncion|-03 -04|30 40|01010101010101010101010|1T0r0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0|28e5", - "America/Panama|EST|50|0||15e5", - "America/Mexico_City|CST CDT|60 50|0101010101010|1T3k0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|20e6", - "America/Managua|CST|60|0||22e5", - "America/Caracas|-04|40|0||29e5", - "America/Lima|-05|50|0||11e6", - "America/Denver|MST MDT|70 60|01010101010101010101010|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|26e5", - "America/Campo_Grande|-03 -04|30 40|010101|1SKr0 1zd0 On0 1HB0 FX0|77e4", - "America/Chicago|CST CDT|60 50|01010101010101010101010|1SSU0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|92e5", - "America/Chihuahua|MST MDT CST|70 60 60|0101010101012|1T3l0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|81e4", - "America/Ciudad_Juarez|MST MDT CST|70 60 60|010101010101201010101010|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 cm0 EP0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|", - "America/Phoenix|MST|70|0||42e5", - "America/Whitehorse|PST PDT MST|80 70 70|010101012|1SSW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|23e3", - "America/New_York|EST EDT|50 40|01010101010101010101010|1SST0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|21e6", - "America/Los_Angeles|PST PDT|80 70|01010101010101010101010|1SSW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|15e6", - "America/Halifax|AST ADT|40 30|01010101010101010101010|1SSS0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|39e4", - "America/Godthab|-03 -02|30 20|01010101010101|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0|17e3", - "America/Grand_Turk|AST EDT EST|40 40 50|012121212121212121212|1Vkv0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|37e2", - "America/Havana|CST CDT|50 40|01010101010101010101010|1SSR0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0|21e5", - "America/Mazatlan|MST MDT|70 60|0101010101010|1T3l0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|44e4", - "America/Metlakatla|AKST AKDT PST|90 80 80|010120101010101010101010|1SSX0 1zb0 Op0 1zb0 uM0 jB0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|14e2", - "America/Miquelon|-03 -02|30 20|01010101010101010101010|1SSR0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|61e2", - "America/Noronha|-02|20|0||30e2", - "America/Ojinaga|MST MDT CST CDT|70 60 60 50|01010101010123232323232|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 Rc0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|23e3", - "America/Santiago|-03 -04|30 40|01010101010101010101010|1Tk30 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|62e5", - "America/Sao_Paulo|-02 -03|20 30|010101|1SKq0 1zd0 On0 1HB0 FX0|20e6", - "Atlantic/Azores|-01 +00|10 0|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|25e4", - "America/St_Johns|NST NDT|3u 2u|01010101010101010101010|1SSRu 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|11e4", - "Antarctica/Casey|+11 +08|-b0 -80|0101010|1Vkh0 1o30 14k0 1kr0 12l0 1o01|10", - "Asia/Bangkok|+07|-70|0||15e6", - "Asia/Vladivostok|+10|-a0|0||60e4", - "Australia/Sydney|AEDT AEST|-b0 -a0|01010101010101010101010|1T340 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|40e5", - "Asia/Tashkent|+05|-50|0||23e5", - "Pacific/Auckland|NZDT NZST|-d0 -c0|01010101010101010101010|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00|14e5", - "Europe/Istanbul|+03|-30|0||13e6", - "Antarctica/Troll|+00 +02|0 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|40", - "Asia/Dhaka|+06|-60|0||16e6", - "Asia/Amman|EET EEST +03|-20 -30 -30|0101010101012|1T2m0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 LA0 1C00|25e5", - "Asia/Kamchatka|+12|-c0|0||18e4", - "Asia/Dubai|+04|-40|0||39e5", - "Asia/Beirut|EET EEST|-20 -30|01010101010101010101010|1T0m0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0|22e5", - "Asia/Kuala_Lumpur|+08|-80|0||71e5", - "Asia/Kolkata|IST|-5u|0||15e6", - "Asia/Chita|+09|-90|0||33e4", - "Asia/Shanghai|CST|-80|0||23e6", - "Asia/Colombo|+0530|-5u|0||22e5", - "Asia/Damascus|EET EEST +03|-20 -30 -30|0101010101012|1T2m0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|26e5", - "Asia/Famagusta|+03 EET EEST|-30 -20 -30|0121212121212121212121|1Urd0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|", - "Asia/Gaza|EET EEST|-20 -30|01010101010101010101010|1SXX0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0|18e5", - "Asia/Hong_Kong|HKT|-80|0||73e5", - "Asia/Jakarta|WIB|-70|0||31e6", - "Asia/Jayapura|WIT|-90|0||26e4", - "Asia/Jerusalem|IST IDT|-20 -30|01010101010101010101010|1SXA0 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0|81e4", - "Asia/Kabul|+0430|-4u|0||46e5", - "Asia/Karachi|PKT|-50|0||24e6", - "Asia/Kathmandu|+0545|-5J|0||12e5", - "Asia/Sakhalin|+11|-b0|0||58e4", - "Asia/Makassar|WITA|-80|0||15e5", - "Asia/Manila|PST|-80|0||24e6", - "Europe/Athens|EET EEST|-20 -30|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|35e5", - "Asia/Pyongyang|KST KST|-8u -90|01|1VGf0|29e5", - "Asia/Qyzylorda|+06 +05|-60 -50|01|1Xei0|73e4", - "Asia/Rangoon|+0630|-6u|0||48e5", - "Asia/Seoul|KST|-90|0||23e6", - "Asia/Tehran|+0330 +0430|-3u -4u|0101010101010|1SWIu 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0|14e6", - "Asia/Tokyo|JST|-90|0||38e6", - "Europe/Lisbon|WET WEST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|27e5", - "Atlantic/Cape_Verde|-01|10|0||50e4", - "Australia/Adelaide|ACDT ACST|-au -9u|01010101010101010101010|1T34u 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|11e5", - "Australia/Brisbane|AEST|-a0|0||20e5", - "Australia/Darwin|ACST|-9u|0||12e4", - "Australia/Eucla|+0845|-8J|0||368", - "Australia/Lord_Howe|+11 +1030|-b0 -au|01010101010101010101010|1T330 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu|347", - "Australia/Perth|AWST|-80|0||18e5", - "Pacific/Easter|-05 -06|50 60|01010101010101010101010|1Tk30 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|30e2", - "Europe/Dublin|GMT IST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|12e5", - "Etc/GMT-1|+01|-10|0||", - "Pacific/Fakaofo|+13|-d0|0||483", - "Pacific/Kiritimati|+14|-e0|0||51e2", - "Etc/GMT-2|+02|-20|0||", - "Pacific/Tahiti|-10|a0|0||18e4", - "Pacific/Niue|-11|b0|0||12e2", - "Etc/GMT+12|-12|c0|0||", - "Pacific/Galapagos|-06|60|0||25e3", - "Etc/GMT+7|-07|70|0||", - "Pacific/Pitcairn|-08|80|0||56", - "Pacific/Gambier|-09|90|0||125", - "Etc/UTC|UTC|0|0||", - "Europe/London|GMT BST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|10e6", - "Europe/Chisinau|EET EEST|-20 -30|01010101010101010101010|1T0o0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|67e4", - "Europe/Moscow|MSK|-30|0||16e6", - "Europe/Volgograd|+03 +04|-30 -40|010|1WQL0 5gn0|10e5", - "Pacific/Honolulu|HST|a0|0||37e4", - "MET|MET MEST|-10 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|", - "Pacific/Chatham|+1345 +1245|-dJ -cJ|01010101010101010101010|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00|600", - "Pacific/Apia|+14 +13|-e0 -d0|0101010101|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0|37e3", - "Pacific/Fiji|+13 +12|-d0 -c0|0101010101|1Swe0 1VA0 s00 1VA0 s00 20o0 pc0 2hc0 bc0|88e4", - "Pacific/Guam|ChST|-a0|0||17e4", - "Pacific/Marquesas|-0930|9u|0||86e2", - "Pacific/Pago_Pago|SST|b0|0||37e2", - "Pacific/Norfolk|+11 +12|-b0 -c0|010101010101010101|219P0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|25e4", - "Pacific/Tongatapu|+14 +13|-e0 -d0|01|1Swd0|75e3", - ], - links: [ - "Africa/Abidjan|Africa/Accra", - "Africa/Abidjan|Africa/Bamako", - "Africa/Abidjan|Africa/Banjul", - "Africa/Abidjan|Africa/Bissau", - "Africa/Abidjan|Africa/Conakry", - "Africa/Abidjan|Africa/Dakar", - "Africa/Abidjan|Africa/Freetown", - "Africa/Abidjan|Africa/Lome", - "Africa/Abidjan|Africa/Monrovia", - "Africa/Abidjan|Africa/Nouakchott", - "Africa/Abidjan|Africa/Ouagadougou", - "Africa/Abidjan|Africa/Timbuktu", - "Africa/Abidjan|America/Danmarkshavn", - "Africa/Abidjan|Atlantic/Reykjavik", - "Africa/Abidjan|Atlantic/St_Helena", - "Africa/Abidjan|Etc/GMT", - "Africa/Abidjan|Etc/GMT+0", - "Africa/Abidjan|Etc/GMT-0", - "Africa/Abidjan|Etc/GMT0", - "Africa/Abidjan|Etc/Greenwich", - "Africa/Abidjan|GMT", - "Africa/Abidjan|GMT+0", - "Africa/Abidjan|GMT-0", - "Africa/Abidjan|GMT0", - "Africa/Abidjan|Greenwich", - "Africa/Abidjan|Iceland", - "Africa/Algiers|Africa/Tunis", - "Africa/Cairo|Africa/Tripoli", - "Africa/Cairo|Egypt", - "Africa/Cairo|Europe/Kaliningrad", - "Africa/Cairo|Libya", - "Africa/Casablanca|Africa/El_Aaiun", - "Africa/Johannesburg|Africa/Maseru", - "Africa/Johannesburg|Africa/Mbabane", - "Africa/Lagos|Africa/Bangui", - "Africa/Lagos|Africa/Brazzaville", - "Africa/Lagos|Africa/Douala", - "Africa/Lagos|Africa/Kinshasa", - "Africa/Lagos|Africa/Libreville", - "Africa/Lagos|Africa/Luanda", - "Africa/Lagos|Africa/Malabo", - "Africa/Lagos|Africa/Ndjamena", - "Africa/Lagos|Africa/Niamey", - "Africa/Lagos|Africa/Porto-Novo", - "Africa/Maputo|Africa/Blantyre", - "Africa/Maputo|Africa/Bujumbura", - "Africa/Maputo|Africa/Gaborone", - "Africa/Maputo|Africa/Harare", - "Africa/Maputo|Africa/Kigali", - "Africa/Maputo|Africa/Lubumbashi", - "Africa/Maputo|Africa/Lusaka", - "Africa/Nairobi|Africa/Addis_Ababa", - "Africa/Nairobi|Africa/Asmara", - "Africa/Nairobi|Africa/Asmera", - "Africa/Nairobi|Africa/Dar_es_Salaam", - "Africa/Nairobi|Africa/Djibouti", - "Africa/Nairobi|Africa/Kampala", - "Africa/Nairobi|Africa/Mogadishu", - "Africa/Nairobi|Indian/Antananarivo", - "Africa/Nairobi|Indian/Comoro", - "Africa/Nairobi|Indian/Mayotte", - "America/Adak|America/Atka", - "America/Adak|US/Aleutian", - "America/Anchorage|America/Juneau", - "America/Anchorage|America/Nome", - "America/Anchorage|America/Sitka", - "America/Anchorage|America/Yakutat", - "America/Anchorage|US/Alaska", - "America/Campo_Grande|America/Cuiaba", - "America/Caracas|America/Boa_Vista", - "America/Caracas|America/Guyana", - "America/Caracas|America/La_Paz", - "America/Caracas|America/Manaus", - "America/Caracas|America/Porto_Velho", - "America/Caracas|Brazil/West", - "America/Caracas|Etc/GMT+4", - "America/Chicago|America/Indiana/Knox", - "America/Chicago|America/Indiana/Tell_City", - "America/Chicago|America/Knox_IN", - "America/Chicago|America/Matamoros", - "America/Chicago|America/Menominee", - "America/Chicago|America/North_Dakota/Beulah", - "America/Chicago|America/North_Dakota/Center", - "America/Chicago|America/North_Dakota/New_Salem", - "America/Chicago|America/Rainy_River", - "America/Chicago|America/Rankin_Inlet", - "America/Chicago|America/Resolute", - "America/Chicago|America/Winnipeg", - "America/Chicago|CST6CDT", - "America/Chicago|Canada/Central", - "America/Chicago|US/Central", - "America/Chicago|US/Indiana-Starke", - "America/Denver|America/Boise", - "America/Denver|America/Cambridge_Bay", - "America/Denver|America/Edmonton", - "America/Denver|America/Inuvik", - "America/Denver|America/Shiprock", - "America/Denver|America/Yellowknife", - "America/Denver|Canada/Mountain", - "America/Denver|MST7MDT", - "America/Denver|Navajo", - "America/Denver|US/Mountain", - "America/Fortaleza|America/Araguaina", - "America/Fortaleza|America/Argentina/Buenos_Aires", - "America/Fortaleza|America/Argentina/Catamarca", - "America/Fortaleza|America/Argentina/ComodRivadavia", - "America/Fortaleza|America/Argentina/Cordoba", - "America/Fortaleza|America/Argentina/Jujuy", - "America/Fortaleza|America/Argentina/La_Rioja", - "America/Fortaleza|America/Argentina/Mendoza", - "America/Fortaleza|America/Argentina/Rio_Gallegos", - "America/Fortaleza|America/Argentina/Salta", - "America/Fortaleza|America/Argentina/San_Juan", - "America/Fortaleza|America/Argentina/San_Luis", - "America/Fortaleza|America/Argentina/Tucuman", - "America/Fortaleza|America/Argentina/Ushuaia", - "America/Fortaleza|America/Bahia", - "America/Fortaleza|America/Belem", - "America/Fortaleza|America/Buenos_Aires", - "America/Fortaleza|America/Catamarca", - "America/Fortaleza|America/Cayenne", - "America/Fortaleza|America/Cordoba", - "America/Fortaleza|America/Jujuy", - "America/Fortaleza|America/Maceio", - "America/Fortaleza|America/Mendoza", - "America/Fortaleza|America/Montevideo", - "America/Fortaleza|America/Paramaribo", - "America/Fortaleza|America/Punta_Arenas", - "America/Fortaleza|America/Recife", - "America/Fortaleza|America/Rosario", - "America/Fortaleza|America/Santarem", - "America/Fortaleza|Antarctica/Palmer", - "America/Fortaleza|Antarctica/Rothera", - "America/Fortaleza|Atlantic/Stanley", - "America/Fortaleza|Etc/GMT+3", - "America/Godthab|America/Nuuk", - "America/Halifax|America/Glace_Bay", - "America/Halifax|America/Goose_Bay", - "America/Halifax|America/Moncton", - "America/Halifax|America/Thule", - "America/Halifax|Atlantic/Bermuda", - "America/Halifax|Canada/Atlantic", - "America/Havana|Cuba", - "America/Lima|America/Bogota", - "America/Lima|America/Eirunepe", - "America/Lima|America/Guayaquil", - "America/Lima|America/Porto_Acre", - "America/Lima|America/Rio_Branco", - "America/Lima|Brazil/Acre", - "America/Lima|Etc/GMT+5", - "America/Los_Angeles|America/Ensenada", - "America/Los_Angeles|America/Santa_Isabel", - "America/Los_Angeles|America/Tijuana", - "America/Los_Angeles|America/Vancouver", - "America/Los_Angeles|Canada/Pacific", - "America/Los_Angeles|Mexico/BajaNorte", - "America/Los_Angeles|PST8PDT", - "America/Los_Angeles|US/Pacific", - "America/Managua|America/Belize", - "America/Managua|America/Costa_Rica", - "America/Managua|America/El_Salvador", - "America/Managua|America/Guatemala", - "America/Managua|America/Regina", - "America/Managua|America/Swift_Current", - "America/Managua|America/Tegucigalpa", - "America/Managua|Canada/Saskatchewan", - "America/Mazatlan|Mexico/BajaSur", - "America/Mexico_City|America/Bahia_Banderas", - "America/Mexico_City|America/Merida", - "America/Mexico_City|America/Monterrey", - "America/Mexico_City|Mexico/General", - "America/New_York|America/Detroit", - "America/New_York|America/Fort_Wayne", - "America/New_York|America/Indiana/Indianapolis", - "America/New_York|America/Indiana/Marengo", - "America/New_York|America/Indiana/Petersburg", - "America/New_York|America/Indiana/Vevay", - "America/New_York|America/Indiana/Vincennes", - "America/New_York|America/Indiana/Winamac", - "America/New_York|America/Indianapolis", - "America/New_York|America/Iqaluit", - "America/New_York|America/Kentucky/Louisville", - "America/New_York|America/Kentucky/Monticello", - "America/New_York|America/Louisville", - "America/New_York|America/Montreal", - "America/New_York|America/Nassau", - "America/New_York|America/Nipigon", - "America/New_York|America/Pangnirtung", - "America/New_York|America/Port-au-Prince", - "America/New_York|America/Thunder_Bay", - "America/New_York|America/Toronto", - "America/New_York|Canada/Eastern", - "America/New_York|EST5EDT", - "America/New_York|US/East-Indiana", - "America/New_York|US/Eastern", - "America/New_York|US/Michigan", - "America/Noronha|Atlantic/South_Georgia", - "America/Noronha|Brazil/DeNoronha", - "America/Noronha|Etc/GMT+2", - "America/Panama|America/Atikokan", - "America/Panama|America/Cancun", - "America/Panama|America/Cayman", - "America/Panama|America/Coral_Harbour", - "America/Panama|America/Jamaica", - "America/Panama|EST", - "America/Panama|Jamaica", - "America/Phoenix|America/Creston", - "America/Phoenix|America/Dawson_Creek", - "America/Phoenix|America/Fort_Nelson", - "America/Phoenix|America/Hermosillo", - "America/Phoenix|MST", - "America/Phoenix|US/Arizona", - "America/Santiago|Chile/Continental", - "America/Santo_Domingo|America/Anguilla", - "America/Santo_Domingo|America/Antigua", - "America/Santo_Domingo|America/Aruba", - "America/Santo_Domingo|America/Barbados", - "America/Santo_Domingo|America/Blanc-Sablon", - "America/Santo_Domingo|America/Curacao", - "America/Santo_Domingo|America/Dominica", - "America/Santo_Domingo|America/Grenada", - "America/Santo_Domingo|America/Guadeloupe", - "America/Santo_Domingo|America/Kralendijk", - "America/Santo_Domingo|America/Lower_Princes", - "America/Santo_Domingo|America/Marigot", - "America/Santo_Domingo|America/Martinique", - "America/Santo_Domingo|America/Montserrat", - "America/Santo_Domingo|America/Port_of_Spain", - "America/Santo_Domingo|America/Puerto_Rico", - "America/Santo_Domingo|America/St_Barthelemy", - "America/Santo_Domingo|America/St_Kitts", - "America/Santo_Domingo|America/St_Lucia", - "America/Santo_Domingo|America/St_Thomas", - "America/Santo_Domingo|America/St_Vincent", - "America/Santo_Domingo|America/Tortola", - "America/Santo_Domingo|America/Virgin", - "America/Sao_Paulo|Brazil/East", - "America/St_Johns|Canada/Newfoundland", - "America/Whitehorse|America/Dawson", - "America/Whitehorse|Canada/Yukon", - "Asia/Bangkok|Antarctica/Davis", - "Asia/Bangkok|Asia/Barnaul", - "Asia/Bangkok|Asia/Ho_Chi_Minh", - "Asia/Bangkok|Asia/Hovd", - "Asia/Bangkok|Asia/Krasnoyarsk", - "Asia/Bangkok|Asia/Novokuznetsk", - "Asia/Bangkok|Asia/Novosibirsk", - "Asia/Bangkok|Asia/Phnom_Penh", - "Asia/Bangkok|Asia/Saigon", - "Asia/Bangkok|Asia/Tomsk", - "Asia/Bangkok|Asia/Vientiane", - "Asia/Bangkok|Etc/GMT-7", - "Asia/Bangkok|Indian/Christmas", - "Asia/Chita|Asia/Dili", - "Asia/Chita|Asia/Khandyga", - "Asia/Chita|Asia/Yakutsk", - "Asia/Chita|Etc/GMT-9", - "Asia/Chita|Pacific/Palau", - "Asia/Dhaka|Antarctica/Vostok", - "Asia/Dhaka|Asia/Almaty", - "Asia/Dhaka|Asia/Bishkek", - "Asia/Dhaka|Asia/Dacca", - "Asia/Dhaka|Asia/Kashgar", - "Asia/Dhaka|Asia/Omsk", - "Asia/Dhaka|Asia/Qostanay", - "Asia/Dhaka|Asia/Thimbu", - "Asia/Dhaka|Asia/Thimphu", - "Asia/Dhaka|Asia/Urumqi", - "Asia/Dhaka|Etc/GMT-6", - "Asia/Dhaka|Indian/Chagos", - "Asia/Dubai|Asia/Baku", - "Asia/Dubai|Asia/Muscat", - "Asia/Dubai|Asia/Tbilisi", - "Asia/Dubai|Asia/Yerevan", - "Asia/Dubai|Etc/GMT-4", - "Asia/Dubai|Europe/Astrakhan", - "Asia/Dubai|Europe/Samara", - "Asia/Dubai|Europe/Saratov", - "Asia/Dubai|Europe/Ulyanovsk", - "Asia/Dubai|Indian/Mahe", - "Asia/Dubai|Indian/Mauritius", - "Asia/Dubai|Indian/Reunion", - "Asia/Gaza|Asia/Hebron", - "Asia/Hong_Kong|Hongkong", - "Asia/Jakarta|Asia/Pontianak", - "Asia/Jerusalem|Asia/Tel_Aviv", - "Asia/Jerusalem|Israel", - "Asia/Kamchatka|Asia/Anadyr", - "Asia/Kamchatka|Etc/GMT-12", - "Asia/Kamchatka|Kwajalein", - "Asia/Kamchatka|Pacific/Funafuti", - "Asia/Kamchatka|Pacific/Kwajalein", - "Asia/Kamchatka|Pacific/Majuro", - "Asia/Kamchatka|Pacific/Nauru", - "Asia/Kamchatka|Pacific/Tarawa", - "Asia/Kamchatka|Pacific/Wake", - "Asia/Kamchatka|Pacific/Wallis", - "Asia/Kathmandu|Asia/Katmandu", - "Asia/Kolkata|Asia/Calcutta", - "Asia/Kuala_Lumpur|Asia/Brunei", - "Asia/Kuala_Lumpur|Asia/Choibalsan", - "Asia/Kuala_Lumpur|Asia/Irkutsk", - "Asia/Kuala_Lumpur|Asia/Kuching", - "Asia/Kuala_Lumpur|Asia/Singapore", - "Asia/Kuala_Lumpur|Asia/Ulaanbaatar", - "Asia/Kuala_Lumpur|Asia/Ulan_Bator", - "Asia/Kuala_Lumpur|Etc/GMT-8", - "Asia/Kuala_Lumpur|Singapore", - "Asia/Makassar|Asia/Ujung_Pandang", - "Asia/Rangoon|Asia/Yangon", - "Asia/Rangoon|Indian/Cocos", - "Asia/Sakhalin|Asia/Magadan", - "Asia/Sakhalin|Asia/Srednekolymsk", - "Asia/Sakhalin|Etc/GMT-11", - "Asia/Sakhalin|Pacific/Bougainville", - "Asia/Sakhalin|Pacific/Efate", - "Asia/Sakhalin|Pacific/Guadalcanal", - "Asia/Sakhalin|Pacific/Kosrae", - "Asia/Sakhalin|Pacific/Noumea", - "Asia/Sakhalin|Pacific/Pohnpei", - "Asia/Sakhalin|Pacific/Ponape", - "Asia/Seoul|ROK", - "Asia/Shanghai|Asia/Chongqing", - "Asia/Shanghai|Asia/Chungking", - "Asia/Shanghai|Asia/Harbin", - "Asia/Shanghai|Asia/Macao", - "Asia/Shanghai|Asia/Macau", - "Asia/Shanghai|Asia/Taipei", - "Asia/Shanghai|PRC", - "Asia/Shanghai|ROC", - "Asia/Tashkent|Antarctica/Mawson", - "Asia/Tashkent|Asia/Aqtau", - "Asia/Tashkent|Asia/Aqtobe", - "Asia/Tashkent|Asia/Ashgabat", - "Asia/Tashkent|Asia/Ashkhabad", - "Asia/Tashkent|Asia/Atyrau", - "Asia/Tashkent|Asia/Dushanbe", - "Asia/Tashkent|Asia/Oral", - "Asia/Tashkent|Asia/Samarkand", - "Asia/Tashkent|Asia/Yekaterinburg", - "Asia/Tashkent|Etc/GMT-5", - "Asia/Tashkent|Indian/Kerguelen", - "Asia/Tashkent|Indian/Maldives", - "Asia/Tehran|Iran", - "Asia/Tokyo|Japan", - "Asia/Vladivostok|Antarctica/DumontDUrville", - "Asia/Vladivostok|Asia/Ust-Nera", - "Asia/Vladivostok|Etc/GMT-10", - "Asia/Vladivostok|Pacific/Chuuk", - "Asia/Vladivostok|Pacific/Port_Moresby", - "Asia/Vladivostok|Pacific/Truk", - "Asia/Vladivostok|Pacific/Yap", - "Atlantic/Azores|America/Scoresbysund", - "Atlantic/Cape_Verde|Etc/GMT+1", - "Australia/Adelaide|Australia/Broken_Hill", - "Australia/Adelaide|Australia/South", - "Australia/Adelaide|Australia/Yancowinna", - "Australia/Brisbane|Australia/Lindeman", - "Australia/Brisbane|Australia/Queensland", - "Australia/Darwin|Australia/North", - "Australia/Lord_Howe|Australia/LHI", - "Australia/Perth|Australia/West", - "Australia/Sydney|Antarctica/Macquarie", - "Australia/Sydney|Australia/ACT", - "Australia/Sydney|Australia/Canberra", - "Australia/Sydney|Australia/Currie", - "Australia/Sydney|Australia/Hobart", - "Australia/Sydney|Australia/Melbourne", - "Australia/Sydney|Australia/NSW", - "Australia/Sydney|Australia/Tasmania", - "Australia/Sydney|Australia/Victoria", - "Etc/UTC|Etc/UCT", - "Etc/UTC|Etc/Universal", - "Etc/UTC|Etc/Zulu", - "Etc/UTC|UCT", - "Etc/UTC|UTC", - "Etc/UTC|Universal", - "Etc/UTC|Zulu", - "Europe/Athens|Asia/Nicosia", - "Europe/Athens|EET", - "Europe/Athens|Europe/Bucharest", - "Europe/Athens|Europe/Helsinki", - "Europe/Athens|Europe/Kiev", - "Europe/Athens|Europe/Kyiv", - "Europe/Athens|Europe/Mariehamn", - "Europe/Athens|Europe/Nicosia", - "Europe/Athens|Europe/Riga", - "Europe/Athens|Europe/Sofia", - "Europe/Athens|Europe/Tallinn", - "Europe/Athens|Europe/Uzhgorod", - "Europe/Athens|Europe/Vilnius", - "Europe/Athens|Europe/Zaporozhye", - "Europe/Chisinau|Europe/Tiraspol", - "Europe/Dublin|Eire", - "Europe/Istanbul|Antarctica/Syowa", - "Europe/Istanbul|Asia/Aden", - "Europe/Istanbul|Asia/Baghdad", - "Europe/Istanbul|Asia/Bahrain", - "Europe/Istanbul|Asia/Istanbul", - "Europe/Istanbul|Asia/Kuwait", - "Europe/Istanbul|Asia/Qatar", - "Europe/Istanbul|Asia/Riyadh", - "Europe/Istanbul|Etc/GMT-3", - "Europe/Istanbul|Europe/Kirov", - "Europe/Istanbul|Europe/Minsk", - "Europe/Istanbul|Turkey", - "Europe/Lisbon|Atlantic/Canary", - "Europe/Lisbon|Atlantic/Faeroe", - "Europe/Lisbon|Atlantic/Faroe", - "Europe/Lisbon|Atlantic/Madeira", - "Europe/Lisbon|Portugal", - "Europe/Lisbon|WET", - "Europe/London|Europe/Belfast", - "Europe/London|Europe/Guernsey", - "Europe/London|Europe/Isle_of_Man", - "Europe/London|Europe/Jersey", - "Europe/London|GB", - "Europe/London|GB-Eire", - "Europe/Moscow|Europe/Simferopol", - "Europe/Moscow|W-SU", - "Europe/Paris|Africa/Ceuta", - "Europe/Paris|Arctic/Longyearbyen", - "Europe/Paris|Atlantic/Jan_Mayen", - "Europe/Paris|CET", - "Europe/Paris|Europe/Amsterdam", - "Europe/Paris|Europe/Andorra", - "Europe/Paris|Europe/Belgrade", - "Europe/Paris|Europe/Berlin", - "Europe/Paris|Europe/Bratislava", - "Europe/Paris|Europe/Brussels", - "Europe/Paris|Europe/Budapest", - "Europe/Paris|Europe/Busingen", - "Europe/Paris|Europe/Copenhagen", - "Europe/Paris|Europe/Gibraltar", - "Europe/Paris|Europe/Ljubljana", - "Europe/Paris|Europe/Luxembourg", - "Europe/Paris|Europe/Madrid", - "Europe/Paris|Europe/Malta", - "Europe/Paris|Europe/Monaco", - "Europe/Paris|Europe/Oslo", - "Europe/Paris|Europe/Podgorica", - "Europe/Paris|Europe/Prague", - "Europe/Paris|Europe/Rome", - "Europe/Paris|Europe/San_Marino", - "Europe/Paris|Europe/Sarajevo", - "Europe/Paris|Europe/Skopje", - "Europe/Paris|Europe/Stockholm", - "Europe/Paris|Europe/Tirane", - "Europe/Paris|Europe/Vaduz", - "Europe/Paris|Europe/Vatican", - "Europe/Paris|Europe/Vienna", - "Europe/Paris|Europe/Warsaw", - "Europe/Paris|Europe/Zagreb", - "Europe/Paris|Europe/Zurich", - "Europe/Paris|Poland", - "Pacific/Auckland|Antarctica/McMurdo", - "Pacific/Auckland|Antarctica/South_Pole", - "Pacific/Auckland|NZ", - "Pacific/Chatham|NZ-CHAT", - "Pacific/Easter|Chile/EasterIsland", - "Pacific/Fakaofo|Etc/GMT-13", - "Pacific/Fakaofo|Pacific/Enderbury", - "Pacific/Fakaofo|Pacific/Kanton", - "Pacific/Galapagos|Etc/GMT+6", - "Pacific/Gambier|Etc/GMT+9", - "Pacific/Guam|Pacific/Saipan", - "Pacific/Honolulu|HST", - "Pacific/Honolulu|Pacific/Johnston", - "Pacific/Honolulu|US/Hawaii", - "Pacific/Kiritimati|Etc/GMT-14", - "Pacific/Niue|Etc/GMT+11", - "Pacific/Pago_Pago|Pacific/Midway", - "Pacific/Pago_Pago|Pacific/Samoa", - "Pacific/Pago_Pago|US/Samoa", - "Pacific/Pitcairn|Etc/GMT+8", - "Pacific/Tahiti|Etc/GMT+10", - "Pacific/Tahiti|Pacific/Rarotonga", - ], - countries: [ - "AD|Europe/Andorra", - "AE|Asia/Dubai", - "AF|Asia/Kabul", - "AG|America/Puerto_Rico America/Antigua", - "AI|America/Puerto_Rico America/Anguilla", - "AL|Europe/Tirane", - "AM|Asia/Yerevan", - "AO|Africa/Lagos Africa/Luanda", - "AQ|Antarctica/Casey Antarctica/Davis Antarctica/Mawson Antarctica/Palmer Antarctica/Rothera Antarctica/Troll Asia/Urumqi Pacific/Auckland Pacific/Port_Moresby Asia/Riyadh Antarctica/McMurdo Antarctica/DumontDUrville Antarctica/Syowa Antarctica/Vostok", - "AR|America/Argentina/Buenos_Aires America/Argentina/Cordoba America/Argentina/Salta America/Argentina/Jujuy America/Argentina/Tucuman America/Argentina/Catamarca America/Argentina/La_Rioja America/Argentina/San_Juan America/Argentina/Mendoza America/Argentina/San_Luis America/Argentina/Rio_Gallegos America/Argentina/Ushuaia", - "AS|Pacific/Pago_Pago", - "AT|Europe/Vienna", - "AU|Australia/Lord_Howe Antarctica/Macquarie Australia/Hobart Australia/Melbourne Australia/Sydney Australia/Broken_Hill Australia/Brisbane Australia/Lindeman Australia/Adelaide Australia/Darwin Australia/Perth Australia/Eucla", - "AW|America/Puerto_Rico America/Aruba", - "AX|Europe/Helsinki Europe/Mariehamn", - "AZ|Asia/Baku", - "BA|Europe/Belgrade Europe/Sarajevo", - "BB|America/Barbados", - "BD|Asia/Dhaka", - "BE|Europe/Brussels", - "BF|Africa/Abidjan Africa/Ouagadougou", - "BG|Europe/Sofia", - "BH|Asia/Qatar Asia/Bahrain", - "BI|Africa/Maputo Africa/Bujumbura", - "BJ|Africa/Lagos Africa/Porto-Novo", - "BL|America/Puerto_Rico America/St_Barthelemy", - "BM|Atlantic/Bermuda", - "BN|Asia/Kuching Asia/Brunei", - "BO|America/La_Paz", - "BQ|America/Puerto_Rico America/Kralendijk", - "BR|America/Noronha America/Belem America/Fortaleza America/Recife America/Araguaina America/Maceio America/Bahia America/Sao_Paulo America/Campo_Grande America/Cuiaba America/Santarem America/Porto_Velho America/Boa_Vista America/Manaus America/Eirunepe America/Rio_Branco", - "BS|America/Toronto America/Nassau", - "BT|Asia/Thimphu", - "BW|Africa/Maputo Africa/Gaborone", - "BY|Europe/Minsk", - "BZ|America/Belize", - "CA|America/St_Johns America/Halifax America/Glace_Bay America/Moncton America/Goose_Bay America/Toronto America/Iqaluit America/Winnipeg America/Resolute America/Rankin_Inlet America/Regina America/Swift_Current America/Edmonton America/Cambridge_Bay America/Yellowknife America/Inuvik America/Dawson_Creek America/Fort_Nelson America/Whitehorse America/Dawson America/Vancouver America/Panama America/Puerto_Rico America/Phoenix America/Blanc-Sablon America/Atikokan America/Creston", - "CC|Asia/Yangon Indian/Cocos", - "CD|Africa/Maputo Africa/Lagos Africa/Kinshasa Africa/Lubumbashi", - "CF|Africa/Lagos Africa/Bangui", - "CG|Africa/Lagos Africa/Brazzaville", - "CH|Europe/Zurich", - "CI|Africa/Abidjan", - "CK|Pacific/Rarotonga", - "CL|America/Santiago America/Punta_Arenas Pacific/Easter", - "CM|Africa/Lagos Africa/Douala", - "CN|Asia/Shanghai Asia/Urumqi", - "CO|America/Bogota", - "CR|America/Costa_Rica", - "CU|America/Havana", - "CV|Atlantic/Cape_Verde", - "CW|America/Puerto_Rico America/Curacao", - "CX|Asia/Bangkok Indian/Christmas", - "CY|Asia/Nicosia Asia/Famagusta", - "CZ|Europe/Prague", - "DE|Europe/Zurich Europe/Berlin Europe/Busingen", - "DJ|Africa/Nairobi Africa/Djibouti", - "DK|Europe/Berlin Europe/Copenhagen", - "DM|America/Puerto_Rico America/Dominica", - "DO|America/Santo_Domingo", - "DZ|Africa/Algiers", - "EC|America/Guayaquil Pacific/Galapagos", - "EE|Europe/Tallinn", - "EG|Africa/Cairo", - "EH|Africa/El_Aaiun", - "ER|Africa/Nairobi Africa/Asmara", - "ES|Europe/Madrid Africa/Ceuta Atlantic/Canary", - "ET|Africa/Nairobi Africa/Addis_Ababa", - "FI|Europe/Helsinki", - "FJ|Pacific/Fiji", - "FK|Atlantic/Stanley", - "FM|Pacific/Kosrae Pacific/Port_Moresby Pacific/Guadalcanal Pacific/Chuuk Pacific/Pohnpei", - "FO|Atlantic/Faroe", - "FR|Europe/Paris", - "GA|Africa/Lagos Africa/Libreville", - "GB|Europe/London", - "GD|America/Puerto_Rico America/Grenada", - "GE|Asia/Tbilisi", - "GF|America/Cayenne", - "GG|Europe/London Europe/Guernsey", - "GH|Africa/Abidjan Africa/Accra", - "GI|Europe/Gibraltar", - "GL|America/Nuuk America/Danmarkshavn America/Scoresbysund America/Thule", - "GM|Africa/Abidjan Africa/Banjul", - "GN|Africa/Abidjan Africa/Conakry", - "GP|America/Puerto_Rico America/Guadeloupe", - "GQ|Africa/Lagos Africa/Malabo", - "GR|Europe/Athens", - "GS|Atlantic/South_Georgia", - "GT|America/Guatemala", - "GU|Pacific/Guam", - "GW|Africa/Bissau", - "GY|America/Guyana", - "HK|Asia/Hong_Kong", - "HN|America/Tegucigalpa", - "HR|Europe/Belgrade Europe/Zagreb", - "HT|America/Port-au-Prince", - "HU|Europe/Budapest", - "ID|Asia/Jakarta Asia/Pontianak Asia/Makassar Asia/Jayapura", - "IE|Europe/Dublin", - "IL|Asia/Jerusalem", - "IM|Europe/London Europe/Isle_of_Man", - "IN|Asia/Kolkata", - "IO|Indian/Chagos", - "IQ|Asia/Baghdad", - "IR|Asia/Tehran", - "IS|Africa/Abidjan Atlantic/Reykjavik", - "IT|Europe/Rome", - "JE|Europe/London Europe/Jersey", - "JM|America/Jamaica", - "JO|Asia/Amman", - "JP|Asia/Tokyo", - "KE|Africa/Nairobi", - "KG|Asia/Bishkek", - "KH|Asia/Bangkok Asia/Phnom_Penh", - "KI|Pacific/Tarawa Pacific/Kanton Pacific/Kiritimati", - "KM|Africa/Nairobi Indian/Comoro", - "KN|America/Puerto_Rico America/St_Kitts", - "KP|Asia/Pyongyang", - "KR|Asia/Seoul", - "KW|Asia/Riyadh Asia/Kuwait", - "KY|America/Panama America/Cayman", - "KZ|Asia/Almaty Asia/Qyzylorda Asia/Qostanay Asia/Aqtobe Asia/Aqtau Asia/Atyrau Asia/Oral", - "LA|Asia/Bangkok Asia/Vientiane", - "LB|Asia/Beirut", - "LC|America/Puerto_Rico America/St_Lucia", - "LI|Europe/Zurich Europe/Vaduz", - "LK|Asia/Colombo", - "LR|Africa/Monrovia", - "LS|Africa/Johannesburg Africa/Maseru", - "LT|Europe/Vilnius", - "LU|Europe/Brussels Europe/Luxembourg", - "LV|Europe/Riga", - "LY|Africa/Tripoli", - "MA|Africa/Casablanca", - "MC|Europe/Paris Europe/Monaco", - "MD|Europe/Chisinau", - "ME|Europe/Belgrade Europe/Podgorica", - "MF|America/Puerto_Rico America/Marigot", - "MG|Africa/Nairobi Indian/Antananarivo", - "MH|Pacific/Tarawa Pacific/Kwajalein Pacific/Majuro", - "MK|Europe/Belgrade Europe/Skopje", - "ML|Africa/Abidjan Africa/Bamako", - "MM|Asia/Yangon", - "MN|Asia/Ulaanbaatar Asia/Hovd Asia/Choibalsan", - "MO|Asia/Macau", - "MP|Pacific/Guam Pacific/Saipan", - "MQ|America/Martinique", - "MR|Africa/Abidjan Africa/Nouakchott", - "MS|America/Puerto_Rico America/Montserrat", - "MT|Europe/Malta", - "MU|Indian/Mauritius", - "MV|Indian/Maldives", - "MW|Africa/Maputo Africa/Blantyre", - "MX|America/Mexico_City America/Cancun America/Merida America/Monterrey America/Matamoros America/Chihuahua America/Ciudad_Juarez America/Ojinaga America/Mazatlan America/Bahia_Banderas America/Hermosillo America/Tijuana", - "MY|Asia/Kuching Asia/Singapore Asia/Kuala_Lumpur", - "MZ|Africa/Maputo", - "NA|Africa/Windhoek", - "NC|Pacific/Noumea", - "NE|Africa/Lagos Africa/Niamey", - "NF|Pacific/Norfolk", - "NG|Africa/Lagos", - "NI|America/Managua", - "NL|Europe/Brussels Europe/Amsterdam", - "NO|Europe/Berlin Europe/Oslo", - "NP|Asia/Kathmandu", - "NR|Pacific/Nauru", - "NU|Pacific/Niue", - "NZ|Pacific/Auckland Pacific/Chatham", - "OM|Asia/Dubai Asia/Muscat", - "PA|America/Panama", - "PE|America/Lima", - "PF|Pacific/Tahiti Pacific/Marquesas Pacific/Gambier", - "PG|Pacific/Port_Moresby Pacific/Bougainville", - "PH|Asia/Manila", - "PK|Asia/Karachi", - "PL|Europe/Warsaw", - "PM|America/Miquelon", - "PN|Pacific/Pitcairn", - "PR|America/Puerto_Rico", - "PS|Asia/Gaza Asia/Hebron", - "PT|Europe/Lisbon Atlantic/Madeira Atlantic/Azores", - "PW|Pacific/Palau", - "PY|America/Asuncion", - "QA|Asia/Qatar", - "RE|Asia/Dubai Indian/Reunion", - "RO|Europe/Bucharest", - "RS|Europe/Belgrade", - "RU|Europe/Kaliningrad Europe/Moscow Europe/Simferopol Europe/Kirov Europe/Volgograd Europe/Astrakhan Europe/Saratov Europe/Ulyanovsk Europe/Samara Asia/Yekaterinburg Asia/Omsk Asia/Novosibirsk Asia/Barnaul Asia/Tomsk Asia/Novokuznetsk Asia/Krasnoyarsk Asia/Irkutsk Asia/Chita Asia/Yakutsk Asia/Khandyga Asia/Vladivostok Asia/Ust-Nera Asia/Magadan Asia/Sakhalin Asia/Srednekolymsk Asia/Kamchatka Asia/Anadyr", - "RW|Africa/Maputo Africa/Kigali", - "SA|Asia/Riyadh", - "SB|Pacific/Guadalcanal", - "SC|Asia/Dubai Indian/Mahe", - "SD|Africa/Khartoum", - "SE|Europe/Berlin Europe/Stockholm", - "SG|Asia/Singapore", - "SH|Africa/Abidjan Atlantic/St_Helena", - "SI|Europe/Belgrade Europe/Ljubljana", - "SJ|Europe/Berlin Arctic/Longyearbyen", - "SK|Europe/Prague Europe/Bratislava", - "SL|Africa/Abidjan Africa/Freetown", - "SM|Europe/Rome Europe/San_Marino", - "SN|Africa/Abidjan Africa/Dakar", - "SO|Africa/Nairobi Africa/Mogadishu", - "SR|America/Paramaribo", - "SS|Africa/Juba", - "ST|Africa/Sao_Tome", - "SV|America/El_Salvador", - "SX|America/Puerto_Rico America/Lower_Princes", - "SY|Asia/Damascus", - "SZ|Africa/Johannesburg Africa/Mbabane", - "TC|America/Grand_Turk", - "TD|Africa/Ndjamena", - "TF|Asia/Dubai Indian/Maldives Indian/Kerguelen", - "TG|Africa/Abidjan Africa/Lome", - "TH|Asia/Bangkok", - "TJ|Asia/Dushanbe", - "TK|Pacific/Fakaofo", - "TL|Asia/Dili", - "TM|Asia/Ashgabat", - "TN|Africa/Tunis", - "TO|Pacific/Tongatapu", - "TR|Europe/Istanbul", - "TT|America/Puerto_Rico America/Port_of_Spain", - "TV|Pacific/Tarawa Pacific/Funafuti", - "TW|Asia/Taipei", - "TZ|Africa/Nairobi Africa/Dar_es_Salaam", - "UA|Europe/Simferopol Europe/Kyiv", - "UG|Africa/Nairobi Africa/Kampala", - "UM|Pacific/Pago_Pago Pacific/Tarawa Pacific/Honolulu Pacific/Midway Pacific/Wake", - "US|America/New_York America/Detroit America/Kentucky/Louisville America/Kentucky/Monticello America/Indiana/Indianapolis America/Indiana/Vincennes America/Indiana/Winamac America/Indiana/Marengo America/Indiana/Petersburg America/Indiana/Vevay America/Chicago America/Indiana/Tell_City America/Indiana/Knox America/Menominee America/North_Dakota/Center America/North_Dakota/New_Salem America/North_Dakota/Beulah America/Denver America/Boise America/Phoenix America/Los_Angeles America/Anchorage America/Juneau America/Sitka America/Metlakatla America/Yakutat America/Nome America/Adak Pacific/Honolulu", - "UY|America/Montevideo", - "UZ|Asia/Samarkand Asia/Tashkent", - "VA|Europe/Rome Europe/Vatican", - "VC|America/Puerto_Rico America/St_Vincent", - "VE|America/Caracas", - "VG|America/Puerto_Rico America/Tortola", - "VI|America/Puerto_Rico America/St_Thomas", - "VN|Asia/Bangkok Asia/Ho_Chi_Minh", - "VU|Pacific/Efate", - "WF|Pacific/Tarawa Pacific/Wallis", - "WS|Pacific/Apia", - "YE|Asia/Riyadh Asia/Aden", - "YT|Africa/Nairobi Indian/Mayotte", - "ZA|Africa/Johannesburg", - "ZM|Africa/Maputo Africa/Lusaka", - "ZW|Africa/Maputo Africa/Harare", - ], - }); - - return moment; -}); diff --git a/app/templates/assiduites/pages/bilan_etud.j2 b/app/templates/assiduites/pages/bilan_etud.j2 index bba1dae0e..304adf221 100644 --- a/app/templates/assiduites/pages/bilan_etud.j2 +++ b/app/templates/assiduites/pages/bilan_etud.j2 @@ -101,8 +101,7 @@ return; } - - countAssiduites(date_debut.toIsoUtcString()(), date_fin.toIsoUtcString()()) + countAssiduites(date_debut.toIsoUtcString(), date_fin.toIsoUtcString()) } diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index e4e793d39..f9a949136 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -111,10 +111,11 @@ + + + +
+ + +
+ + + \ No newline at end of file diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 7d87ac679..38eadef16 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -1297,6 +1297,16 @@ def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str: ) +@bp.route("/test", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoView) +def test(): + """XXX fonction de test a retirer""" + if request.method == "POST": + print("test date_utils : ", request.form) + return render_template("assiduites/pages/test.j2") + + # --- Fonctions internes --- From 338d2f7ec8229c1d623733bdf4f4f14f0c194999 Mon Sep 17 00:00:00 2001 From: Iziram Date: Tue, 14 Nov 2023 16:38:07 +0100 Subject: [PATCH 06/17] Assiduites : fix journee entiere + utilisation scodoc-datetime --- app/scodoc/sco_utils.py | 2 +- app/static/js/date_utils.js | 84 ++++++++++++++++--- .../assiduites/pages/ajout_justificatif.j2 | 58 ++++++------- 3 files changed, 100 insertions(+), 44 deletions(-) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index bb26b58ad..6e04677dc 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -231,7 +231,7 @@ def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or No try: date: datetime.datetime = datetime.datetime.fromisoformat(date) return date if convert else True - except (dtparser.ParserError, ValueError, TypeError): + except (ValueError, TypeError): return None if convert else False diff --git a/app/static/js/date_utils.js b/app/static/js/date_utils.js index d22487d32..486de19ee 100644 --- a/app/static/js/date_utils.js +++ b/app/static/js/date_utils.js @@ -1,3 +1,8 @@ +Object.defineProperty(Date.prototype, "isValid", { + value: function () { + return !Number.isNaN(this.getTime()); + }, +}); Object.defineProperty(Date.prototype, "startOf", { /** * Génère u la date à la plus petite valeur pour la précision donnée. @@ -338,6 +343,7 @@ class ScoDocDateTimePicker extends HTMLElement { const timeInput = document.createElement("input"); timeInput.type = "time"; timeInput.id = "time"; + timeInput.step = 60; // Ajouter les inputs dans le shadow DOM shadow.appendChild(dateInput); @@ -362,6 +368,10 @@ class ScoDocDateTimePicker extends HTMLElement { shadow.appendChild(style); } + static get observedAttributes() { + return ["show"]; // Ajoute 'show' à la liste des attributs observés + } + connectedCallback() { // Récupérer l'attribut 'name' this.name = this.getAttribute("name"); @@ -386,6 +396,34 @@ class ScoDocDateTimePicker extends HTMLElement { : ""; } }); + this.updateDisplay(); + } + + attributeChangedCallback(name, oldValue, newValue) { + if (name === "show") { + this.updateDisplay(); // Met à jour l'affichage si l'attribut 'show' change + } + } + + updateDisplay() { + const mode = this.getAttribute("show") || "both"; + const dateInput = this.shadowRoot.querySelector("#date"); + const timeInput = this.shadowRoot.querySelector("#time"); + + switch (mode) { + case "date": + dateInput.style.display = "inline-block"; + timeInput.style.display = "none"; + break; + case "time": + dateInput.style.display = "none"; + timeInput.style.display = "inline-block"; + break; + case "both": + default: + dateInput.style.display = "inline-block"; + timeInput.style.display = "inline-block"; + } } // Vérifier si la valeur forme une date valide @@ -418,17 +456,13 @@ class ScoDocDateTimePicker extends HTMLElement { return this._value; } - // Setter pour définir la valeur. Sépare la valeur en date et heure et les définit individuellement. - set value(val) { - let [date, time] = val.split("T"); - this.shadowRoot.querySelector("#date").value = date; - - if ((time.match(/0/g) || []).length > 1) { - time = time.slice(0, time.indexOf(":") + 3); - } - - this.shadowRoot.querySelector("#time").value = time; - this._value = val; + get valueAsObject() { + const dateInput = this.shadowRoot.querySelector("#date"); + const timeInput = this.shadowRoot.querySelector("#time"); + return { + date: dateInput.value, + time: timeInput.value, + }; } // Getter pour obtenir la valeur en tant qu'objet Date. @@ -436,6 +470,34 @@ class ScoDocDateTimePicker extends HTMLElement { return new Date(this._value); } + // Setter pour définir la valeur. Sépare la valeur en date et heure et les définit individuellement. + set value(val) { + let [date, time] = val.split("T"); + this.shadowRoot.querySelector("#date").value = date; + + time = time.substring(0, 5); + + this.shadowRoot.querySelector("#time").value = time; + this._value = val; + } + + // Setter pour définir la valeur à partir d'un objet avec les propriétés 'date' et 'time'. + set valueAsObject(obj) { + const dateInput = this.shadowRoot.querySelector("#date"); + const timeInput = this.shadowRoot.querySelector("#time"); + + if (obj.hasOwnProperty("date")) { + dateInput.value = obj.date || ""; // Définit la valeur de l'input de date si elle est fournie + } + + if (obj.hasOwnProperty("time")) { + timeInput.value = obj.time.substring(0, 5) || ""; // Définit la valeur de l'input d'heure si elle est fournie + } + + // Met à jour la valeur interne en fonction des nouvelles valeurs des inputs + this.updateValue(); + } + // Setter pour définir la valeur à partir d'un objet Date. set valueAsDate(dateVal) { // Formatage de l'objet Date en string et mise à jour de la valeur. diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index 0c102cb13..0935ed60b 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -11,15 +11,11 @@
-
- - -
Date de début - Journée(s) entière(s) + Journée entière
Date de fin @@ -55,6 +51,10 @@
+
+ + +
@@ -112,15 +112,14 @@ function validateFields() { const field = document.querySelector('.justi-form') const { deb, fin } = getDates() + const date_debut = new Date(deb); + const date_fin = new Date(fin); - if (deb == "" || fin == "") { + if (deb == "" || fin == "" || !date_debut.isValid() || !date_fin.isValid()) { openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin valide."), "", color = "crimson"); return false; } - const date_debut = new Date(deb); - const date_fin = new Date(fin); - if (date_fin.isBefore(date_debut)) { openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson"); return false; @@ -219,39 +218,31 @@ } function dayOnly() { - const { deb, fin } = getDates(); + const date_deb = document.getElementById("justi_date_debut"); + const date_fin = document.getElementById("justi_date_fin"); if (document.getElementById('justi_journee').checked) { - document.getElementById("justi_date_debut").type = "date" - document.getElementById("justi_date_debut").value = deb.slice(0, deb.indexOf('T')) - - document.getElementById("justi_date_fin").type = "date" - document.getElementById("justi_date_fin").value = fin.slice(0, fin.indexOf('T')) + date_deb.setAttribute("show", "date") + date_fin.setAttribute("show", "date") + document.getElementById("date_fin").classList.add("hidden"); } else { - document.getElementById("justi_date_debut").type = "datetime-local" - document.getElementById("justi_date_debut").value = `${deb}T${assi_morning}` + date_deb.removeAttribute("show") + date_fin.removeAttribute("show") + document.getElementById("date_fin").classList.remove("hidden"); - document.getElementById("justi_date_fin").type = "datetime-local" - document.getElementById("justi_date_fin").value = `${fin}T${assi_evening}` } } function getDates() { - if (document.querySelector('.page #justi_journee').checked) { - const date_str_deb = document.querySelector(".page #justi_date_debut").value - const date_str_fin = document.querySelector(".page #justi_date_fin").value - - - - return { - "deb": date_str_deb ? `${date_str_deb}T${assi_morning}` : "", - "fin": date_str_fin ? `${date_str_fin}T${assi_evening}` : "", - } - } + const date_deb = document.querySelector(".page #justi_date_debut") + const date_fin = document.querySelector(".page #justi_date_fin") + const journee = document.querySelector('.page #justi_journee').checked + const deb = date_deb.valueAsObject.date + "T" + (journee ? assi_morning : date_deb.valueAsObject.time) + const fin = (journee ? date_deb.valueAsObject.date : date_fin.valueAsObject.date) + "T" + (journee ? assi_evening : date_fin.valueAsObject.time) return { - "deb": document.querySelector(".page #justi_date_debut").value, - "fin": document.querySelector(".page #justi_date_fin").value, + "deb": deb, + "fin": fin, } } @@ -265,6 +256,9 @@ loadAll(); document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() }); dayOnly() + + document.getElementById("justi_date_debut").valueAsObject = { time: assi_morning } + document.getElementById("justi_date_fin").valueAsObject = { time: assi_evening } } {% endblock pageContent %} \ No newline at end of file From 511016bbcf74daa181bcfca5711ab48b1a7e2a10 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Nov 2023 15:55:52 +0100 Subject: [PATCH 07/17] EDT: config.: chargement d'un sample ics pour aider --- app/scodoc/sco_edt_cal.py | 45 +++++++++------ .../assiduites/pages/config_assiduites.j2 | 57 ++++++++++++++++++- app/views/scodoc.py | 20 ++++++- 3 files changed, 101 insertions(+), 21 deletions(-) diff --git a/app/scodoc/sco_edt_cal.py b/app/scodoc/sco_edt_cal.py index 7d63add01..476546f9b 100644 --- a/app/scodoc/sco_edt_cal.py +++ b/app/scodoc/sco_edt_cal.py @@ -41,50 +41,58 @@ from app.scodoc.sco_exceptions import ScoValueError import app.scodoc.sco_utils as scu -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)" - ) - return None +def get_ics_filename(edt_id: str) -> str: + "Le chemin vers l'ics de cet edt_id" 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) + return edt_ics_path.format(edt_id=edt_id) + + +def formsemestre_load_calendar( + formsemestre: FormSemestre = None, edt_id: str = None +) -> tuple[bytes, icalendar.cal.Calendar]: + """Load ics data, return raw ics and decoded calendar. + Raises ScoValueError if not configured or not available or invalid format. + """ + if edt_id is None and formsemestre: + edt_id = formsemestre.get_edt_id() + if not edt_id: + raise ScoValueError( + "accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)" + ) + ics_filename = get_ics_filename(edt_id) try: with open(ics_filename, "rb") as file: log(f"Loading edt from {ics_filename}") + data = file.read() try: - calendar = icalendar.Calendar.from_ical(file.read()) + calendar = icalendar.Calendar.from_ical(data) except ValueError as exc: log( f"""formsemestre_load_calendar: error importing ics for { - formsemestre}\npath='{ics_filename}'""" + formsemestre or ''}\npath='{ics_filename}'""" ) raise ScoValueError( f"calendrier ics illisible (edt_id={edt_id})" ) from exc except FileNotFoundError as exc: log( - f"formsemestre_load_calendar: ics not found for {formsemestre}\npath='{ics_filename}'" + f"formsemestre_load_calendar: ics not found for {formsemestre or ''}\npath='{ics_filename}'" ) raise ScoValueError( f"Fichier ics introuvable (filename={ics_filename})" ) from exc except PermissionError as exc: log( - f"""formsemestre_load_calendar: permission denied for {formsemestre + f"""formsemestre_load_calendar: permission denied for {formsemestre or '' }\npath='{ics_filename}'""" ) raise ScoValueError( f"Fichier ics inaccessible: vérifier permissions (filename={ics_filename})" ) from exc - return calendar + return data, calendar # --- Couleurs des évènements emploi du temps @@ -178,7 +186,7 @@ def formsemestre_edt_dict(formsemestre: FormSemestre) -> list[dict]: def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]: "chargement fichier, filtrage et extraction des identifiants." # Chargement du calendier ics - calendar = formsemestre_load_calendar(formsemestre) + _, calendar = formsemestre_load_calendar(formsemestre) if not calendar: return [] # --- Paramètres d'extraction @@ -236,7 +244,8 @@ def _load_and_convert_ics(formsemestre: FormSemestre) -> list[dict]: edt_group = extract_event_data( event, edt_ics_group_field, edt_ics_group_pattern ) - # si pas de groupe dans l'event, oi si groupe non reconnu, prend toute la promo ("tous") + # si pas de groupe dans l'event, ou si groupe non reconnu, + # prend toute la promo ("tous") group: GroupDescr = ( edt2group.get(edt_group, default_group) if edt_group diff --git a/app/templates/assiduites/pages/config_assiduites.j2 b/app/templates/assiduites/pages/config_assiduites.j2 index 7522968a9..3d75e9b8c 100644 --- a/app/templates/assiduites/pages/config_assiduites.j2 +++ b/app/templates/assiduites/pages/config_assiduites.j2 @@ -10,9 +10,50 @@ div.config-section { margin-right: -15px; margin-left: -15px; } - -{% endblock %} +#zone-test { + margin-bottom: 12px; +} +#raw-ics-sample-zone { + display: none; +} +#raw-ics-sample-zone>div { + font-style: italic; +} +#test_load_ics:disabled { + background-color: gray; + color: white; /* Optional: change text color if needed */ + cursor: not-allowed; +} + +{% endblock styles %} + + +{% block scripts %} +{{ super() }} + + +{% endblock scripts %} {% block app_content %}
@@ -46,6 +87,18 @@ affectent notamment les comptages d'absences de tous les bulletins des {{ wtf.form_field(form.edt_ics_path) }}
+
+ Pour essayer, indiquer un edt_id : + + +
+
Voici un évènement chargé au milieu de ce calendrier: +
+ +
+
Extraction des identifiants depuis les calendriers
Indiquer ici comment récupérer les informations (titre, groupe, module) diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 4d5af816f..6c30eec34 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -81,7 +81,7 @@ from app.models import ( from app.models import departements from app.models.config import PersonalizedLink - +from app.scodoc import sco_edt_cal from app.scodoc import sco_find_etud from app.scodoc import sco_logos from app.scodoc import sco_utils as scu @@ -376,6 +376,24 @@ def config_assiduites(): ) +@bp.route("/ScoDoc/ics_raw_sample/") +@admin_required +def ics_raw_sample(edt_id: str): + "Renvoie un extrait de l'ics brut, pour aider à configurer les extractions" + try: + raw_ics, _ = sco_edt_cal.formsemestre_load_calendar(edt_id=edt_id) + except ScoValueError as exc: + return exc.args[0] + try: + ics = raw_ics.decode(scu.SCO_ENCODING) + except SyntaxError: + return f"Erreur lors de la conversion vers {scu.SCO_ENCODING}" + evs = ics.split("BEGIN:VEVENT") + if len(evs) < 1: + return "pas d'évènements VEVENT détectés dans ce fichier" + return "BEGIN:VEVENT" + evs[len(evs) // 2] + + @bp.route("/ScoDoc/config_codes_decisions", methods=["GET", "POST"]) @admin_required def config_codes_decisions(): From eaa57d0eb74193d8dded638ceaa33cc43a3defa6 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Nov 2023 19:26:44 +0100 Subject: [PATCH 08/17] =?UTF-8?q?ajout=20l=C3=A9gende?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/templates/assiduites/pages/config_assiduites.j2 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/templates/assiduites/pages/config_assiduites.j2 b/app/templates/assiduites/pages/config_assiduites.j2 index 3d75e9b8c..620e3b760 100644 --- a/app/templates/assiduites/pages/config_assiduites.j2 +++ b/app/templates/assiduites/pages/config_assiduites.j2 @@ -93,7 +93,9 @@ affectent notamment les comptages d'absences de tous les bulletins des
-
Voici un évènement chargé au milieu de ce calendrier: +
Voici un évènement chargé au milieu de ce calendrier. + Utilisez cet exemple pour configurer les expressions d'extraction + en bas de ce formulaire.
From b673ca2667402d065e02020054300cd13d188672 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Nov 2023 22:48:28 +0100 Subject: [PATCH 09/17] Fix: invalidation cache sur modif evaluation --- app/scodoc/sco_evaluation_edit.py | 8 +++++--- app/static/css/edt.css | 9 ++++++++- app/templates/formsemestre/edt.j2 | 13 +++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/scodoc/sco_evaluation_edit.py b/app/scodoc/sco_evaluation_edit.py index c24be275c..ed63817cd 100644 --- a/app/scodoc/sco_evaluation_edit.py +++ b/app/scodoc/sco_evaluation_edit.py @@ -45,6 +45,7 @@ from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc import html_sco_header +from app.scodoc import sco_cache from app.scodoc import sco_evaluations from app.scodoc import sco_moduleimpl from app.scodoc import sco_preferences @@ -87,7 +88,7 @@ def evaluation_create_form( {html_sco_header.sco_header()}

Opération non autorisée

Modification évaluation impossible pour {current_user.get_nomplogin()}

-

Revenir

@@ -131,7 +132,7 @@ def evaluation_create_form( H = [ f"""

{action} en {scu.MODULE_TYPE_NAMES[mod["module_type"]]} {mod["code"] or "module sans code"} {mod["titre"]} {link}

""" @@ -299,7 +300,7 @@ def evaluation_create_form( "type": "float", "explanation": f""" ({ - "coef. mod.:" +str(coef_ue) if coef_ue + "coef. mod.:" +str(coef_ue) if coef_ue else "ce module n'a pas de coef. dans cette UE" }) {ue.titre} @@ -382,4 +383,5 @@ def evaluation_create_form( evaluation.set_ue_poids(ue, tf[2][f"poids_{ue.id}"]) db.session.add(evaluation) db.session.commit() + sco_cache.invalidate_formsemestre(evaluation.moduleimpl.formsemestre.id) return flask.redirect(dest_url) diff --git a/app/static/css/edt.css b/app/static/css/edt.css index b0990d8f1..135be6530 100644 --- a/app/static/css/edt.css +++ b/app/static/css/edt.css @@ -23,5 +23,12 @@ height: calc(100% - 44px) !important; } .toastui-calendar-week-view-day-names, .toastui-calendar-time { - overflow: hidden !important; + overflow: hidden !important; +} + +.ic-arrow-line-left { + background: url('../icons/ic-arrow-line-left.png') no-repeat; +} +.ic-arrow-line-right { + background: url('../icons/ic-arrow-line-right.png') no-repeat; } diff --git a/app/templates/formsemestre/edt.j2 b/app/templates/formsemestre/edt.j2 index d5bb1debf..d8c92eb48 100644 --- a/app/templates/formsemestre/edt.j2 +++ b/app/templates/formsemestre/edt.j2 @@ -13,6 +13,19 @@

Expérimental: emploi du temps

+
+ + + + + + +
From fbc501bf3c87fb3439331f1ed4062aff369b93fb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Nov 2023 23:16:25 +0100 Subject: [PATCH 10/17] EDT: navigation prev/next week --- app/static/css/edt.css | 64 +++++++++++++++++--------- app/templates/formsemestre/edt.j2 | 75 ++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 41 deletions(-) diff --git a/app/static/css/edt.css b/app/static/css/edt.css index 135be6530..f97c91e85 100644 --- a/app/static/css/edt.css +++ b/app/static/css/edt.css @@ -1,34 +1,58 @@ - .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; + 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); + color: rgb(25, 113, 25); } .group-edt { - color: red; - background-color: yellow; + color: red; + background-color: yellow; } +#renderRange { + margin-left: 16px; +} .toastui-calendar-timegrid { - height: 100% !important; - min-height: auto !important; + height: 100% !important; + min-height: auto !important; } -.toastui-calendar-time{ - height: calc(100% - 44px) !important; +.toastui-calendar-time { + height: calc(100% - 44px) !important; } -.toastui-calendar-week-view-day-names, .toastui-calendar-time { - overflow: hidden !important; +.toastui-calendar-week-view-day-names, +.toastui-calendar-time { + overflow: hidden !important; } -.ic-arrow-line-left { - background: url('../icons/ic-arrow-line-left.png') no-repeat; +.btn { + border-radius: 25px; + border-color: #ddd; } -.ic-arrow-line-right { - background: url('../icons/ic-arrow-line-right.png') no-repeat; + +.btn:hover { + border: solid 1px #bbb; + background-color: #fff; +} + +.btn:active { + background-color: #f9f9f9; + border: solid 1px #bbb; + outline: none; +} + +.btn:disabled { + background-color: #f9f9f9; + border: solid 1px #ddd; + color: #bbb; +} + +.btn:focus:active, +.btn:focus, +.btn:active { + outline: none; } diff --git a/app/templates/formsemestre/edt.j2 b/app/templates/formsemestre/edt.j2 index d8c92eb48..267d05e81 100644 --- a/app/templates/formsemestre/edt.j2 +++ b/app/templates/formsemestre/edt.j2 @@ -18,10 +18,10 @@ @@ -41,7 +41,12 @@ let hm_formatter = new Intl.DateTimeFormat('default', { hour12: false }); +function getDataAction(target) { + return target.dataset ? target.dataset.action : target.getAttribute('data-action'); +} + document.addEventListener('DOMContentLoaded', function() { + document.getElementById('menu-navi').addEventListener('click', onClickNavi); const Calendar = tui.Calendar; const container = document.getElementById('calendar'); const options = { @@ -86,25 +91,7 @@ document.addEventListener('DOMContentLoaded', function() { }; const calendar = new Calendar(container, options); - //let events = [ - // { - // id: "12456", - // start:"2023-11-10T09:30", - // end:"2023-11-10T11:30", - // backgroundColor:"lightblue", - // color: "red", // couleur du texte - // location: "quelque part", - // title:'Essai saisir', - // }, - // { - // id: "12457", - // start:"2023-11-10T09:30", - // end:"2023-11-10T11:50", - // backgroundColor:"lightgreen", - // color: "blue", // couleur du texte - // title:'TD groupe 2', - // }, - //]; + fetch(`${SCO_URL}/../api/formsemestre/{{formsemestre.id}}/edt`) .then(r=>{return r.json()}) .then(events=>{ @@ -115,6 +102,52 @@ document.addEventListener('DOMContentLoaded', function() { calendar.createEvents(events); } }); + + function formatDate(date) { + let year = date.getFullYear(); + let month = (date.getMonth() + 1).toString().padStart(2, '0'); // Months are zero-indexed in JavaScript + let day = date.getDate().toString().padStart(2, '0'); + return `${day}/${month}/${year}`; + } + + function setRenderRangeText() { + var renderRange = document.getElementById('renderRange'); + var options = calendar.getOptions(); + var viewName = calendar.getViewName(); + + var html = []; + if (viewName === 'day') { + html.push(currentCalendarDate('YYYY.MM.DD')); + } else if (viewName === 'month' && + (!options.month.visibleWeeksCount || options.month.visibleWeeksCount > 4)) { + html.push(currentCalendarDate('YYYY.MM')); + } else { + html.push(formatDate(calendar.getDateRangeStart())); + html.push(' - '); + html.push(formatDate(calendar.getDateRangeEnd())); + } + renderRange.innerHTML = html.join(''); + } + function onClickNavi(e) { + var action = getDataAction(e.target); + + switch (action) { + case 'move-prev': + calendar.prev(); + break; + case 'move-next': + calendar.next(); + break; + case 'move-today': + calendar.today(); + break; + default: + return; + } + + setRenderRangeText(); + // setSchedules(); + } }); From 96333f46974bdba03e936db13aa552c0203ac64a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Nov 2023 23:24:29 +0100 Subject: [PATCH 11/17] Serve locally xlsx-populate (no CDN) --- app/static/libjs/xlsx-populate-1.21.0.min.js | 2 ++ app/templates/scolar/partition_editor.j2 | 2 +- app/templates/scolar/students_groups_auto_assignment.j2 | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 app/static/libjs/xlsx-populate-1.21.0.min.js diff --git a/app/static/libjs/xlsx-populate-1.21.0.min.js b/app/static/libjs/xlsx-populate-1.21.0.min.js new file mode 100644 index 000000000..8a7afb033 --- /dev/null +++ b/app/static/libjs/xlsx-populate-1.21.0.min.js @@ -0,0 +1,2 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).XlsxPopulate=e()}}(function(){var define,module,exports;return function a(o,s,f){function u(t,e){if(!s[t]){if(!o[t]){var r="function"==typeof require&&require;if(!e&&r)return r(t,!0);if(c)return c(t,!0);var n=new Error("Cannot find module '"+t+"'");throw n.code="MODULE_NOT_FOUND",n}var i=s[t]={exports:{}};o[t][0].call(i.exports,function(e){return u(o[t][1][e]||e)},i,i.exports,a,o,s,f)}return s[t].exports}for(var c="function"==typeof require&&require,e=0;es.length&&(l=s.length);var d=s.slice(h+u,l+u),p=d.length%i;p&&(d=g.concat([d,g.alloc(i-p)]));var b=this._createIV(n,a,i,c),m=this._crypt(e,t,r,o,b,d);f.push(m),c++}var v=g.concat(f);if(e)v=g.concat([this._createUInt32LEBuffer(s.length,8),v]);else{var y=s.readUInt32LE(0);v=v.slice(0,y)}return v}},{key:"_createUInt32LEBuffer",value:function(e,t){var r=1c&&(s=s.slice(0,c));return s}},{key:"_createIV",value:function(e,t,r,n){"number"==typeof n&&(n=this._createUInt32LEBuffer(n));var i=this._hash(e,t,n);if(i.lengthr&&(i=i.slice(0,r));return i}}])&&i(t.prototype,r),n&&i(t,n),e}();r.exports=e}).call(this,t("buffer").Buffer)},{"./XmlBuilder":21,"./XmlParser":22,"./externals":27,"./xmlq":29,buffer:77,cfb:78,crypto:86,lodash:170}],8:[function(e,t,r){"use strict";function i(e,t){for(var r=0;r=r._nextId&&(r._nextId=t+1)})}},{key:"_init",value:function(e){e=e||{name:"Relationships",attributes:{xmlns:"http://schemas.openxmlformats.org/package/2006/relationships"},children:[]},this._node=e}}])&&i(e.prototype,r),n&&i(e,n),t}();t.exports=n},{lodash:170}],12:[function(e,t,r){"use strict";function i(e,t){for(var r=0;rt){var o=u.cloneDeep(n);o.attributes.min=t+1;for(var s=o.attributes.min;s<=o.attributes.max;s++)this._colNodes[s]=o}}else r={name:"col",attributes:{min:t,max:t},children:[]},this._colNodes[t]=r;var f=new h(this,r);return this._columns[t]=f}},{key:"definedName",value:function(){var r=this;return new b("Workbook.definedName").case("string",function(e){return r.workbook().scopedDefinedName(r,e)}).case(["string","*"],function(e,t){return r.workbook().scopedDefinedName(r,e,t),r}).handle(arguments)}},{key:"delete",value:function(){return this.workbook().deleteSheet(this),this.workbook()}},{key:"find",value:function(t,r){t=f(t);var n=[];return this._rows.forEach(function(e){e&&(n=n.concat(e.find(t,r)))}),n}},{key:"gridLinesVisible",value:function(){var t=this,r=this._getOrCreateSheetViewNode();return new b("Sheet.gridLinesVisible").case(function(){return 1===r.attributes.showGridLines||void 0===r.attributes.showGridLines}).case("boolean",function(e){return r.attributes.showGridLines=e?1:0,t}).handle(arguments)}},{key:"hidden",value:function(){var r=this;return new b("Sheet.hidden").case(function(){return"hidden"===r._idNode.attributes.state||"veryHidden"===r._idNode.attributes.state&&"very"}).case("*",function(e){if(e){var t=u.filter(r.workbook().sheets(),function(e){return!e.hidden()});if(1===t.length&&t[0]===r)throw new Error("This sheet may not be hidden as a workbook must contain at least one visible sheet.");if(r.active())t[t[0]===r?1:0].active(!0)}return"very"===e?r._idNode.attributes.state="veryHidden":e?r._idNode.attributes.state="hidden":delete r._idNode.attributes.state,r}).handle(arguments)}},{key:"move",value:function(e){return this.workbook().moveSheet(this,e),this}},{key:"name",value:function(){var t=this;return new b("Sheet.name").case(function(){return"".concat(t._idNode.attributes.name)}).case("string",function(e){return t._idNode.attributes.name=e,t}).handle(arguments)}},{key:"range",value:function(){var i=this;return new b("Sheet.range").case("string",function(e){var t=p.fromAddress(e);if("range"!==t.type)throw new Error("Sheet.range: Invalid address");return i.range(t.startRowNumber,t.startColumnNumber,t.endRowNumber,t.endColumnNumber)}).case(["*","*"],function(e,t){return"string"==typeof e&&(e=i.cell(e)),"string"==typeof t&&(t=i.cell(t)),new o(e,t)}).case(["number","*","number","*"],function(e,t,r,n){return i.range(i.cell(e,t),i.cell(r,n))}).handle(arguments)}},{key:"autoFilter",value:function(e){return this._autoFilter=e,this}},{key:"row",value:function(e){if(e<1)throw new RangeError("Invalid row number ".concat(e,". Remember that spreadsheets use 1-based indexing."));if(this._rows[e])return this._rows[e];var t=new c(this,{name:"row",attributes:{r:e},children:[]});return this._rows[e]=t}},{key:"tabColor",value:function(){var r=this;return new b("Sheet.tabColor").case(function(){var e=d.findChild(r._sheetPrNode,"tabColor");if(e){var t={};return e.attributes.hasOwnProperty("rgb")?t.rgb=e.attributes.rgb:e.attributes.hasOwnProperty("theme")?t.theme=e.attributes.theme:e.attributes.hasOwnProperty("indexed")&&(t.rgb=m[e.attributes.indexed]),e.attributes.hasOwnProperty("tint")&&(t.tint=e.attributes.tint),t}}).case("string",function(e){return r.tabColor({rgb:e})}).case("integer",function(e){return r.tabColor({theme:e})}).case("nil",function(){return d.removeChild(r._sheetPrNode,"tabColor"),r}).case("object",function(e){var t=d.appendChildIfNotFound(r._sheetPrNode,"tabColor");return d.setAttributes(t,{rgb:e.rgb&&e.rgb.toUpperCase(),indexed:null,theme:e.theme,tint:e.tint}),r}).handle(arguments)}},{key:"tabSelected",value:function(){var t=this,r=this._getOrCreateSheetViewNode();return new b("Sheet.tabSelected").case(function(){return 1===r.attributes.tabSelected}).case("boolean",function(e){return e?r.attributes.tabSelected=1:delete r.attributes.tabSelected,t}).handle(arguments)}},{key:"rightToLeft",value:function(){var t=this,r=this._getOrCreateSheetViewNode();return new b("Sheet.rightToLeft").case(function(){return r.attributes.rightToLeft}).case("boolean",function(e){return e?r.attributes.rightToLeft=!0:delete r.attributes.rightToLeft,t}).handle(arguments)}},{key:"usedRange",value:function(){for(var e=u.findIndex(this._rows),t=this._rows.length-1,r=0,n=0,i=0;ithis._maxSharedFormulaId&&(this._maxSharedFormulaId=e)}},{key:"printOptions",value:function(){var r=this,n=this._getCheckAttributeNameHelper("printOptions",["gridLines","gridLinesSet","headings","horizontalCentered","verticalCentered"]);return new b("Sheet.printOptions").case(["string"],function(e){return n(e),1===r._printOptionsNode.attributes[e]}).case(["string","nil"],function(e){return n(e),delete r._printOptionsNode.attributes[e],r}).case(["string","boolean"],function(e,t){return n(e),t?(r._printOptionsNode.attributes[e]=1,r):r.printOptions(e,void 0)}).handle(arguments)}},{key:"printGridLines",value:function(){var t=this;return new b("Sheet.gridLines").case(function(){return t.printOptions("gridLines")&&t.printOptions("gridLinesSet")}).case(["nil"],function(){return t.printOptions("gridLines",void 0),t.printOptions("gridLinesSet",void 0),t}).case(["boolean"],function(e){return t.printOptions("gridLines",e),t.printOptions("gridLinesSet",e),t}).handle(arguments)}},{key:"pageMargins",value:function(){var r=this;if(void 0===this.pageMarginsPreset())throw new Error("Sheet.pageMargins: preset is undefined.");var n=this._getCheckAttributeNameHelper("pageMargins",["left","right","top","bottom","header","footer"]),i=this._getCheckRangeHelper("pageMargins",0,void 0);return new b("Sheet.pageMargins").case(["string"],function(e){n(e);var t=r._pageMarginsNode.attributes[e];return void 0!==t?parseFloat(t):r._pageMarginsPresetName?parseFloat(r._pageMarginsPresets[r._pageMarginsPresetName][e]):void 0}).case(["string","nil"],function(e){return n(e),delete r._pageMarginsNode.attributes[e],r}).case(["string","number"],function(e,t){return n(e),i(t),r._pageMarginsNode.attributes[e]=t,r}).case(["string","string"],function(e,t){return r.pageMargins(e,parseFloat(t))}).handle(arguments)}},{key:"pageMarginsPreset",value:function(){var r=this;return new b("Sheet.pageMarginsPreset").case(function(){return r._pageMarginsPresetName}).case(["nil"],function(){return r._pageMarginsPresetName=void 0,r._pageMarginsNode.attributes={},r}).case(["string"],function(e){return r._getCheckAttributeNameHelper("pageMarginsPreset",Object.keys(r._pageMarginsPresets))(e),r._pageMarginsPresetName=e,r._pageMarginsNode.attributes={},r}).case(["string","object"],function(e,t){if(r._pageMarginsPresets.hasOwnProperty(e))throw new Error("Sheet.pageMarginsPreset: The preset ".concat(e," already exists!"));if(!1===u.isEqual(u.sortBy(["left","right","top","bottom","header","footer"]),u.sortBy(Object.keys(t))))throw new Error('Sheet.pageMarginsPreset: Invalid preset attributes for one or key(s)! - "'.concat(Object.keys(t),'"'));return u.forEach(function(e,t){var r=parseFloat(e);if(u.isNaN(r)||!1===u.isNumber(r))throw new Error('Sheet.pageMarginsPreset: Invalid preset attribute value! - "'.concat(e,'"'))}),r._pageMarginsPresetName=e,r._pageMarginsNode.attributes={},r._pageMarginsPresets[e]=t,r}).handle(arguments)}},{key:"panes",value:function(){var r=this,n=this._getCheckAttributeNameHelper("pane.state",["split","frozen","frozenSplit"]),i=this._getCheckAttributeNameHelper("pane.activePane",["bottomLeft","bottomRight","topLeft","topRight"]),a=this._getOrCreateSheetViewNode(),o=d.findChild(a,"pane");return new b("Sheet.pane").case(function(){if(o){var e=u.cloneDeep(o.attributes);return e.state||(e.state="split"),e}}).case(["nil"],function(){return d.removeChild(a,"pane"),r}).case(["object"],function(e){var t=u.assign({activePane:"bottomRight"},e);return n(t.state),i(t.activePane),o?o.attributes=t:(o={name:"pane",attributes:t,children:[]},d.appendChild(a,o)),r}).handle(arguments)}},{key:"freezePanes",value:function(){var a=this;return new b("Sheet.feezePanes").case(["integer","integer"],function(e,t){var r=p.columnNumberToName(e+1)+(t+1),n=0===e?"bottomLeft":"bottomRight";return n=0===t?"topRight":n,a.panes({state:"frozen",topLeftCell:r,xSplit:e,ySplit:t,activePane:n})}).case(["string"],function(e){var t=p.fromAddress(e),r=t.columnNumber-1,n=t.rowNumber-1,i=0==r?"bottomLeft":"bottomRight";return i=0==n?"topRight":i,a.panes({state:"frozen",topLeftCell:e,xSplit:r,ySplit:n,activePane:i})}).handle(arguments)}},{key:"splitPanes",value:function(e,t){return this.panes({state:"split",xSplit:e,ySplit:t})}},{key:"resetPanes",value:function(){return this.panes(null)}},{key:"_getCheckAttributeNameHelper",value:function(t,r){return function(e){if(!u.includes(r,e))throw new Error("Sheet.".concat(t,': "').concat(e,'" is not supported.'))}}},{key:"_getCheckTypeHelper",value:function(t,r){return function(e){if(n(e)!==r)throw new TypeError("Sheet.".concat(t,": invalid type - value must be of type ").concat(r,"."))}}},{key:"_getCheckRangeHelper",value:function(t,r,n){var i=this._getCheckTypeHelper(t,"number");return function(e){if(i(e),void 0!==r&&e=n._nextNumFormatId&&(n._nextNumFormatId=t+1)})}},{key:"_init",value:function(e){this._node=e,this._numFmtsNode=c.findChild(this._node,"numFmts"),this._fontsNode=c.findChild(this._node,"fonts"),this._fillsNode=c.findChild(this._node,"fills"),this._bordersNode=c.findChild(this._node,"borders"),this._cellXfsNode=c.findChild(this._node,"cellXfs"),this._numFmtsNode||(this._numFmtsNode={name:"numFmts",attributes:{},children:[]},c.insertBefore(this._node,this._numFmtsNode,this._fontsNode)),delete this._numFmtsNode.attributes.count,delete this._fontsNode.attributes.count,delete this._fillsNode.attributes.count,delete this._bordersNode.attributes.count,delete this._cellXfsNode.attributes.count}}])&&i(e.prototype,r),n&&i(e,n),t}();t.exports=n},{"./Style":17,"./xmlq":29,lodash:170}],19:[function(M,T,e){(function(i,a){"use strict";function o(e){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function s(e,t){for(var r=0;r
diff --git a/app/templates/assiduites/pages/visu_assi.j2 b/app/templates/assiduites/pages/visu_assi.j2 index f947928f3..71585f8d7 100644 --- a/app/templates/assiduites/pages/visu_assi.j2 +++ b/app/templates/assiduites/pages/visu_assi.j2 @@ -1,8 +1,8 @@ {% extends "sco_page.j2" %} {% block scripts %} - {{ super() }} - +{{ super() }} + {% endblock %} {% block app_content %} @@ -10,10 +10,10 @@

Visualisation de l'assiduité {{gr_tit|safe}}

- - + + {{scu.ICON_XLS|safe}} @@ -21,8 +21,8 @@ {{tableau | safe}} -
-Les comptes sont exprimés en {{ assi_metric | lower}}s. +
+ Les comptes sont exprimés en {{ assi_metric | lower}}s.
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/templates/assiduites/widgets/conflict.j2 b/app/templates/assiduites/widgets/conflict.j2 index 57a74da3d..73709d9b7 100644 --- a/app/templates/assiduites/widgets/conflict.j2 +++ b/app/templates/assiduites/widgets/conflict.j2 @@ -258,7 +258,7 @@ const success = () => { const separatorTime = document.getElementById("promptTime").value; const dateString = - document.querySelector("#tl_date").value + `T${separatorTime}`; + getDate().format("YYYY-MM-DD") + `T${separatorTime}`; const separtorDate = new Date(dateString); const assiduite_debut = new Date(this.selectedAssiduite.date_debut); diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2 index 3fd5f5d67..36d03a3d6 100644 --- a/app/templates/assiduites/widgets/minitimeline.j2 +++ b/app/templates/assiduites/widgets/minitimeline.j2 @@ -13,7 +13,8 @@ */ function createMiniTimeline(assiduitesArray, day = null) { const array = [...assiduitesArray]; - const dateiso = day == null ? document.getElementById("tl_date").value : day; + + const dateiso = day == null ? getDate().format("YYYY-MM-DD") : day; const timeline = document.createElement("div"); timeline.className = "mini-timeline"; if (isSingleEtud()) { diff --git a/app/templates/assiduites/widgets/moduleimpl_dynamic_selector.j2 b/app/templates/assiduites/widgets/moduleimpl_dynamic_selector.j2 index 1a5c5a2d5..b82f655fc 100644 --- a/app/templates/assiduites/widgets/moduleimpl_dynamic_selector.j2 +++ b/app/templates/assiduites/widgets/moduleimpl_dynamic_selector.j2 @@ -93,9 +93,10 @@ function updateSelect(moduleimpl_id, query = "#moduleimpl_select", dateIso = null) { let sem = getEtudFormSemestres() - if (dateIso == null) { - dateIso = document.querySelector("#tl_date").value + if (!dateIso) { + dateIso = getDate().format("YYYY-MM-DD") } + sem = filterFormSemestres(sem, dateIso) const mod = getModulesImplByFormsemestre(sem) populateSelect(mod, moduleimpl_id, query); diff --git a/app/views/assiduites.py b/app/views/assiduites.py index ab2feec05..65e9735fe 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -25,6 +25,7 @@ ############################################################################## import datetime +import re from flask import g, request, render_template, flash from flask import abort, url_for, redirect @@ -319,7 +320,7 @@ def signal_assiduites_etud(): render_template( "assiduites/pages/signal_assiduites_etud.j2", sco=ScoData(etud), - date=date, + date=_dateiso_to_datefr(date), morning=morning, lunch=lunch, timeline=_timeline(heures=",".join([f"'{s}'" for s in heures])), @@ -425,8 +426,8 @@ def bilan_etud(): ) # Gestion des dates du bilan (par défaut l'année scolaire) - date_debut: str = f"{scu.annee_scolaire()}-09-01" - date_fin: str = f"{scu.annee_scolaire()+1}-06-30" + date_debut: str = f"01/09/{scu.annee_scolaire()}" + date_fin: str = f"30/06/{scu.annee_scolaire()+1}" # Récupération de la métrique d'assiduité assi_metric = scu.translate_assiduites_metric( @@ -697,7 +698,7 @@ def signal_assiduites_group(): "assiduites/pages/signal_assiduites_group.j2", gr_tit=gr_tit, sem=sem["titre_num"], - date=date, + date=_dateiso_to_datefr(date), formsemestre_id=formsemestre_id, grp=sco_groups_view.menu_groups_choice(groups_infos), moduleimpl_select=_module_selector(formsemestre, moduleimpl_id), @@ -843,7 +844,7 @@ def visu_assiduites_group(): "assiduites/pages/signal_assiduites_group.j2", gr_tit=gr_tit, sem=sem["titre_num"], - date=date, + date=_dateiso_to_datefr(date), formsemestre_id=formsemestre_id, grp=sco_groups_view.menu_groups_choice(groups_infos), moduleimpl_select=_module_selector(formsemestre, moduleimpl_id), @@ -1015,8 +1016,8 @@ def visu_assi_group(): inverse=False, short=False, ), - date_debut=dates["debut"], - date_fin=dates["fin"], + date_debut=_dateiso_to_datefr(dates["debut"]), + date_fin=_dateiso_to_datefr(dates["debut"]), gr_tit=gr_tit, group_ids=request.args.get("group_ids", None), sco=ScoData(formsemestre=groups_infos.get_formsemestre()), @@ -1318,6 +1319,31 @@ def test(): # --- Fonctions internes --- +def _dateiso_to_datefr(date_iso: str) -> str: + """ + _dateiso_to_datefr Transforme une date iso en date format français + + Args: + date_iso (str): date au format iso (YYYY-MM-DD) + + Raises: + ValueError: Si l'argument `date_iso` n'est pas au bon format + + Returns: + str: date au format français (DD/MM/YYYY) + """ + + regex_date_iso: str = r"^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$" + + # Vérification de la date_iso + if not re.match(regex_date_iso, date_iso): + raise ValueError( + f"La dateiso passée en paramètre [{date_iso}] n'est pas valide." + ) + + return f"{date_iso[8:10]}/{date_iso[5:7]}/{date_iso[0:4]}" + + def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str: """ _get_date_str transforme une période en chaîne lisible From 7502acf1dfb63f36156b3e57ec1fee3d101705ac Mon Sep 17 00:00:00 2001 From: Iziram Date: Wed, 15 Nov 2023 17:34:13 +0100 Subject: [PATCH 14/17] Assiduites : maj calendrier --- app/static/js/assiduites.js | 10 +- app/templates/assiduites/pages/bilan_etud.j2 | 2 + app/templates/assiduites/pages/calendrier.j2 | 295 +++++++++++++++++-- app/views/assiduites.py | 10 +- 4 files changed, 281 insertions(+), 36 deletions(-) diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index a58592cc2..fe9ee9d46 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -201,7 +201,8 @@ async function async_get(path, success, errors) { throw new Error("Network response was not ok."); } } catch (error) { - errors(error); + console.error(error); + if (errors) errors(error); } } @@ -249,7 +250,8 @@ async function async_post(path, data, success, errors) { throw new Error("Network response was not ok."); } } catch (error) { - errors(error); + console.error(error); + if (errors) errors(error); } } @@ -1162,7 +1164,7 @@ function getAllAssiduitesFromEtud( .replace("°", courant ? "&courant" : "") : "" }`; - + //TODO Utiliser async_get au lieu de jquery $.ajax({ async: true, type: "GET", @@ -1744,6 +1746,8 @@ function getAllJustificatifsFromEtud( `/api/justificatifs/${etudid}${ order ? "/query?order°".replace("°", courant ? "&courant" : "") : "" }`; + + //TODO Utiliser async_get au lieu de jquery $.ajax({ async: true, type: "GET", diff --git a/app/templates/assiduites/pages/bilan_etud.j2 b/app/templates/assiduites/pages/bilan_etud.j2 index a1c3d0df4..77712bb1e 100644 --- a/app/templates/assiduites/pages/bilan_etud.j2 +++ b/app/templates/assiduites/pages/bilan_etud.j2 @@ -107,6 +107,7 @@ function getAssiduitesCount(dateDeb, dateFin, query) { const url_api = getUrl() + `/api/assiduites/${etudid}/count/query?date_debut=${dateDeb}&date_fin=${dateFin}&${query}`; + //Utiliser async_get au lieu de Jquery return $.ajax({ async: true, type: "GET", @@ -120,6 +121,7 @@ } function countAssiduites(dateDeb, dateFin) { + //TODO Utiliser Fetch when plutot que jquery $.when( getAssiduitesCount(dateDeb, dateFin, `etat=present`), getAssiduitesCount(dateDeb, dateFin, `etat=retard`), diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index a6b270d9d..3b94e1bef 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -4,13 +4,23 @@
{{minitimeline | safe }}

Assiduité de {{sco.etud.nomprenom}}

+ +
+ + + +
+
- Année scolaire 2022-2023 Changer année: + Année scolaire 2022-2023Changer + année: + + Assiduité de {{sco.etud.nomprenom}}
@@ -34,7 +44,37 @@
+ + -{% endblock pageContent %} \ No newline at end of file +{% endblock pageContent %} From f485791e3bba6469da117269d8b560b63c16b772 Mon Sep 17 00:00:00 2001 From: Iziram Date: Thu, 16 Nov 2023 14:25:06 +0100 Subject: [PATCH 17/17] Assiduites : Gestions couleurs + bugfix calendrier Close #800 --- app/static/css/assiduites.css | 70 +++- app/static/icons/absent.svg | 16 +- app/static/icons/absent_ancien.svg | 11 + app/static/icons/aucun.svg | 2 +- app/static/icons/present.svg | 4 +- app/static/icons/retard.svg | 4 +- app/static/js/assiduites.js | 2 +- app/static/js/date_utils.js | 11 + .../assiduites/pages/ajout_justificatif.j2 | 2 +- app/templates/assiduites/pages/calendrier.j2 | 303 ++++++++++++------ .../pages/signal_assiduites_etud.j2 | 13 +- .../pages/signal_assiduites_group.j2 | 12 +- app/templates/assiduites/widgets/alert.j2 | 2 +- app/templates/assiduites/widgets/conflict.j2 | 16 +- app/templates/assiduites/widgets/differee.j2 | 22 +- .../assiduites/widgets/legende_couleur.j2 | 12 + .../assiduites/widgets/minitimeline.j2 | 18 +- app/templates/assiduites/widgets/prompt.j2 | 2 +- .../assiduites/widgets/tableau_assi.j2 | 6 +- .../assiduites/widgets/tableau_base.j2 | 16 +- .../assiduites/widgets/tableau_justi.j2 | 6 +- app/templates/assiduites/widgets/timeline.j2 | 4 +- app/templates/assiduites/widgets/toast.j2 | 8 +- 23 files changed, 357 insertions(+), 205 deletions(-) mode change 100755 => 100644 app/static/icons/absent.svg create mode 100755 app/static/icons/absent_ancien.svg create mode 100644 app/templates/assiduites/widgets/legende_couleur.j2 diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index c935f4f83..fe429ecfc 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -1,3 +1,33 @@ +:root { + --color-present: #6bdb83; + --color-absent: #e62a11; + --color-retard: #f0c865; + --color-justi: #7059FF; + --color-justi-invalide: #a84476; + --color-nonwork: #badfff; + + --color-absent-justi: #e65ab7; + --color-retard-justi: #ffef7a; + + --color-error: #FF0000; + --color-warning: #eec660; + --color-information: #658ef0; + + --color-def: #d61616; + --color-conflit: #ff00009c; + --color-bg-def: #c8c8c8; + --color-primary: #7059FF; + --color-secondary: #6f9fff; + + --color-defaut: #FFF; + --color-defaut-dark: #444; + + + + --motif-justi: repeating-linear-gradient(135deg, transparent, transparent 4px, var(--color-justi) 4px, var(--color-justi) 8px); + --motif-justi-invalide: repeating-linear-gradient(-135deg, transparent, transparent 4px, var(--color-justi-invalide) 4px, var(--color-justi-invalide) 8px); +} + * { box-sizing: border-box; } @@ -87,7 +117,7 @@ } .ui-slider-range.ui-widget-header.ui-corner-all { - background-color: #F9C768; + background-color: var(--color-warning); background-image: none; opacity: 0.50; visibility: visible; @@ -122,7 +152,7 @@ .etud_row.def, .etud_row.dem { - background-color: #c8c8c8; + background-color: var(--color-bg-def); } /* --- Index --- */ @@ -149,7 +179,7 @@ .tr.def .td.sticky span::after { display: block; content: " (Déf.)"; - color: #d61616; + color: var(--color-def); margin-left: 2px; } @@ -157,7 +187,7 @@ .tr.dem .td.sticky span::after { display: block; content: " (Dém.)"; - color: #d61616; + color: var(--color-def); margin-left: 2px; } @@ -213,32 +243,36 @@ } .etud_row.conflit { - background-color: #ff0000c2; + background-color: var(--color-conflit); } .etud_row .assiduites_bar .absent, .demo.absent { - background-color: #F1A69C !important; + background-color: var(--color-absent) !important; } .etud_row .assiduites_bar .present, .demo.present { - background-color: #9CF1AF !important; + background-color: var(--color-present) !important; } .etud_row .assiduites_bar .retard, .demo.retard { - background-color: #F1D99C !important; + background-color: var(--color-retard) !important; +} + +.demo.nonwork { + background-color: var(--color-nonwork) !important; } .etud_row .assiduites_bar .justified, .demo.justified { - background-image: repeating-linear-gradient(135deg, transparent, transparent 4px, #7059FF 4px, #7059FF 8px); + background-image: var(--motif-justi); } .etud_row .assiduites_bar .invalid_justified, .demo.invalid_justified { - background-image: repeating-linear-gradient(225deg, transparent, transparent 4px, #d61616 4px, #d61616 8px); + background-image: var(--motif-justi-invalide); } @@ -273,27 +307,35 @@ height: 35px; background-position: center; background-size: cover; + border-radius: 5px; + border: 1px solid var(--color-defaut-dark); } .rbtn.present::before { background-image: url(../icons/present.svg); + background-color: var(--color-present); + } .rbtn.absent::before { + background-color: var(--color-absent); background-image: url(../icons/absent.svg); } .rbtn.aucun::before { background-image: url(../icons/aucun.svg); + background-color: var(--color-defaut-dark); + } .rbtn.retard::before { + background-color: var(--color-retard); background-image: url(../icons/retard.svg); } .rbtn:checked:before { - outline: 5px solid #7059FF; + outline: 5px solid var(--color-primary); border-radius: 50%; } @@ -486,7 +528,7 @@ .loader { border: 6px solid #f3f3f3; border-radius: 50%; - border-top: 6px solid #3498db; + border-top: 6px solid var(--color-primary); width: 60px; height: 60px; position: absolute; @@ -532,7 +574,7 @@ } .rouge { - color: crimson; + color: var(--color-error); } .legende { @@ -588,7 +630,7 @@ #forcemodule { border-radius: 8px; - background: crimson; + background: var(--color-error); max-width: fit-content; padding: 5px; color: white; diff --git a/app/static/icons/absent.svg b/app/static/icons/absent.svg old mode 100755 new mode 100644 index 697635cd9..5c6385bf5 --- a/app/static/icons/absent.svg +++ b/app/static/icons/absent.svg @@ -1,11 +1,9 @@ - - - - + + + + + + + - - - - - diff --git a/app/static/icons/absent_ancien.svg b/app/static/icons/absent_ancien.svg new file mode 100755 index 000000000..cb82811c4 --- /dev/null +++ b/app/static/icons/absent_ancien.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/static/icons/aucun.svg b/app/static/icons/aucun.svg index eaff20043..c23efffc7 100755 --- a/app/static/icons/aucun.svg +++ b/app/static/icons/aucun.svg @@ -1,5 +1,5 @@ - + diff --git a/app/static/icons/present.svg b/app/static/icons/present.svg index e1628c836..4a649b1ca 100755 --- a/app/static/icons/present.svg +++ b/app/static/icons/present.svg @@ -1,7 +1,7 @@ - + - + diff --git a/app/static/icons/retard.svg b/app/static/icons/retard.svg index b8a7f3d25..b41ef3e74 100755 --- a/app/static/icons/retard.svg +++ b/app/static/icons/retard.svg @@ -1,6 +1,6 @@ - - + + diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index fe9ee9d46..caac0c69f 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1696,7 +1696,7 @@ function fastJustify(assiduite) { content, success, () => {}, - "#7059FF" + "var(--color-primary)" ); }; if (assiduite.etudid) { diff --git a/app/static/js/date_utils.js b/app/static/js/date_utils.js index 4a1d21d98..ee9ed1503 100644 --- a/app/static/js/date_utils.js +++ b/app/static/js/date_utils.js @@ -48,6 +48,17 @@ Date.toFRA = function (dateIso) { 7 )}/${dateIso.substring(0, 4)}`; }; +/** + * Vérifie si le début de l'une des périodes est avant la fin de l'autre + * et si la fin de cette période est après le début de l'autre. + * @param {Object} period {deb:Object, fin:Object} + * @param {Object} interval {deb:Object, fin:Object} + * @returns vrai si la periode et l'interval ont une intersection commune + */ +Date.intersect = function (period, interval) { + return period.deb <= interval.fin && period.fin >= interval.deb; +}; + Object.defineProperty(Date.prototype, "isValid", { value: function () { return !Number.isNaN(this.getTime()); diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index 0935ed60b..445379ca4 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -152,7 +152,7 @@ const requests = [] Array.from(in_files.files).forEach((f) => { - pushToast(generateToast(document.createTextNode(`Importation du fichier : ${f.name} commencée`), color = "#f0c865")); + pushToast(generateToast(document.createTextNode(`Importation du fichier : ${f.name} commencée`), color = "var(--color-information)")); const fd = new FormData(); fd.append('file', f); requests.push( diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index 3c6c091d3..c3dcf3e22 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -8,7 +8,7 @@
- +
@@ -25,54 +25,98 @@

Calendrier

-

Les jours non travaillés sont affiché en violet

-

Les jours possèdant une bordure "bleu" sont des jours où des absences/retards ont été justifiées par un - justificatif valide

-

Les jours possèdant une bordure "rouge" sont des jours où des absences/retards ont été justifiées par un - justificatif non valide

-

Le jour sera affiché en :

-
    -
  • Rouge : s'il y a une absence enregistrée
  • -
  • Orange : s'il y a un retard et pas d'absence
  • -
  • Vert : s'il y a une présence enregistrée mais pas d'absence ni de retard
  • -
  • Blanc : s'il n'y a rien d'enregistré
  • +

    Code couleur

    +
      +
    • → présence de l'étudiant lors de la période +
    • +
    • → la période n'est pas travaillée +
    • +
    • → absence de l'étudiant lors de la période +
    • +
    • → absence justifiée +
    • +
    • → retard de l'étudiant lors de la période +
    • +
    • → retard justifié +
    • + +
    • → la période est justifiée par un + justificatif valide
    • +
    • → la période est + justifiée par un justificatif non valide / en attente de validation +
    -

    Vous pouvez passer le curseur sur les jours colorés afin de voir les informations de cette journée.

    +

    Vous pouvez passer le curseur sur les jours colorés afin de voir les informations supplémentaires

+
    +
  • présence +
  • +
  • non travaillé +
  • +
  • absence +
  • +
  • absence justifiée +
  • +
  • retard +
  • +
  • retard justifié +
  • +
  • + justificatif valide
  • +
  • justificatif non valide +
  • +
- - - @@ -310,28 +405,6 @@ return assiduitiesByDay; } - function getDayColor(etat) { - let color; - switch (etat.toUpperCase()) { - case "PRESENT": - color = ""; - break; - case "ABSENT": - color = "#F1A69C"; - break; - case "RETARD": - color = "#f0c865"; - break; - case "NONWORK": - color = "#bd81ca" - break; - default: - color = "#FFF"; - break; - } - return color; - } - function generateCalendar(assiduitiesByDay, nonWorkdays = []) { const calendar = document.querySelector('.calendrier') const options = getOptions(); @@ -360,7 +433,7 @@ Object.keys(assiduitiesByDay[month]).forEach((date) => { let dayAssiduities = assiduitiesByDay[month][date].assiduites; let dayJustificatifs = assiduitiesByDay[month][date].justificatifs; - let color = ""; + let color = "sans_etat"; if (dayAssiduities.some((a) => a.etat.toLowerCase() === "absent")) color = "absent"; else if (dayAssiduities.some((a) => a.etat.toLowerCase() === "retard") && options.show_reta) @@ -373,45 +446,49 @@ if (dayJustificatifs.some((j) => j.etat.toLowerCase() === "valide")) { est_just = "est_just"; } else if (dayJustificatifs.some((j) => j.etat.toLowerCase() !== "valide")) { - est_just = "est_just invalide"; + est_just = "invalide"; } const momentDate = new Date(date); let dayOfMonth = momentDate.getDate(); let dayOfWeek = momentDate.getDay(); dayOfWeek = days[dayOfWeek]; - let isWorkDay = nonWorkdays.includes(dayOfWeek.toLowerCase()); + let isNonWorkDay = nonWorkdays.includes(dayOfWeek.toLowerCase()); const day = document.createElement('div'); day.className = `day`; - if (isWorkDay) { + if (isNonWorkDay) { color = "nonwork"; } else if (!options.mode_demi) { day.className = `day ${est_just}`; } - if (options.mode_demi && !isWorkDay) { + + if (options.mode_demi && !isNonWorkDay) { + est_just = [] // affichage n° jour + matin + aprem const span_jour = document.createElement("span") - span_jour.textContent = dayOfMonth + dayOfWeek[0]; + span_jour.textContent = dayOfWeek[0] + dayOfMonth; const span_matin = document.createElement("span"); span_matin.classList.add("color"); const matin = [new Date(date), new Date(date)] - color = "" + color = "sans_etat" matin[0].setHours(0, 0, 0, 0) matin[1].setHours(12, 59, 59) + + const assiduitesMatin = dayAssiduities.filter((el) => { const deb = new Date(el.date_debut); const fin = new Date(el.date_fin); - return deb.isBetween(matin[0], matin[1]) || fin.isBetween(matin[0], matin[1]) + return Date.intersect({ deb: deb, fin: fin }, { deb: matin[0], fin: matin[1] }) }) const justificatifsMatin = dayJustificatifs.filter((el) => { const deb = new Date(el.date_debut); const fin = new Date(el.date_fin); - return deb.isBetween(matin[0], matin[1]) || fin.isBetween(matin[0], matin[1]) + return Date.intersect({ deb: deb, fin: fin }, { deb: matin[0], fin: matin[1] }) }) if (assiduitesMatin.some((a) => a.etat.toLowerCase() === "absent")) color = "absent"; @@ -427,7 +504,7 @@ if (justificatifsMatin.some((j) => j.etat.toLowerCase() === "valide")) { est_just = ["est_just"]; } else if (justificatifsMatin.some((j) => j.etat.toLowerCase() !== "valide")) { - est_just = ["est_just", "invalide"]; + est_just = ["invalide"]; } span_matin.classList.add(...est_just) @@ -437,20 +514,21 @@ const span_aprem = document.createElement("span"); span_aprem.classList.add("color"); const aprem = [new Date(date), new Date(date)] - color = "" + color = "sans_etat" aprem[0].setHours(13, 0, 0, 0) aprem[1].setHours(23, 59, 59) + const assiduitesAprem = dayAssiduities.filter((el) => { const deb = new Date(el.date_debut); const fin = new Date(el.date_fin); - return deb.isBetween(aprem[0], aprem[1]) || fin.isBetween(aprem[0], aprem[1]) + return Date.intersect({ deb: deb, fin: fin }, { deb: aprem[0], fin: aprem[1] }) }) const justificatifsAprem = dayJustificatifs.filter((el) => { const deb = new Date(el.date_debut); const fin = new Date(el.date_fin); - return deb.isBetween(aprem[0], aprem[1]) || fin.isBetween(aprem[0], aprem[1]) + return Date.intersect({ deb: deb, fin: fin }, { deb: aprem[0], fin: aprem[1] }) }) if (assiduitesAprem.some((a) => a.etat.toLowerCase() === "absent")) color = "absent"; @@ -464,34 +542,53 @@ } if (justificatifsAprem.some((j) => j.etat.toLowerCase() === "valide")) { - est_just = ["est_just"];; + est_just = ["est_just"]; } else if (justificatifsAprem.some((j) => j.etat.toLowerCase() !== "valide")) { - est_just = ["est_just", "invalide"];; + est_just = ["invalide"]; } - span_matin.classList.add(...est_just) + span_aprem.classList.add(...est_just) day.appendChild(span_jour) day.appendChild(span_matin) day.appendChild(span_aprem) + } else { day.classList.add("color") if (color != "") { day.classList.add(color); } - day.textContent = `${dayOfWeek} ${dayOfMonth}`; + + if (isNonWorkDay) { + const span_jour = document.createElement("span") + span_jour.textContent = dayOfWeek[0] + dayOfMonth; + day.appendChild(span_jour); + } else { + day.textContent = `${dayOfWeek} ${dayOfMonth}`; + } + + } + console.warn(day.classList, day.classList.length) + if (!nonWorkdays.includes(dayOfWeek.toLowerCase()) && dayAssiduities.length > 0) { const cache = document.createElement('div') cache.classList.add('dayline'); + const title = document.createElement('div') + title.className = "dayline-title"; + + title.innerHTML = "Assiduité du
" + `${formatDate(momentDate)}`; + cache.appendChild(title) cache.appendChild( createMiniTimeline(dayAssiduities, date) ) day.appendChild(cache) } + + daysEl.appendChild(day); }); monthEl.appendChild(daysEl) @@ -586,4 +683,4 @@ function isCalendrier() { return true } -{% endblock pageContent %} +{% endblock pageContent %} \ No newline at end of file diff --git a/app/templates/assiduites/pages/signal_assiduites_etud.j2 b/app/templates/assiduites/pages/signal_assiduites_etud.j2 index f611c3cbe..0fa0a383d 100644 --- a/app/templates/assiduites/pages/signal_assiduites_etud.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_etud.j2 @@ -59,18 +59,9 @@ Correspondance des couleurs :

    -
  • → présence de l'étudiant lors de la période -
  • -
  • → retard de l'étudiant lors de la période -
  • -
  • → absence de l'étudiant lors de la période -
  • -
  • → l'assiduité est justifiée par un - justificatif valide
  • -
  • → l'assiduité est - justifiée par un justificatif non valide / en attente de validation -
  • + {% include "assiduites/widgets/legende_couleur.j2" %}
+

Vous pouvez justifier rapidement une assiduité en saisisant l'assiduité puis en appuyant sur "Justifier"

Explication de la saisie différée

diff --git a/app/templates/assiduites/pages/signal_assiduites_group.j2 b/app/templates/assiduites/pages/signal_assiduites_group.j2 index 9ce818293..6e761c3a1 100644 --- a/app/templates/assiduites/pages/signal_assiduites_group.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_group.j2 @@ -70,17 +70,7 @@ Correspondance des couleurs :

    -
  • → présence de l'étudiant lors de la période -
  • -
  • → retard de l'étudiant lors de la période -
  • -
  • → absence de l'étudiant lors de la période -
  • -
  • → l'assiduité est justifiée par un - justificatif valide
  • -
  • → l'assiduité est - justifiée par un justificatif non valide / en attente de validation -
  • + {% include "assiduites/widgets/legende_couleur.j2" %}
diff --git a/app/templates/assiduites/widgets/alert.j2 b/app/templates/assiduites/widgets/alert.j2 index 4da10c9ea..bf1f0bfec 100644 --- a/app/templates/assiduites/widgets/alert.j2 +++ b/app/templates/assiduites/widgets/alert.j2 @@ -127,7 +127,7 @@