Merge branch 'iziram-sco_gen_cal'

This commit is contained in:
Emmanuel Viennet 2024-05-30 13:31:07 +02:00
commit e18c1d8fd0
8 changed files with 180 additions and 83 deletions

View File

@ -30,6 +30,7 @@
Lecture et conversion des ics. Lecture et conversion des ics.
""" """
from datetime import timezone from datetime import timezone
import glob import glob
import os import os
@ -229,7 +230,7 @@ def translate_calendar(
heure_deb=event["heure_deb"], heure_deb=event["heure_deb"],
heure_fin=event["heure_fin"], heure_fin=event["heure_fin"],
moduleimpl_id=modimpl.id, moduleimpl_id=modimpl.id,
jour=event["jour"], day=event["jour"],
) )
if modimpl and group if modimpl and group
else None else None

View File

@ -832,7 +832,7 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
<a class="stdlink" href="{ <a class="stdlink" href="{
url_for("assiduites.signal_assiduites_group", url_for("assiduites.signal_assiduites_group",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
jour=datetime.date.today().isoformat(), day=datetime.date.today().isoformat(),
formsemestre_id=formsemestre.id, formsemestre_id=formsemestre.id,
group_ids=group.id, group_ids=group.id,
)}"> )}">

View File

@ -54,11 +54,11 @@ class Jour:
""" """
return self.date.isocalendar()[0:2] == datetime.date.today().isocalendar()[0:2] return self.date.isocalendar()[0:2] == datetime.date.today().isocalendar()[0:2]
def get_date(self) -> str: def get_date(self, fmt=scu.DATE_FMT) -> str:
""" """
Renvoie la date du jour au format "dd/mm/yyyy" Renvoie la date du jour au format fmt ou "dd/mm/yyyy" par défaut
""" """
return self.date.strftime(scu.DATE_FMT) return self.date.strftime(fmt)
def get_html(self): def get_html(self):
""" """
@ -93,12 +93,22 @@ class Calendrier:
Représente un calendrier Représente un calendrier
Permet d'obtenir les informations sur les jours Permet d'obtenir les informations sur les jours
et générer une représentation html et générer une représentation html
highlight: str
-> ["jour", "semaine", "mois"]
permet de mettre en valeur lors du passage de la souris
""" """
def __init__(self, date_debut: datetime.date, date_fin: datetime.date): def __init__(
self,
date_debut: datetime.date,
date_fin: datetime.date,
highlight: str = None,
):
self.date_debut = date_debut self.date_debut = date_debut
self.date_fin = date_fin self.date_fin = date_fin
self.jours: dict[str, list[Jour]] = {} self.jours: dict[str, list[Jour]] = {}
self.highlight: str = highlight
def _get_dates_between(self) -> list[datetime.date]: def _get_dates_between(self) -> list[datetime.date]:
""" """
@ -130,11 +140,13 @@ class Calendrier:
month = scu.MONTH_NAMES_ABBREV[date.month - 1] month = scu.MONTH_NAMES_ABBREV[date.month - 1]
# Ajouter le jour à la liste correspondante au mois # Ajouter le jour à la liste correspondante au mois
if month not in organized: if month not in organized:
organized[month] = [] organized[month] = {} # semaine {22: []}
jour: Jour = self.instanciate_jour(date) jour: Jour = self.instanciate_jour(date)
semaine = date.strftime("%G-W%V")
organized[month].append(jour) if semaine not in organized[month]:
organized[month][semaine] = []
organized[month][semaine].append(jour)
self.jours = organized self.jours = organized
@ -150,4 +162,53 @@ class Calendrier:
get_html Renvoie le code html du calendrier get_html Renvoie le code html du calendrier
""" """
self.organize_by_month() self.organize_by_month()
return render_template("calendrier.j2", calendrier=self.jours) return render_template(
"calendrier.j2", calendrier=self.jours, highlight=self.highlight
)
class JourChoix(Jour):
"""
Représente un jour dans le calendrier pour choisir une date
"""
def get_html(self):
return ""
class CalendrierChoix(Calendrier):
"""
Représente un calendrier pour choisir une date
"""
def instanciate_jour(self, date: datetime.date) -> Jour:
return JourChoix(date)
def calendrier_choix_date(
date_debut: datetime.date,
date_fin: datetime.date,
url: str,
mode: str = "jour",
titre: str = "Choisir une date",
):
"""
Permet d'afficher un calendrier pour choisir une date et renvoyer sur une url.
mode : str
- "jour" -> ajoutera "&day=yyyy-mm-dd" à l'url (ex: 2024-05-30)
- "semaine" -> ajoutera "&week=yyyy-Www" à l'url (ex : 2024-W22)
titre : str
- texte à afficher au dessus du calendrier
"""
calendrier: CalendrierChoix = CalendrierChoix(date_debut, date_fin, highlight=mode)
return render_template(
"choix_date.j2",
calendrier=calendrier.get_html(),
url=url,
titre=titre,
mode=mode,
)

View File

@ -983,7 +983,7 @@ def form_choix_jour_saisie_hebdo(groups_infos, moduleimpl_id=None):
"assiduites.signal_assiduites_group", "assiduites.signal_assiduites_group",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
group_ids=",".join(map(str,groups_infos.group_ids)), group_ids=",".join(map(str,groups_infos.group_ids)),
jour=datetime.date.today().isoformat(), day=datetime.date.today().isoformat(),
formsemestre_id=groups_infos.formsemestre_id, formsemestre_id=groups_infos.formsemestre_id,
moduleimpl_id="" if moduleimpl_id is None else moduleimpl_id moduleimpl_id="" if moduleimpl_id is None else moduleimpl_id
) )

