Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
12 changed files with 138 additions and 81 deletions
Showing only changes of commit bb331e31c1 - Show all commits

View File

@ -28,12 +28,14 @@
"""
Formulaire configuration Module Assiduités
"""
import datetime
from flask_wtf import FlaskForm
from wtforms import SubmitField, DecimalField
from wtforms import DecimalField, SubmitField, ValidationError
from wtforms.fields.simple import StringField
from wtforms.validators import Optional
from wtforms.widgets import TimeInput
import datetime
class TimeField(StringField):
@ -72,9 +74,28 @@ class TimeField(StringField):
else:
raise ValueError
self.data = datetime.time(hour, minutes, seconds)
except ValueError:
except ValueError as exc:
self.data = None
raise ValueError(self.gettext("Not a valid time string"))
raise ValueError(self.gettext("Not a valid time string")) from exc
def check_tick_time(form, field):
"""Le tick_time doit être entre 0 et 60 minutes"""
if field.data < 1 or field.data > 59:
raise ValidationError("Valeur de granularité invalide (entre 1 et 59)")
def check_ics_path(form, field):
"""Vérifie que le chemin est bien un chemin absolu
et qu'il contient edt_id
"""
data = field.data.strip()
if not data:
return
if not data.startswith("/"):
raise ValidationError("Le chemin vers les ics doit commencer par /")
if not "{edt_id}" in data:
raise ValidationError("Le chemin vers les ics doit utiliser {edt_id}")
class ConfigAssiduitesForm(FlaskForm):
@ -84,7 +105,20 @@ class ConfigAssiduitesForm(FlaskForm):
lunch_time = TimeField("Heure de midi (date pivot entre Matin et Après Midi)")
afternoon_time = TimeField("Fin de la journée")
tick_time = DecimalField("Granularité de la Time Line (temps en minutes)", places=0)
tick_time = DecimalField(
"Granularité de la timeline (temps en minutes)",
places=0,
validators=[check_tick_time],
)
edt_ics_path = StringField(
label="Chemin vers les ics",
description="""Chemin absolu unix sur le serveur vers le fichier ics donnant l'emploi
du temps d'un semestre. La balise <tt>{edt_id}</tt> sera remplacée par l'edt_id du
semestre (par défaut, son code étape Apogée).
Si ce champ n'est pas renseigné, les emplois du temps ne seront pas utilisés.""",
validators=[Optional(), check_ics_path],
)
submit = SubmitField("Valider")
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})

View File

@ -42,11 +42,13 @@ def check_cas_uid_from_mail_regexp(form, field):
if not ScoDocSiteConfig.cas_uid_from_mail_regexp_is_valid(field.data):
raise ValidationError("expression régulière invalide")
def check_cas_edt_id_from_xml_regexp(form, field):
"Vérifie la regexp fournie pour l'extraction du CAS id"
if not ScoDocSiteConfig.cas_edt_id_from_xml_regexp_is_valid(field.data):
raise ValidationError("expression régulière pour edt_id invalide")
class ConfigCASForm(FlaskForm):
"Formulaire paramétrage CAS"
cas_enable = BooleanField("Activer le CAS")

View File

