forked from ScoDoc/ScoDoc
Compare commits
No commits in common. "924037d5c6f4cacc1e128824df60e1da3d5ebb9c" and "4cb7479b6fbc584f1e7ebc30050e1a0bd97a4d5b" have entirely different histories.
924037d5c6
...
4cb7479b6f
@ -394,32 +394,6 @@ def group_edit(group_id: int):
|
|||||||
return group.to_dict(with_partition=True)
|
return group.to_dict(with_partition=True)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/group/<int:group_id>/set_edt_id/<string:edt_id>", methods=["POST"])
|
|
||||||
@api_web_bp.route("/group/<int:group_id>/set_edt_id/<string:edt_id>", methods=["POST"])
|
|
||||||
@login_required
|
|
||||||
@scodoc
|
|
||||||
@permission_required(Permission.ScoView)
|
|
||||||
@as_json
|
|
||||||
def group_set_edt_id(group_id: int, edt_id: str):
|
|
||||||
"""Set edt_id for this group.
|
|
||||||
Contrairement à /edit, peut-être changé pour toute partition
|
|
||||||
ou formsemestre non verrouillé.
|
|
||||||
"""
|
|
||||||
query = GroupDescr.query.filter_by(id=group_id)
|
|
||||||
if g.scodoc_dept:
|
|
||||||
query = (
|
|
||||||
query.join(Partition).join(FormSemestre).filter_by(dept_id=g.scodoc_dept_id)
|
|
||||||
)
|
|
||||||
group: GroupDescr = query.first_or_404()
|
|
||||||
if not group.partition.formsemestre.can_change_groups():
|
|
||||||
return json_error(401, "opération non autorisée")
|
|
||||||
log(f"group_set_edt_id( {group_id}, '{edt_id}' )")
|
|
||||||
group.edt_id = edt_id
|
|
||||||
db.session.add(group)
|
|
||||||
db.session.commit()
|
|
||||||
return group.to_dict(with_partition=True)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
|
@bp.route("/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"])
|
||||||
@api_web_bp.route(
|
@api_web_bp.route(
|
||||||
"/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"]
|
"/formsemestre/<int:formsemestre_id>/partition/create", methods=["POST"]
|
||||||
@ -520,7 +494,6 @@ def formsemestre_order_partitions(formsemestre_id: int):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
app.set_sco_dept(formsemestre.departement.acronym)
|
app.set_sco_dept(formsemestre.departement.acronym)
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id)
|
sco_cache.invalidate_formsemestre(formsemestre_id)
|
||||||
log(f"formsemestre_order_partitions({partition_ids})")
|
|
||||||
return [
|
return [
|
||||||
partition.to_dict()
|
partition.to_dict()
|
||||||
for partition in formsemestre.partitions.order_by(Partition.numero)
|
for partition in formsemestre.partitions.order_by(Partition.numero)
|
||||||
|
@ -139,7 +139,7 @@ class ConfigAssiduitesForm(FlaskForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
edt_ics_title_field = StringField(
|
edt_ics_title_field = StringField(
|
||||||
label="Champ contenant le titre",
|
label="Champs 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="Champ contenant le groupe",
|
label="Champs 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="Champ contenant le module",
|
label="Champs 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,18 +177,6 @@ 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})
|
||||||
|
@ -242,12 +242,10 @@ class GroupDescr(ScoDocModel):
|
|||||||
|
|
||||||
def to_dict(self, with_partition=True) -> dict:
|
def to_dict(self, with_partition=True) -> dict:
|
||||||
"""as a dict, with or without partition"""
|
"""as a dict, with or without partition"""
|
||||||
if with_partition:
|
|
||||||
partition_dict = self.partition.to_dict(with_groups=False)
|
|
||||||
d = dict(self.__dict__)
|
d = dict(self.__dict__)
|
||||||
d.pop("_sa_instance_state", None)
|
d.pop("_sa_instance_state", None)
|
||||||
if with_partition:
|
if with_partition:
|
||||||
d["partition"] = partition_dict
|
d["partition"] = self.partition.to_dict(with_groups=False)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def get_edt_ids(self) -> list[str]:
|
def get_edt_ids(self) -> list[str]:
|
||||||
|
@ -36,7 +36,6 @@ 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
|
||||||
@ -57,11 +56,8 @@ 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:
|
if edt_id is None and formsemestre:
|
||||||
if formsemestre:
|
edt_ids = formsemestre.get_edt_ids()
|
||||||
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)"
|
||||||
@ -136,10 +132,8 @@ 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="28px" src="{scu.STATIC_DIR}/icons/promo.svg"
|
promo_icon = f"""<img height="24px" 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"]
|
||||||
@ -197,26 +191,19 @@ 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}">{abs_icon}</a>
|
url_abs}">absences</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} {
|
"title": f"""{title} {group_disp} {link_abs}""",
|
||||||
'('+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,
|
||||||
@ -270,16 +257,6 @@ 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 = {
|
||||||
@ -289,7 +266,6 @@ 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 = []
|
||||||
@ -337,19 +313,7 @@ def load_and_convert_ics(formsemestre: FormSemestre) -> tuple[list[dict], list[s
|
|||||||
else:
|
else:
|
||||||
modimpl = False
|
modimpl = False
|
||||||
edt_module = ""
|
edt_module = ""
|
||||||
# --- Enseignant
|
# --- TODO: 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(
|
||||||
{
|
{
|
||||||
@ -360,9 +324,6 @@ 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)
|
||||||
|
@ -91,6 +91,9 @@ body:not(.editionActivated) .editing {
|
|||||||
.nonEditable .editing {
|
.nonEditable .editing {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.nonEditable .editing.rename {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.editionActivated #zoneChoix,
|
.editionActivated #zoneChoix,
|
||||||
@ -209,21 +212,16 @@ body.editionActivated .filtres>div>div>div>div {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grabbing>.hidenDropZone {
|
.grabbing>div[data-idpartition]:not([data-idgroupe]):hover:before {
|
||||||
pointer-events: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grabbing>div[data-idpartition]:not([data-idgroupe]):hover:before,
|
|
||||||
.grabbing>.hidenDropZone:hover:before {
|
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: -4px !important;
|
left: -4px;
|
||||||
right: -4px !important;
|
right: -4px;
|
||||||
bottom: calc(100% + 1px) !important;
|
bottom: calc(100% + 1px);
|
||||||
height: 2px !important;
|
height: 2px;
|
||||||
width: auto !important;
|
width: auto;
|
||||||
background: #c44;
|
background: #c44;
|
||||||
animation: insertPartion 0.2s infinite alternate ease-in-out !important;
|
animation: insertPartion 0.2s infinite alternate ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes insertPartion {
|
@keyframes insertPartion {
|
||||||
@ -285,7 +283,6 @@ body.editionActivated .filtres>div>div>div>div {
|
|||||||
|
|
||||||
#zonePartitions .filtres {
|
#zonePartitions .filtres {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePartitions .filtres>div {
|
#zonePartitions .filtres>div {
|
||||||
@ -335,24 +332,6 @@ body.editionActivated .filtres>div>div>div>div {
|
|||||||
margin: 4px 0 0 0;
|
margin: 4px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePartitions .filtres .groupes>button {
|
|
||||||
transition: none;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
text-shadow: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zonePartitions .filtres .hidenDropZone {
|
|
||||||
height: 100px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zonePartitions .filtres .groupes>div {
|
#zonePartitions .filtres .groupes>div {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #09c;
|
background: #09c;
|
||||||
@ -363,11 +342,6 @@ body.editionActivated .filtres>div>div>div>div {
|
|||||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
|
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePartitions .filtres .actif {
|
|
||||||
background: #0c9;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body:not(.editionActivated) .filtres .groupes>div {
|
body:not(.editionActivated) .filtres .groupes>div {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -382,7 +356,7 @@ body:not(.editionActivated) .filtres .groupes>div:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.editionActivated .filtres [data-idgroupe=aucun] {
|
body.editionActivated .filtres [data-idgroupe=aucun] {
|
||||||
display: none !important;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.editionActivated .filtres .nonEditable .move {
|
body.editionActivated .filtres .nonEditable .move {
|
||||||
@ -633,7 +607,3 @@ h3 {
|
|||||||
#zoneGroupes .groupe[data-idgroupe=aucun]>div:nth-child(1) {
|
#zoneGroupes .groupe[data-idgroupe=aucun]>div:nth-child(1) {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zonePartitions button span.editing:not(:first-child) {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
Before 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é, pris au hasard au milieu de ce calendrier.
|
<div>Voici un évènement chargé 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,10 +121,7 @@ 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,9 +4,6 @@
|
|||||||
{% 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;
|
||||||
@ -71,17 +68,13 @@ 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 form-titre">
|
<div class="form-group">
|
||||||
<span class="mod-label">
|
<span class="mod-label">
|
||||||
<label>Module</label>
|
<label>Module</label>
|
||||||
<span class="code-apo-module" title="codes dans la formation">Code Apo. Module</span>
|
<span class="code-apo-module">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,23 +47,6 @@ 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>
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
arrayPartitions.forEach((partition) => {
|
arrayPartitions.forEach((partition) => {
|
||||||
|
|
||||||
let divPartition = templateFiltres_partition(partition);
|
let divPartition = templateFiltres_partition(partition);
|
||||||
divFiltres.append(divPartition);
|
divFiltres.appendChild(divPartition);
|
||||||
|
|
||||||
let arrayGroups = Object.values(partition.groups).sort((a, b) => {
|
let arrayGroups = Object.values(partition.groups).sort((a, b) => {
|
||||||
return a.numero - b.numero;
|
return a.numero - b.numero;
|
||||||
@ -146,9 +146,6 @@
|
|||||||
</div>`;
|
</div>`;
|
||||||
})
|
})
|
||||||
|
|
||||||
let hiden = document.createElement("div");
|
|
||||||
hiden.className = "hidenDropZone";
|
|
||||||
divFiltres.append(hiden);
|
|
||||||
document.querySelector("#zoneGroupes>.groupes").innerHTML = outputGroupes;
|
document.querySelector("#zoneGroupes>.groupes").innerHTML = outputGroupes;
|
||||||
|
|
||||||
/* Etudiants */
|
/* Etudiants */
|
||||||
@ -250,23 +247,15 @@
|
|||||||
let div = document.createElement("button");
|
let div = document.createElement("button");
|
||||||
div.classList.add("dt-button");
|
div.classList.add("dt-button");
|
||||||
div.dataset.idgroupe = groupe.id;
|
div.dataset.idgroupe = groupe.id;
|
||||||
div.dataset.idedt = groupe.edt_id || "";
|
let edt_id_str = groupe.edt_id ? `<tt class="edt_id" title="id edt">[${groupe.edt_id}]</tt>` : "";
|
||||||
let title_EDT = `Identifiant EDT: ${groupe.edt_id || groupe.group_name}`;
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<span class="editing move">||</span>
|
<span class="editing move">||</span>
|
||||||
<span>${groupe.group_name}</span>
|
<span>${groupe.group_name} ${edt_id_str}</span>
|
||||||
<span class="editing rename">✏️</span>
|
<span class="editing rename"><a href="/ScoDoc/{{formsemestre.departement.acronym}}/Scolarite/group_rename?group_id=${groupe.id}">✏️</a></span>
|
||||||
<span class="editing calendarEdit" title="${title_EDT}">📅</span>
|
|
||||||
<span class="editing suppr">❌</span>`;
|
<span class="editing suppr">❌</span>`;
|
||||||
|
|
||||||
if (title_EDT) {
|
|
||||||
div.querySelector(".calendarEdit").classList.add("actif");
|
|
||||||
}
|
|
||||||
|
|
||||||
div.addEventListener("click", filtre);
|
div.addEventListener("click", filtre);
|
||||||
div.querySelector(".move").addEventListener("mousedown", moveStart);
|
div.querySelector(".move").addEventListener("mousedown", moveStart);
|
||||||
div.querySelector(".rename").addEventListener("click", editText);
|
|
||||||
div.querySelector(".calendarEdit").addEventListener("click", editCalendar);
|
|
||||||
div.querySelector(".suppr").addEventListener("click", suppr);
|
div.querySelector(".suppr").addEventListener("click", suppr);
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
@ -286,7 +275,7 @@
|
|||||||
function listeGroupesAutoaffectation() {
|
function listeGroupesAutoaffectation() {
|
||||||
let output = '<option value disabled selected hidden>Choisir</option>';
|
let output = '<option value disabled selected hidden>Choisir</option>';
|
||||||
|
|
||||||
document.querySelectorAll('#zonePartitions .filtres>[data-idpartition]').forEach(partition => {
|
document.querySelectorAll('#zonePartitions .filtres>div').forEach(partition => {
|
||||||
|
|
||||||
output += `
|
output += `
|
||||||
<optgroup label="${partition.children[0].children[1].innerText}">
|
<optgroup label="${partition.children[0].children[1].innerText}">
|
||||||
@ -766,63 +755,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************/
|
|
||||||
/* Edition de l'id_EDT */
|
|
||||||
/***********************/
|
|
||||||
function editCalendar() {
|
|
||||||
|
|
||||||
let data = `data-idgroupe="${this.parentElement.dataset.idgroupe}"`;
|
|
||||||
let nom = this.previousElementSibling.previousElementSibling.innerText;
|
|
||||||
let id_EDT = this.parentElement.dataset.idedt;
|
|
||||||
|
|
||||||
let div = document.createElement("div");
|
|
||||||
div.className = "confirm";
|
|
||||||
div.innerHTML = `
|
|
||||||
<div>
|
|
||||||
<h1>Modifier l'id EDT du groupe <span>${nom}</span></h1>
|
|
||||||
|
|
||||||
<input value="${id_EDT}">
|
|
||||||
<p>Optionnel : identifiant du groupe dans le logiciel d'emploi du temps, pour le cas où les noms de groupes ne seraient pas les mêmes dans ScoDoc et dans l'emploi du temps (si plusieurs ids de groupes EDT doivent correspondre au même groupe ScoDoc, les séparer par des virgules).</p>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="ok" ${data}>Valider</div>
|
|
||||||
<div class="nok">Annuler</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
document.body.append(div);
|
|
||||||
document.querySelector(".ok").addEventListener("click", editCalConfirm);
|
|
||||||
document.querySelector(".nok").addEventListener("click", closeConfirm);
|
|
||||||
}
|
|
||||||
|
|
||||||
function editCalConfirm() {
|
|
||||||
let idGroupe = this.dataset.idgroupe;
|
|
||||||
let id_EDT = this.parentElement.parentElement.querySelector("input").value;
|
|
||||||
|
|
||||||
let btnGroupe = document.querySelector(`#zonePartitions .groupes [data-idgroupe="${idGroupe}"]`);
|
|
||||||
btnGroupe.dataset.idedt = id_EDT;
|
|
||||||
btnGroupe.querySelector(".calendarEdit").title = id_EDT || "";
|
|
||||||
|
|
||||||
if (id_EDT) {
|
|
||||||
btnGroupe.querySelector(".calendarEdit").classList.add("actif");
|
|
||||||
} else {
|
|
||||||
btnGroupe.querySelector(".calendarEdit").classList.remove("actif");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Save
|
|
||||||
let url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/group/${idGroupe}/set_edt_id/${id_EDT}`;
|
|
||||||
|
|
||||||
fetch(url, { method: "POST" })
|
|
||||||
.then(r => { return r.json() })
|
|
||||||
.then(r => {
|
|
||||||
if (r.id != idGroupe) {
|
|
||||||
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (5).</h2>";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
closeConfirm();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************************/
|
/*********************************/
|
||||||
/* Suppression parcours / groupe */
|
/* Suppression parcours / groupe */
|
||||||
/*********************************/
|
/*********************************/
|
||||||
@ -870,7 +802,7 @@
|
|||||||
.then(r => { return r.json() })
|
.then(r => { return r.json() })
|
||||||
.then(r => {
|
.then(r => {
|
||||||
if (r.OK != true) {
|
if (r.OK != true) {
|
||||||
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (6).</h2>";
|
document.querySelector("main").innerHTML = "<h2>Une erreur s'est produite lors de la sauvegarde des données (5).</h2>";
|
||||||
}
|
}
|
||||||
listeGroupesAutoaffectation();
|
listeGroupesAutoaffectation();
|
||||||
})
|
})
|
||||||
@ -936,7 +868,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Save positions
|
// Save positions
|
||||||
if (this.dataset.idpartition || this.classList.contains("hidenDropZone")) {
|
if (this.dataset.idpartition) {
|
||||||
let params = (new URL(document.location)).searchParams;
|
let params = (new URL(document.location)).searchParams;
|
||||||
let formsemestre_id = params.get('formsemestre_id');
|
let formsemestre_id = params.get('formsemestre_id');
|
||||||
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/${formsemestre_id}/partitions/order`;
|
var url = `/ScoDoc/{{formsemestre.departement.acronym}}/api/formsemestre/${formsemestre_id}/partitions/order`;
|
||||||
|
@ -209,6 +209,4 @@ 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, log
|
from app import db
|
||||||
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,8 +332,6 @@ 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():
|
||||||
@ -382,20 +380,16 @@ 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("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("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]
|
||||||
|
|
||||||
|
@ -120,14 +120,6 @@ def test_formsemestre_partition(api_headers):
|
|||||||
assert group["group_name"] == group_d["group_name"]
|
assert group["group_name"] == group_d["group_name"]
|
||||||
assert group["edt_id"] == "GEDT2"
|
assert group["edt_id"] == "GEDT2"
|
||||||
|
|
||||||
# Change edt_id via route dédiée:
|
|
||||||
group_t = POST_JSON(
|
|
||||||
f"/group/{group_r['id']}/set_edt_id/GEDT3",
|
|
||||||
headers=headers,
|
|
||||||
)
|
|
||||||
assert group_t["id"] == group_r["id"]
|
|
||||||
assert group_t["edt_id"] == "GEDT3"
|
|
||||||
|
|
||||||
# Place un étudiant dans le groupe
|
# Place un étudiant dans le groupe
|
||||||
etud = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=headers)[0]
|
etud = GET(f"/formsemestre/{formsemestre_id}/etudiants", headers=headers)[0]
|
||||||
repl = POST_JSON(f"/group/{group['id']}/set_etudiant/{etud['id']}", headers=headers)
|
repl = POST_JSON(f"/group/{group['id']}/set_etudiant/{etud['id']}", headers=headers)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user