View File

@ -346,7 +346,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
f""" f"""
<span class="moduleimpl_abs_link"><a class="stdlink" href="{ <span class="moduleimpl_abs_link"><a class="stdlink" href="{
url_for("assiduites.signal_assiduites_group", scodoc_dept=g.scodoc_dept) url_for("assiduites.signal_assiduites_group", scodoc_dept=g.scodoc_dept)
}?group_ids={group_id}&jour={ }?group_ids={group_id}&day={
datetime.date.today().isoformat() datetime.date.today().isoformat()
}&formsemestre_id={formsemestre.id} }&formsemestre_id={formsemestre.id}
&moduleimpl_id={moduleimpl_id} &moduleimpl_id={moduleimpl_id}

View File

@ -1,10 +1,11 @@
<div class="calendrier"> <div class="calendrier">
{% for mois,jours in calendrier.items() %} {% for mois,semaines in calendrier.items() %}
<div class="mois"> <div class="mois {{'highlight' if highlight=='mois'}}">
<h3>{{mois}}</h3> <h3>{{mois}}</h3>
<div class="jours"> {% for semaine in semaines %}
{% for jour in jours %} <div class="jours {{'highlight' if highlight=='semaine'}}" week_index="{{semaine}}">
<div class="jour {{jour.get_class()}}"> {% for jour in semaines[semaine] %}
<div class="jour {{jour.get_class()}} {{'highlight' if highlight=='jour'}}" date="{{jour.get_date('%Y-%m-%d')}}">
<span class="nom">{{jour.get_nom()}}</span> <span class="nom">{{jour.get_nom()}}</span>
<div class="contenu"> <div class="contenu">
{{jour.get_html() | safe}} {{jour.get_html() | safe}}
@ -12,6 +13,7 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endfor %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -84,5 +86,8 @@
border-left: solid 3px var(--couleur); border-left: solid 3px var(--couleur);
border-right: solid 3px var(--couleur); border-right: solid 3px var(--couleur);
} }
.highlight:hover{
border: solid 3px yellow;
}
</style> </style>

View File

@ -0,0 +1,62 @@
{% extends "sco_page.j2" %}
{% block styles %}
{{super()}}
<style>
.highlight {
cursor: pointer !important;
}
.highlight * {
cursor: pointer !important;
}
#gtrcontent h2.titre {
text-align: center;
margin-top: 10px;
}
.content{
width: 90%;
max-width: 1600px;
}
</style>
{% endblock %}
{% block app_content %}
<div class="content">
<h2 class="titre">{{titre}}</h2>
{{calendrier | safe}}
</div>
{% endblock app_content %}
{% block scripts %}
{{ super() }}
<script>
const mode = "{{mode}}";
const url = new URL(window.location.origin + "{{url | safe}}");
document.addEventListener("DOMContentLoaded", ()=>{
const highlight = document.querySelectorAll(".highlight");
highlight.forEach((el)=>{
el.addEventListener("click", (e)=>{
if (mode == "jour"){
const date = el.getAttribute("date");
url.searchParams.set("day", date);
}
if (mode == "semaine"){
const date = el.getAttribute("week_index");
url.searchParams.set("week", date);
}
window.location.href = url;
})
})
})
</script>
{% endblock scripts %}

View File

