1
0
forked from ScoDoc/ScoDoc

Fix signal_assiduites_group: saisie si timezone client != serveur

This commit is contained in:
Emmanuel Viennet 2024-09-24 19:23:42 +02:00
parent 851f64f38a
commit 00efa7bcad
5 changed files with 36 additions and 18 deletions

View File

@ -387,6 +387,20 @@ def localize_datetime(date: datetime.datetime) -> datetime.datetime:
return new_date return new_date
def get_local_timezone_offset() -> str:
"""Récupère l'offset de la timezone du serveur, sous la forme
"+HH:MM"
"""
local_time = datetime.datetime.now().astimezone()
utc_offset = local_time.utcoffset()
total_seconds = int(utc_offset.total_seconds())
offset_hours = total_seconds // 3600
offset_minutes = (abs(total_seconds) % 3600) // 60
offset_sign = "+" if offset_hours >= 0 else "-"
offset_str = f"{offset_sign}{abs(offset_hours):02d}:{offset_minutes:02d}"
return offset_str
def is_period_overlapping( def is_period_overlapping(
periode: tuple[datetime.datetime, datetime.datetime], periode: tuple[datetime.datetime, datetime.datetime],
interval: tuple[datetime.datetime, datetime.datetime], interval: tuple[datetime.datetime, datetime.datetime],

View File

@ -223,27 +223,27 @@ function creerLigneEtudiant(etud, index) {
</div> </div>
</div> </div>
<fieldset class="btns_field single" etudid="497" type="creation" assiduite_id="-1"> <fieldset class="btns_field single" etudid="497" type="creation" assiduite_id="-1">
<input <input
type="checkbox" type="checkbox"
value="present" value="present"
name="btn_assiduites_1" name="btn_assiduites_1"
id="rbtn_present" id="rbtn_present"
class="rbtn present" class="rbtn present"
title="present" title="present"
> >
<input <input
type="checkbox" type="checkbox"
value="retard" value="retard"
name="btn_assiduites_1" name="btn_assiduites_1"
id="rbtn_retard" id="rbtn_retard"
class="rbtn retard" class="rbtn retard"
title="retard" title="retard"
> >
<input <input
type="checkbox" type="checkbox"
value="absent" value="absent"
name="btn_assiduites_1" name="btn_assiduites_1"
id="rbtn_absent" id="rbtn_absent"
class="rbtn absent" class="rbtn absent"
title="absent" title="absent"
> >
@ -609,7 +609,7 @@ async function actionAssiduite(etud, etat, type, assiduite = null) {
const modimpl_id = $("#moduleimpl_select").val(); const modimpl_id = $("#moduleimpl_select").val();
if (assiduite && assiduite.etat.toLowerCase() === etat) type = "suppression"; if (assiduite && assiduite.etat.toLowerCase() === etat) type = "suppression";
const { deb, fin } = getPeriodAsDate(); const { deb, fin } = getPeriodAsDate(true); // en tz server
// génération d'un objet assiduité basique qui sera complété // génération d'un objet assiduité basique qui sera complété
let assiduiteObjet = assiduite ?? { let assiduiteObjet = assiduite ?? {
date_debut: deb, date_debut: deb,
@ -709,7 +709,7 @@ function erreurModuleImpl(message) {
function mettreToutLeMonde(etat, el = null) { function mettreToutLeMonde(etat, el = null) {
const lignesEtuds = [...document.querySelectorAll("fieldset.btns_field")]; const lignesEtuds = [...document.querySelectorAll("fieldset.btns_field")];
const { deb, fin } = getPeriodAsDate(); const { deb, fin } = getPeriodAsDate(true); // tz server
const assiduiteObjet = { const assiduiteObjet = {
date_debut: deb, date_debut: deb,
date_fin: fin, date_fin: fin,

View File

@ -59,7 +59,7 @@
block.style.width = `${widthPercentage}%`; block.style.width = `${widthPercentage}%`;
if (assiduité.etat != "CRENEAU") { if (assiduité.etat != "CRENEAU") {
// Si on clique dessus on veut pouvoir // Si on clique dessus on veut pouvoir
// mettre à jour la timeline principale et modifier le moduleimpl_select // mettre à jour la timeline principale et modifier le moduleimpl_select
block.addEventListener("click", () => { block.addEventListener("click", () => {
let deb = startDate.getHours() + startDate.getMinutes() / 60; let deb = startDate.getHours() + startDate.getMinutes() / 60;
@ -71,7 +71,7 @@
$("#moduleimpl_select").val(getModuleImplId(assiduité)) $("#moduleimpl_select").val(getModuleImplId(assiduité))
setTimeout(()=>{ setTimeout(()=>{
$("#moduleimpl_select").trigger("change"); $("#moduleimpl_select").trigger("change");
}, 0) }, 0)
}); });
//ajouter affichage assiduites on over //ajouter affichage assiduites on over

View File

@ -12,6 +12,7 @@
</div> </div>
</div> </div>
<script> <script>
const SERVER_TIMEZONE_OFFSET = "{{ scu.get_local_timezone_offset() }}";
const timelineContainer = document.querySelector(".timeline-container"); const timelineContainer = document.querySelector(".timeline-container");
const periodTimeLine = document.querySelector(".period"); const periodTimeLine = document.querySelector(".period");
const t_start = {{ t_start }}; const t_start = {{ t_start }};
@ -21,7 +22,7 @@
const tick_time = 60 / {{ tick_time }}; const tick_time = 60 / {{ tick_time }};
const tick_delay = 1 / tick_time; const tick_delay = 1 / tick_time;
const period_default = 2; const period_default = 2; // durée créneau par défaut: 2 heures
let handleMoving = false; let handleMoving = false;
@ -264,10 +265,10 @@
// On les arrondit aux ticks les plus proches // On les arrondit aux ticks les plus proches
const startValue = snapToQuarter(startHour); const startValue = snapToQuarter(startHour);
const endValue = snapToQuarter(endHour); const endValue = snapToQuarter(endHour);
// on verifie que les valeurs sont bien dans les bornes // on verifie que les valeurs sont bien dans les bornes
const computedValues = [Math.max(startValue, t_start), Math.min(t_end, endValue)]; const computedValues = [Math.max(startValue, t_start), Math.min(t_end, endValue)];
// si les valeurs sont hors des bornes, on les ajuste // si les valeurs sont hors des bornes, on les ajuste
if (computedValues[0] > t_end || computedValues[1] < t_start) { if (computedValues[0] > t_end || computedValues[1] < t_start) {
return [t_start, Math.min(t_end, t_start + period_default)]; return [t_start, Math.min(t_end, t_start + period_default)];
@ -338,19 +339,22 @@
// Renvoie les valeurs de la période sous forme de date // Renvoie les valeurs de la période sous forme de date
// Les heures sont récupérées depuis la timeline // Les heures sont récupérées depuis la timeline
// la date est récupérée depuis un champ "#date" (datepicker) // la date est récupérée depuis un champ "#date" (datepicker)
function getPeriodAsDate(){ function getPeriodAsDate(add_server_tz = false) {
let [deb, fin] = getPeriodValues(); let [deb, fin] = getPeriodValues();
deb = numberToTime(deb); deb = numberToTime(deb);
fin = numberToTime(fin); fin = numberToTime(fin);
const dateStr = $("#date") const dateStr = $("#date")
.datepicker("getDate") .datepicker("getDate")
.format("yyyy-mm-dd") .format("yyyy-mm-dd")
.substring(0, 10); // récupération que de la date, pas des heures .substring(0, 10); // récupération que de la date, pas des heures
// Les heures deb et fin sont telles qu'affichées, c'est à dire
// en heure locale DU SERVEUR (des étudiants donc)
let offset = add_server_tz ? SERVER_TIMEZONE_OFFSET : "";
return { return {
deb: new Date(`${dateStr}T${deb}`), deb: new Date(`${dateStr}T${deb}${offset}`),
fin: new Date(`${dateStr}T${fin}`) fin: new Date(`${dateStr}T${fin}${offset}`)
} }
} }
// Sauvegarde les valeurs de la période dans le local storage // Sauvegarde les valeurs de la période dans le local storage

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.7.25" SCOVERSION = "9.7.26"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"