forked from ScoDoc/ScoDoc
EDT: extraction et affichage de l'enseignant
This commit is contained in:
parent
4cb7479b6f
commit
999757dd77
@ -139,7 +139,7 @@ class ConfigAssiduitesForm(FlaskForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
edt_ics_title_field = StringField(
|
edt_ics_title_field = StringField(
|
||||||
label="Champs contenant le titre",
|
label="Champ contenant le titre",
|
||||||
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
||||||
validators=[Optional(), check_ics_field],
|
validators=[Optional(), check_ics_field],
|
||||||
)
|
)
|
||||||
@ -152,7 +152,7 @@ class ConfigAssiduitesForm(FlaskForm):
|
|||||||
validators=[Optional(), check_ics_regexp],
|
validators=[Optional(), check_ics_regexp],
|
||||||
)
|
)
|
||||||
edt_ics_group_field = StringField(
|
edt_ics_group_field = StringField(
|
||||||
label="Champs contenant le groupe",
|
label="Champ contenant le groupe",
|
||||||
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
||||||
validators=[Optional(), check_ics_field],
|
validators=[Optional(), check_ics_field],
|
||||||
)
|
)
|
||||||
@ -165,7 +165,7 @@ class ConfigAssiduitesForm(FlaskForm):
|
|||||||
validators=[Optional(), check_ics_regexp],
|
validators=[Optional(), check_ics_regexp],
|
||||||
)
|
)
|
||||||
edt_ics_mod_field = StringField(
|
edt_ics_mod_field = StringField(
|
||||||
label="Champs contenant le module",
|
label="Champ contenant le module",
|
||||||
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
||||||
validators=[Optional(), check_ics_field],
|
validators=[Optional(), check_ics_field],
|
||||||
)
|
)
|
||||||
@ -177,6 +177,18 @@ class ConfigAssiduitesForm(FlaskForm):
|
|||||||
""",
|
""",
|
||||||
validators=[Optional(), check_ics_regexp],
|
validators=[Optional(), check_ics_regexp],
|
||||||
)
|
)
|
||||||
|
edt_ics_uid_field = StringField(
|
||||||
|
label="Champ contenant l'enseignant",
|
||||||
|
description="""champ de l'évènement calendrier: DESCRIPTION, SUMMARY, ...""",
|
||||||
|
validators=[Optional(), check_ics_field],
|
||||||
|
)
|
||||||
|
edt_ics_uid_regexp = StringField(
|
||||||
|
label="Extraction de l'enseignant",
|
||||||
|
description=r"""expression régulière python dont le premier groupe doit
|
||||||
|
correspondre à l'identifiant (edt_id) de l'enseignant associé à l'évènement.
|
||||||
|
Exemple: <tt>Enseignant : ([0-9]+)</tt>
|
||||||
|
""",
|
||||||
|
validators=[Optional(), check_ics_regexp],
|
||||||
|
)
|
||||||
submit = SubmitField("Valider")
|
submit = SubmitField("Valider")
|
||||||
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})
|
||||||
|
@ -36,6 +36,7 @@ import icalendar
|
|||||||
|
|
||||||
from flask import g, url_for
|
from flask import g, url_for
|
||||||
from app import log
|
from app import log
|
||||||
|
from app.auth.models import User
|
||||||
from app.models import FormSemestre, GroupDescr, ModuleImpl, ScoDocSiteConfig
|
from app.models import FormSemestre, GroupDescr, ModuleImpl, ScoDocSiteConfig
|
||||||
from app.scodoc.sco_exceptions import ScoValueError
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
@ -56,8 +57,11 @@ def formsemestre_load_calendar(
|
|||||||
Raises ScoValueError if not configured or not available or invalid format.
|
Raises ScoValueError if not configured or not available or invalid format.
|
||||||
"""
|
"""
|
||||||
edt_ids = []
|
edt_ids = []
|
||||||
if edt_id is None and formsemestre:
|
if edt_id is None:
|
||||||
edt_ids = formsemestre.get_edt_ids()
|
if formsemestre:
|
||||||
|
edt_ids = formsemestre.get_edt_ids()
|
||||||
|
else:
|
||||||
|
edt_ids = [edt_id]
|
||||||
if not edt_ids:
|
if not edt_ids:
|
||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)"
|
"accès aux emplois du temps non configuré pour ce semestre (pas d'edt_id)"
|
||||||
@ -132,8 +136,10 @@ def formsemestre_edt_dict(
|
|||||||
except ScoValueError as exc:
|
except ScoValueError as exc:
|
||||||
return exc.args[0]
|
return exc.args[0]
|
||||||
# Génération des événements pour le calendrier html
|
# Génération des événements pour le calendrier html
|
||||||
promo_icon = f"""<img height="24px" src="{scu.STATIC_DIR}/icons/promo.svg"
|
promo_icon = f"""<img height="28px" src="{scu.STATIC_DIR}/icons/promo.svg"
|
||||||
title="promotion complète" alt="promotion"/>"""
|
title="promotion complète" alt="promotion"/>"""
|
||||||
|
abs_icon = f"""<img height="28px" src="{scu.STATIC_DIR}/icons/absences.svg"
|
||||||
|
title="saisir absences" alt="saisir absences"/>"""
|
||||||
events_cal = []
|
events_cal = []
|
||||||
for event in events_scodoc:
|
for event in events_scodoc:
|
||||||
group: GroupDescr | bool = event["group"]
|
group: GroupDescr | bool = event["group"]
|
||||||
@ -191,19 +197,26 @@ def formsemestre_edt_dict(
|
|||||||
# --- Lien saisie abs
|
# --- Lien saisie abs
|
||||||
link_abs = (
|
link_abs = (
|
||||||
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
|
f"""<div class="module-edt link-abs"><a class="stdlink" href="{
|
||||||
url_abs}">absences</a>
|
url_abs}">{abs_icon}</a>
|
||||||
</div>"""
|
</div>"""
|
||||||
if url_abs
|
if url_abs
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ens_user_name = event["ens"].user_name if event["ens"] else None
|
||||||
|
ens_nomprenom = event["ens"].get_nomprenom() if event["ens"] else None
|
||||||
d = {
|
d = {
|
||||||
# Champs utilisés par tui.calendar
|
# Champs utilisés par tui.calendar
|
||||||
"calendarId": "cal1",
|
"calendarId": "cal1",
|
||||||
"title": f"""{title} {group_disp} {link_abs}""",
|
"title": f"""{title} {group_disp} {
|
||||||
|
'('+ens_nomprenom+')' if ens_nomprenom else ''
|
||||||
|
} {link_abs}""",
|
||||||
"start": event["start"],
|
"start": event["start"],
|
||||||
"end": event["end"],
|
"end": event["end"],
|
||||||
"backgroundColor": event["group_bg_color"],
|
"backgroundColor": event["group_bg_color"],
|
||||||
# Infos brutes pour usage API éventuel
|
# Infos brutes pour usage API éventuel
|
||||||
|
"ens_edt": event["edt_ens"],
|
||||||
|
"ens_user_name": ens_user_name,
|
||||||
"group_id": group.id if group else None,
|
"group_id": group.id if group else None,
|
||||||
"group_edt_id": event["edt_group"],
|
"group_edt_id": event["edt_group"],
|
||||||
"moduleimpl_id": modimpl.id if modimpl else None,
|
"moduleimpl_id": modimpl.id if modimpl else None,
|
||||||
@ -257,6 +270,16 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s
|
|||||||
raise ScoValueError(
|
raise ScoValueError(
|
||||||
"expression d'extraction du module depuis l'emploi du temps invalide"
|
"expression d'extraction du module depuis l'emploi du temps invalide"
|
||||||
) from exc
|
) from exc
|
||||||
|
edt_ics_uid_field = ScoDocSiteConfig.get("edt_ics_uid_field")
|
||||||
|
edt_ics_uid_regexp = ScoDocSiteConfig.get("edt_ics_uid_regexp")
|
||||||
|
try:
|
||||||
|
edt_ics_uid_pattern = (
|
||||||
|
re.compile(edt_ics_uid_regexp) if edt_ics_uid_regexp else None
|
||||||
|
)
|
||||||
|
except re.error as exc:
|
||||||
|
raise ScoValueError(
|
||||||
|
"expression d'extraction de l'enseignant depuis l'emploi du temps invalide"
|
||||||
|
) from exc
|
||||||
# --- Correspondances id edt -> id scodoc pour groupes, modules et enseignants
|
# --- Correspondances id edt -> id scodoc pour groupes, modules et enseignants
|
||||||
edt2group = formsemestre_retreive_groups_from_edt_id(formsemestre)
|
edt2group = formsemestre_retreive_groups_from_edt_id(formsemestre)
|
||||||
group_colors = {
|
group_colors = {
|
||||||
@ -266,6 +289,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s
|
|||||||
edt_groups_ids = set() # les ids de groupes tels que dans l'ics
|
edt_groups_ids = set() # les ids de groupes tels que dans l'ics
|
||||||
default_group = formsemestre.get_default_group()
|
default_group = formsemestre.get_default_group()
|
||||||
edt2modimpl = formsemestre_retreive_modimpls_from_edt_id(formsemestre)
|
edt2modimpl = formsemestre_retreive_modimpls_from_edt_id(formsemestre)
|
||||||
|
edt2user: dict[str, User | None] = {} # construit au fur et à mesure (cache)
|
||||||
# ---
|
# ---
|
||||||
events = [e for e in calendar.walk() if e.name == "VEVENT"]
|
events = [e for e in calendar.walk() if e.name == "VEVENT"]
|
||||||
events_sco = []
|
events_sco = []
|
||||||
@ -313,7 +337,19 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s
|
|||||||
else:
|
else:
|
||||||
modimpl = False
|
modimpl = False
|
||||||
edt_module = ""
|
edt_module = ""
|
||||||
# --- TODO: enseignant
|
# --- Enseignant
|
||||||
|
if edt_ics_uid_pattern:
|
||||||
|
edt_ens = extract_event_data(
|
||||||
|
event, edt_ics_uid_field, edt_ics_uid_pattern
|
||||||
|
)
|
||||||
|
if edt_ens in edt2user:
|
||||||
|
ens = edt2user[edt_ens]
|
||||||
|
else:
|
||||||
|
ens = User.query.filter_by(edt_id=edt_ens).first()
|
||||||
|
edt2user[edt_ens] = ens
|
||||||
|
else:
|
||||||
|
ens = None
|
||||||
|
edt_ens = ""
|
||||||
#
|
#
|
||||||
events_sco.append(
|
events_sco.append(
|
||||||
{
|
{
|
||||||
@ -324,6 +360,9 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s
|
|||||||
"group_bg_color": group_bg_color, # associée au groupe
|
"group_bg_color": group_bg_color, # associée au groupe
|
||||||
"modimpl": modimpl, # False si extracteur non configuré
|
"modimpl": modimpl, # False si extracteur non configuré
|
||||||
"edt_module": edt_module, # id module edt non traduit
|
"edt_module": edt_module, # id module edt non traduit
|
||||||
|
# Enseignant
|
||||||
|
"edt_ens": edt_ens, # id ens edt, non traduit
|
||||||
|
"ens": ens,
|
||||||
# heures pour saisie abs: en heure LOCALE DU SERVEUR
|
# heures pour saisie abs: en heure LOCALE DU SERVEUR
|
||||||
"heure_deb": event.decoded("dtstart")
|
"heure_deb": event.decoded("dtstart")
|
||||||
.replace(tzinfo=timezone.utc)
|
.replace(tzinfo=timezone.utc)
|
||||||
|
1
app/static/icons/absences.svg
Normal file
1
app/static/icons/absences.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.8 KiB |
@ -96,7 +96,7 @@ affectent notamment les comptages d'absences de tous les bulletins des
|
|||||||
<button id="test_load_ics" type="button" onclick="load_ics_sample()" disabled>
|
<button id="test_load_ics" type="button" onclick="load_ics_sample()" disabled>
|
||||||
Essayer de charger l'ics</button>
|
Essayer de charger l'ics</button>
|
||||||
<div id="raw-ics-sample-zone">
|
<div id="raw-ics-sample-zone">
|
||||||
<div>Voici un évènement chargé au milieu de ce calendrier.
|
<div>Voici un évènement chargé, pris au hasard au milieu de ce calendrier.
|
||||||
Utilisez cet exemple pour configurer les expressions d'extraction
|
Utilisez cet exemple pour configurer les expressions d'extraction
|
||||||
en bas de ce formulaire.
|
en bas de ce formulaire.
|
||||||
</div>
|
</div>
|
||||||
@ -121,7 +121,10 @@ affectent notamment les comptages d'absences de tous les bulletins des
|
|||||||
{{ wtf.form_field(form.edt_ics_mod_field) }}
|
{{ wtf.form_field(form.edt_ics_mod_field) }}
|
||||||
{{ wtf.form_field(form.edt_ics_mod_regexp) }}
|
{{ wtf.form_field(form.edt_ics_mod_regexp) }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="config-edt">
|
||||||
|
{{ wtf.form_field(form.edt_ics_uid_field) }}
|
||||||
|
{{ wtf.form_field(form.edt_ics_uid_regexp) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
{{ wtf.form_field(form.submit) }}
|
{{ wtf.form_field(form.submit) }}
|
||||||
|
@ -93,6 +93,6 @@
|
|||||||
<script src="{{scu.STATIC_DIR}}/js/scodoc.js"></script>
|
<script src="{{scu.STATIC_DIR}}/js/scodoc.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const SCO_URL = "{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)[:-11] }}";
|
const SCO_URL = "{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept)[:-11] }}";
|
||||||
const SCO_TIMEZONE = {{ scu.TIME_ZONE }}
|
const SCO_TIMEZONE = "{{ scu.TIME_ZONE }}";
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -4,6 +4,9 @@
|
|||||||
{% block styles %}
|
{% block styles %}
|
||||||
{{super()}}
|
{{super()}}
|
||||||
<style>
|
<style>
|
||||||
|
.form-titre {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
span.mod-label {
|
span.mod-label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
@ -68,13 +71,17 @@ Pour les modifier, aller dans l'édition de la formation.
|
|||||||
<form id="mf" class="form form-horizontal" method="post" enctype="multipart/form-data" role="form">
|
<form id="mf" class="form form-horizontal" method="post" enctype="multipart/form-data" role="form">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{{ wtf.form_errors(form, hiddens="only") }}
|
{{ wtf.form_errors(form, hiddens="only") }}
|
||||||
<div class="form-group">
|
<div class="form-group form-titre">
|
||||||
<span class="mod-label">
|
<span class="mod-label">
|
||||||
<label>Module</label>
|
<label>Module</label>
|
||||||
<span class="code-apo-module">Code Apo. Module</span>
|
<span class="code-apo-module" title="codes dans la formation">Code Apo. Module</span>
|
||||||
<span class="field-apo">Code(s) Apogée</span>
|
|
||||||
<span class="field-edt">Code EDT</span>
|
|
||||||
</span>
|
</span>
|
||||||
|
<span class="field-apo"
|
||||||
|
title="codes spécifiques à ce semestre (si différents de ceux de la formation)">
|
||||||
|
Code(s) Apogée</span>
|
||||||
|
<span class="field-edt"
|
||||||
|
title="identifiant dans l'emploi du temps (si différent du code Apogée)">
|
||||||
|
Code EDT</span>
|
||||||
</div>
|
</div>
|
||||||
{% for modimpl in formsemestre.modimpls_sorted %}
|
{% for modimpl in formsemestre.modimpls_sorted %}
|
||||||
{{ render_text_field(form["modimpl_apo_" ~ modimpl.id], form["modimpl_edt_" ~ modimpl.id], modimpl.module.get_codes_apogee()) }}
|
{{ render_text_field(form["modimpl_apo_" ~ modimpl.id], form["modimpl_edt_" ~ modimpl.id], modimpl.module.get_codes_apogee()) }}
|
||||||
|
@ -47,6 +47,23 @@ table#edt2group tbody tr.active-row {
|
|||||||
si vous voyez ici de nombreuses lignes, il est possible que l'expression régulière
|
si vous voyez ici de nombreuses lignes, il est possible que l'expression régulière
|
||||||
d'extraction soit incorrecte (voir configuration globale) ou bien que votre logiciel d'emploi du temps génère de nombreux évènements non associés à un groupe donné.
|
d'extraction soit incorrecte (voir configuration globale) ou bien que votre logiciel d'emploi du temps génère de nombreux évènements non associés à un groupe donné.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if ScoDocSiteConfig.get("edt_ics_group_field") %}
|
||||||
|
<div>Les groupes sont extrait du champs <b>{{ScoDocSiteConfig.get("edt_ics_group_field")}}</b>
|
||||||
|
à l'aide de l'expression régulière: <tt>{{ScoDocSiteConfig.get("edt_ics_group_regexp")}}</tt>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="fontred">extraction non configuré</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if current_user.has_permission(sco.Permission.ScoSuperAdmin) %}
|
||||||
|
<div>Pour changer ce réglage, <a href="{{url_for('scodoc.config_assiduites')}}"
|
||||||
|
class="stdlink">voir la page de configuration</a>.</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="fontred">au besoin, l'administrateur peut changer ces réglages dans
|
||||||
|
le panneau de configuration générale.</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div>Voici ce qui a été extrait de l'emploi du temps par l'expression régulière configurée:
|
<div>Voici ce qui a été extrait de l'emploi du temps par l'expression régulière configurée:
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -209,4 +209,6 @@ def formsemestre_edt_help_config(formsemestre_id: int):
|
|||||||
edt_groups_ids=edt_groups_ids,
|
edt_groups_ids=edt_groups_ids,
|
||||||
events_sco=events_sco,
|
events_sco=events_sco,
|
||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
|
ScoDocSiteConfig=ScoDocSiteConfig,
|
||||||
|
title="Aide configuration EDT",
|
||||||
)
|
)
|
||||||
|
@ -52,7 +52,7 @@ from PIL import Image as PILImage
|
|||||||
from werkzeug.exceptions import BadRequest, NotFound
|
from werkzeug.exceptions import BadRequest, NotFound
|
||||||
|
|
||||||
|
|
||||||
from app import db
|
from app import db, log
|
||||||
from app.auth.models import User, Role
|
from app.auth.models import User, Role
|
||||||
from app.auth.cas import set_cas_configuration
|
from app.auth.cas import set_cas_configuration
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
@ -332,6 +332,8 @@ def config_assiduites():
|
|||||||
("edt_ics_group_regexp", "Expression extraction groupe"),
|
("edt_ics_group_regexp", "Expression extraction groupe"),
|
||||||
("edt_ics_mod_field", "Champ contenant module"),
|
("edt_ics_mod_field", "Champ contenant module"),
|
||||||
("edt_ics_mod_regexp", "Expression extraction module"),
|
("edt_ics_mod_regexp", "Expression extraction module"),
|
||||||
|
("edt_ics_uid_field", "Champ contenant l'enseignant"),
|
||||||
|
("edt_ics_uid_regexp", "Expression extraction de l'enseignant"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
@ -380,16 +382,20 @@ def config_assiduites():
|
|||||||
@admin_required
|
@admin_required
|
||||||
def ics_raw_sample(edt_id: str):
|
def ics_raw_sample(edt_id: str):
|
||||||
"Renvoie un extrait de l'ics brut, pour aider à configurer les extractions"
|
"Renvoie un extrait de l'ics brut, pour aider à configurer les extractions"
|
||||||
|
log(f"ics_raw_sample/{edt_id}")
|
||||||
try:
|
try:
|
||||||
raw_ics, _ = sco_edt_cal.formsemestre_load_calendar(edt_id=edt_id)
|
raw_ics, _ = sco_edt_cal.formsemestre_load_calendar(edt_id=edt_id)
|
||||||
except ScoValueError as exc:
|
except ScoValueError as exc:
|
||||||
|
log(f"ics_raw_sample: formsemestre_load_calendar({edt_id}) failed")
|
||||||
return exc.args[0]
|
return exc.args[0]
|
||||||
try:
|
try:
|
||||||
ics = raw_ics.decode(scu.SCO_ENCODING)
|
ics = raw_ics.decode(scu.SCO_ENCODING)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
|
log(f"ics_raw_sample: raw_ics.decode failed")
|
||||||
return f"Erreur lors de la conversion vers {scu.SCO_ENCODING}"
|
return f"Erreur lors de la conversion vers {scu.SCO_ENCODING}"
|
||||||
evs = ics.split("BEGIN:VEVENT")
|
evs = ics.split("BEGIN:VEVENT")
|
||||||
if len(evs) < 1:
|
if len(evs) < 1:
|
||||||
|
log(f"ics_raw_sample: empty calendar")
|
||||||
return "pas d'évènements VEVENT détectés dans ce fichier"
|
return "pas d'évènements VEVENT détectés dans ce fichier"
|
||||||
return "BEGIN:VEVENT" + evs[len(evs) // 2]
|
return "BEGIN:VEVENT" + evs[len(evs) // 2]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user