@ -894,63 +894,6 @@ def calendrier_assi_etud():
) )
@bp.route("/choix_date", methods=["GET", "POST"])
@scodoc
@permission_required(Permission.AbsChange)
def choix_date() -> str:
"""
choix_date Choix de la date pour la saisie des assiduités
Route utilisée uniquement si la date courante n'est pas dans le semestre
concerné par la requête vers une des pages suivantes :
- saisie_assiduites_group
"""
formsemestre_id = request.args.get("formsemestre_id")
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
group_ids = request.args.get("group_ids")
moduleimpl_id = request.args.get("moduleimpl_id")
form = ChoixDateForm(request.form)
if form.validate_on_submit():
if form.cancel.data:
return redirect(url_for("scodoc.index"))
# Vérifier si date dans semestre
ok: bool = False
try:
date: datetime.date = datetime.datetime.strptime(
form.date.data, scu.DATE_FMT
).date()
if date < formsemestre.date_debut or date > formsemestre.date_fin:
form.set_error(
"La date sélectionnée n'est pas dans le semestre.", form.date
)
else:
ok = True
except ValueError:
form.set_error("Date invalide", form.date)
if ok:
return redirect(
url_for(
"assiduites.signal_assiduites_group",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
group_ids=group_ids,
moduleimpl_id=moduleimpl_id,
jour=date.isoformat(),
)
)
return render_template(
"assiduites/pages/choix_date.j2",
form=form,
sco=ScoData(formsemestre=formsemestre),
deb=formsemestre.date_debut.strftime(scu.DATE_FMT),
fin=formsemestre.date_fin.strftime(scu.DATE_FMT),
)
@bp.route("/signal_assiduites_group") @bp.route("/signal_assiduites_group")
@scodoc @scodoc
@permission_required(Permission.AbsChange) @permission_required(Permission.AbsChange)
@ -965,7 +908,7 @@ def signal_assiduites_group():
# formsemestre_id est optionnel si modimpl est indiqué # formsemestre_id est optionnel si modimpl est indiqué
formsemestre_id: int = request.args.get("formsemestre_id", -1) formsemestre_id: int = request.args.get("formsemestre_id", -1)
moduleimpl_id: int = request.args.get("moduleimpl_id") moduleimpl_id: int = request.args.get("moduleimpl_id")
date: str = request.args.get("jour", datetime.date.today().isoformat()) date: str = request.args.get("day", datetime.date.today().isoformat())
heures: list[str] = [ heures: list[str] = [
request.args.get("heure_deb", ""), request.args.get("heure_deb", ""),
request.args.get("heure_fin", ""), request.args.get("heure_fin", ""),
@ -1028,14 +971,23 @@ def signal_assiduites_group():
if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin: if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin:
# Si le jour est hors semestre, renvoyer vers choix date # Si le jour est hors semestre, renvoyer vers choix date
return redirect( flash(
url_for( "La date sélectionnée n'est pas dans le semestre. Choisissez une autre date."
"assiduites.choix_date", )
formsemestre_id=formsemestre_id,
group_ids=group_ids, return sco_gen_cal.calendrier_choix_date(
moduleimpl_id=moduleimpl_id, formsemestre.date_debut,
formsemestre.date_fin,
url=url_for(
"assiduites.signal_assiduites_group",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
) formsemestre_id=formsemestre_id,
group_ids=",".join(group_ids),
moduleimpl_id=moduleimpl_id,
day="placeholder",
),
mode="jour",
titre="Choix de la date",
) )
# --- Restriction en fonction du moduleimpl_id --- # --- Restriction en fonction du moduleimpl_id ---
@ -2038,7 +1990,23 @@ def signal_assiduites_hebdo():
# les chaines sont triables par ordre alphanumérique croissant # les chaines sont triables par ordre alphanumérique croissant
# et produiront le même ordre que les dates par ordre chronologique croissant # et produiront le même ordre que les dates par ordre chronologique croissant
if week < fs_deb_iso8601 or week > fs_fin_iso8601: if week < fs_deb_iso8601 or week > fs_fin_iso8601:
raise ScoValueError("Semaine hors du semestre", dest_url=request.referrer) flash(
"La semaine n'est pas dans le semestre, choisissez la semaine sur laquelle saisir l'assiduité"
)
return sco_gen_cal.calendrier_choix_date(
date_debut=formsemestre.date_debut,
date_fin=formsemestre.date_fin,
url=url_for(
"assiduites.signal_assiduites_hebdo",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
group_ids=group_ids,
moduleimpl_id=moduleimpl_id,
week="placeholder",
),
mode="semaine",
titre="Choix de la semaine",
)
# Vérification des groupes # Vérification des groupes
group_ids = group_ids.split(",") if group_ids != "" else [] group_ids = group_ids.split(",") if group_ids != "" else []