@ -29,7 +29,7 @@ def PersonalizedLinksForm() -> _PersonalizedLinksForm:
F,
f"link_{idx}",
StringField(
f"Titre",
"Titre",
validators=[
validators.Optional(),
validators.Length(min=1, max=80),
@ -42,7 +42,7 @@ def PersonalizedLinksForm() -> _PersonalizedLinksForm:
F,
f"link_url_{idx}",
StringField(
f"URL",
"URL",
description="adresse, incluant le http.",
validators=[
validators.Optional(),
@ -56,7 +56,7 @@ def PersonalizedLinksForm() -> _PersonalizedLinksForm:
F,
f"link_with_args_{idx}",
BooleanField(
f"ajouter arguments",
"ajouter arguments",
description="query string avec ids",
),
)

View File

@ -42,9 +42,6 @@ class ModuleImpl(db.Model):
viewonly=True,
)
def __init__(self, **kwargs):
super(ModuleImpl, self).__init__(**kwargs)
def __repr__(self):
return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>"

View File

@ -74,7 +74,8 @@ _moduleEditor = ndb.EditableTable(
"semestre_id",
"numero",
"code_apogee",
"module_type"
"module_type",
"edt_id",
#'ects'
),
sortkey="numero, code, titre",

View File

@ -68,6 +68,7 @@ _formsemestreEditor = ndb.EditableTable(
"ens_can_edit_eval",
"elt_sem_apo",
"elt_annee_apo",
"edt_id",
),
filter_dept=True,
sortkey="date_debut",

View File

@ -40,6 +40,7 @@ from app.models import (
ModuleImpl,
Evaluation,
UniteEns,
ScoDocSiteConfig,
ScolarFormSemestreValidation,
ScolarAutorisationInscription,
ApcValidationAnnee,
@ -445,6 +446,18 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
},
)
)
if ScoDocSiteConfig.get("edt_ics_path"):
modform.append(
(
"edt_id",
{
"size": 32,
"title": "Identifiant EDT",
"explanation": "optionnel, identifiant sur le logiciel emploi du temps (par défaut, utilise la première étape Apogée).",
"allow_null": True,
},
)
)
if edit:
formtit = f"""
<p><a class="stdlink" href="{url_for("notes.formsemestre_edit_uecoefs",

View File

@ -79,7 +79,9 @@ partitionEditor = ndb.EditableTable(
)
groupEditor = ndb.EditableTable(
"group_descr", "group_id", ("group_id", "partition_id", "group_name", "numero")
"group_descr",
"group_id",
("group_id", "partition_id", "group_name", "numero", "edt_id"),
)
group_list = groupEditor.list

View File

@ -13,10 +13,9 @@ affectent notamment les comptages d'absences de tous les bulletins des
</div>
<form class="form form-horizontal" method="post" enctype="multipart/form-data" role="form">
<div class="row">
<div class="col-md-8">
<form class="form form-horizontal" method="post" enctype="multipart/form-data" role="form">
{{ form.hidden_tag() }}
{{ wtf.form_errors(form, hiddens="only") }}
@ -24,13 +23,24 @@ affectent notamment les comptages d'absences de tous les bulletins des
{{ wtf.form_field(form.lunch_time) }}
{{ wtf.form_field(form.afternoon_time) }}
{{ wtf.form_field(form.tick_time) }}
</div>
</div>
<div class="row">
<h1>Emplois du temps</h1>
<div class="help">ScoDoc peut récupérer les emplois du temps de chaque session.</div>
<div class="col-md-8">
<div class="config-edt">
{{ wtf.form_field(form.edt_ics_path) }}
</div>
<div class="form-group">
{{ wtf.form_field(form.submit) }}
{{ wtf.form_field(form.cancel) }}
</div>
</div>
</div>
</form>
</div>
</div>

View File

@ -73,8 +73,8 @@ Heure: <b><tt>{{ time.strftime("%d/%m/%Y %H:%M") }}</tt></b>
</p>
</section>
<section>
<h2>Assiduité</h2>
<p><a class="stdlink" href="{{url_for('scodoc.config_assiduites')}}">Configuration du suivi de l'assiduité</a>
<h2>Assiduité et emplois du temps</h2>
<p><a class="stdlink" href="{{url_for('scodoc.config_assiduites')}}">Configuration du suivi de l'assiduité et accès aux emplois du temps</a>
</p>
</section>

View File

@ -323,6 +323,7 @@ def config_assiduites():
form = ConfigAssiduitesForm()
if request.method == "POST" and form.cancel.data: # cancel button
return redirect(url_for("scodoc.index"))
if form.validate_on_submit():
if ScoDocSiteConfig.set("assi_morning_time", form.data["morning_time"]):
flash("Heure du début de la journée enregistrée")
@ -330,18 +331,13 @@ def config_assiduites():
flash("Heure de midi enregistrée")
if ScoDocSiteConfig.set("assi_afternoon_time", form.data["afternoon_time"]):
flash("Heure de fin de la journée enregistrée")
if (
form.data["tick_time"] > 0
and form.data["tick_time"] < 60
and ScoDocSiteConfig.set("assi_tick_time", float(form.data["tick_time"]))
):
if ScoDocSiteConfig.set("assi_tick_time", float(form.data["tick_time"])):
flash("Granularité de la timeline enregistrée")
else:
flash("Erreur : Granularité invalide ou identique")
if ScoDocSiteConfig.set("edt_ics_path", form.data["edt_ics_path"]):
flash("Chemin vers les calendriers ics enregistré")
return redirect(url_for("scodoc.configuration"))
elif request.method == "GET":
if request.method == "GET":
form.morning_time.data = ScoDocSiteConfig.get(
"assi_morning_time", datetime.time(8, 0, 0)
)
@ -356,6 +352,7 @@ def config_assiduites():
except ValueError:
form.tick_time.data = 15.0
ScoDocSiteConfig.set("assi_tick_time", 15.0)
form.edt_ics_path.data = ScoDocSiteConfig.get("edt_ics_path")
return render_template(
"assiduites/pages/config_assiduites.j2",