forked from ScoDoc/ScoDoc
form inscription/desinscription à toutes les UEs du BUT
This commit is contained in:
parent
79bdf03caa
commit
9a4b78be16
@ -122,6 +122,10 @@ def formsemestre_get_ue_capitalisees(formsemestre: FormSemestre) -> pd.DataFrame
|
|||||||
event_date :
|
event_date :
|
||||||
} ]
|
} ]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Note: pour récupérer aussi les UE validées en CMp ou ADJ, changer une ligne
|
||||||
|
# and ( SFV.code = 'ADM' or SFV.code = 'ADJ' or SFV.code = 'CMP' )
|
||||||
|
|
||||||
query = """
|
query = """
|
||||||
SELECT DISTINCT SFV.*, ue.ue_code
|
SELECT DISTINCT SFV.*, ue.ue_code
|
||||||
FROM
|
FROM
|
||||||
|
@ -35,7 +35,7 @@ from flask_login import current_user
|
|||||||
|
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre, Identite, UniteEns
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
@ -297,7 +297,13 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
|||||||
options.append(modimpl)
|
options.append(modimpl)
|
||||||
|
|
||||||
# Page HTML:
|
# Page HTML:
|
||||||
H = [html_sco_header.html_sem_header("Inscriptions aux modules du semestre")]
|
H = [
|
||||||
|
html_sco_header.html_sem_header(
|
||||||
|
"Inscriptions aux modules et UE du semestre",
|
||||||
|
javascripts=["js/etud_info.js", "js/moduleimpl_inscriptions_stats.js"],
|
||||||
|
init_qtip=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
H.append(f"<h3>Inscrits au semestre: {len(inscrits)} étudiants</h3>")
|
H.append(f"<h3>Inscrits au semestre: {len(inscrits)} étudiants</h3>")
|
||||||
|
|
||||||
@ -393,7 +399,9 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
|||||||
# Etudiants "dispensés" d'une UE (capitalisée)
|
# Etudiants "dispensés" d'une UE (capitalisée)
|
||||||
ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id)
|
ues_cap_info = get_etuds_with_capitalized_ue(formsemestre_id)
|
||||||
if ues_cap_info:
|
if ues_cap_info:
|
||||||
H.append('<h3>Étudiants avec UEs capitalisées:</h3><ul class="ue_inscr_list">')
|
H.append(
|
||||||
|
'<h3>Étudiants avec UEs capitalisées (ADM):</h3><ul class="ue_inscr_list">'
|
||||||
|
)
|
||||||
ues = [
|
ues = [
|
||||||
sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys()
|
sco_edit_ue.ue_list({"ue_id": ue_id})[0] for ue_id in ues_cap_info.keys()
|
||||||
]
|
]
|
||||||
@ -461,20 +469,24 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
|||||||
if can_change:
|
if can_change:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<div><a class="stdlink" href="{
|
f"""<div><a class="stdlink" href="{
|
||||||
url_for("notes.etud_inscrit_ue", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
|
url_for("notes.etud_inscrit_ue",
|
||||||
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
|
scodoc_dept=g.scodoc_dept, etudid=etud["etudid"],
|
||||||
|
formsemestre_id=formsemestre_id, ue_id=ue["ue_id"])
|
||||||
}">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div>
|
}">inscrire à {"" if is_apc else "tous les modules de"} cette UE</a></div>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
H.append("</ul></li>")
|
H.append("</ul></li>")
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
|
# BUT: propose dispense de toutes UEs
|
||||||
|
if is_apc:
|
||||||
|
H.append(_list_but_ue_inscriptions(res, read_only=not can_change))
|
||||||
|
|
||||||
H.append(
|
H.append(
|
||||||
"""<hr/><p class="help">Cette page décrit les inscriptions actuelles.
|
"""<hr/><p class="help">Cette page décrit les inscriptions actuelles.
|
||||||
Vous pouvez changer (si vous en avez le droit) les inscrits dans chaque module en
|
Vous pouvez changer (si vous en avez le droit) les inscrits dans chaque module en
|
||||||
cliquant sur la ligne du module.</p>
|
cliquant sur la ligne du module.</p>
|
||||||
<p class="help">Note: la déinscription d'un module ne perd pas les notes. Ainsi, si
|
<p class="help">Note: la déinscription d'un module ne perd pas les notes. Ainsi, si
|
||||||
l'étudiant est ensuite réinscrit au même module, il retrouvera ses notes.</p>
|
l'étudiant est ensuite réinscrit au même module, il retrouvera ses notes.</p>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
@ -483,6 +495,93 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _list_but_ue_inscriptions(res: NotesTableCompat, read_only: bool = True) -> str:
|
||||||
|
"""HTML pour dispenser/reinscrire chaque étudiant à chaque UE du BUT"""
|
||||||
|
H = [
|
||||||
|
"""
|
||||||
|
<div class="list_but_ue_inscriptions">
|
||||||
|
<h3>Inscriptions/déinscription aux UEs du BUT</h3>
|
||||||
|
<form class="list_but_ue_inscriptions">
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
table_inscr = _table_but_ue_inscriptions(res)
|
||||||
|
ue_ids = set.union(*(set(x.keys()) for x in table_inscr.values()))
|
||||||
|
ues = sorted(
|
||||||
|
(UniteEns.query.get(ue_id) for ue_id in ue_ids),
|
||||||
|
key=lambda u: (u.numero or 0, u.acronyme),
|
||||||
|
)
|
||||||
|
H.append("""<table><tr><th></th>""")
|
||||||
|
for ue in ues:
|
||||||
|
H.append(f"""<th title="{ue.titre or ''}">{ue.acronyme}</th>""")
|
||||||
|
H.append("""</tr>""")
|
||||||
|
|
||||||
|
for etudid, ues_etud in table_inscr.items():
|
||||||
|
etud: Identite = Identite.query.get(etudid)
|
||||||
|
H.append(
|
||||||
|
f"""<tr><td><a class="discretelink etudinfo" id={etudid}
|
||||||
|
href="{url_for(
|
||||||
|
"scolar.ficheEtud",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
etudid=etudid,
|
||||||
|
)}"
|
||||||
|
>{etud.nomprenom}</a></td>"""
|
||||||
|
)
|
||||||
|
for ue in ues:
|
||||||
|
est_inscr = ues_etud.get(ue.id) # None si pas concerné
|
||||||
|
if est_inscr is None:
|
||||||
|
content = ""
|
||||||
|
else:
|
||||||
|
content = f"""<input type="checkbox"
|
||||||
|
{'checked' if est_inscr else ''}
|
||||||
|
{'disabled' if read_only else ''}
|
||||||
|
title="{etud.nomprenom} {'inscrit' if est_inscr else 'non inscrit'} à l'UE {ue.acronyme}",
|
||||||
|
onchange="change_ue_inscr(this);"
|
||||||
|
data-url_inscr={
|
||||||
|
url_for("notes.etud_inscrit_ue",
|
||||||
|
scodoc_dept=g.scodoc_dept, etudid=etudid,
|
||||||
|
formsemestre_id=res.formsemestre.id, ue_id=ue.id)
|
||||||
|
}
|
||||||
|
data-url_desinscr={
|
||||||
|
url_for("notes.etud_desinscrit_ue",
|
||||||
|
scodoc_dept=g.scodoc_dept, etudid=etudid,
|
||||||
|
formsemestre_id=res.formsemestre.id, ue_id=ue.id)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
"""
|
||||||
|
|
||||||
|
H.append(f"""<td>{content}</td>""")
|
||||||
|
H.append(
|
||||||
|
"""</table>
|
||||||
|
</form>
|
||||||
|
<div class="help">
|
||||||
|
L'inscription ou désinscription aux UE du BUT n'affecte pas les inscriptions aux modules
|
||||||
|
mais permet de "dispenser" un étudiant de suivre certaines UE de son parcours.
|
||||||
|
Il peut s'agit d'étudiants redoublants ayant déjà acquis l'UE, ou d'autres cas particuliers.
|
||||||
|
La dispense d'UE est réversible à tout moment (avant le jury de fin de semestre)
|
||||||
|
et n'affecte pas les notes saisies.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _table_but_ue_inscriptions(res: NotesTableCompat) -> dict[int, dict]:
|
||||||
|
""" "table" avec les inscriptions aux UEs de chaque étudiant
|
||||||
|
{
|
||||||
|
etudid : { ue_id : True | False }
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
etudid: {
|
||||||
|
ue_id: (etudid, ue_id) not in res.dispense_ues
|
||||||
|
for ue_id in res.etud_ues_ids(etudid)
|
||||||
|
}
|
||||||
|
for etudid, inscr in res.formsemestre.etuds_inscriptions.items()
|
||||||
|
if inscr.etat == scu.INSCRIT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def descr_inscrs_module(moduleimpl_id, set_all, partitions):
|
def descr_inscrs_module(moduleimpl_id, set_all, partitions):
|
||||||
"""returns tous_inscrits, nb_inscrits, descr"""
|
"""returns tous_inscrits, nb_inscrits, descr"""
|
||||||
ins = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
|
ins = sco_moduleimpl.do_moduleimpl_inscription_list(moduleimpl_id=moduleimpl_id)
|
||||||
|
@ -1955,6 +1955,56 @@ span.eval_coef_ue {
|
|||||||
|
|
||||||
span.eval_coef_ue_titre {}
|
span.eval_coef_ue_titre {}
|
||||||
|
|
||||||
|
/* Inscriptions modules/UE */
|
||||||
|
div.list_but_ue_inscriptions {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 2px solid black;
|
||||||
|
background-color: #eafffa;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list_but_ue_inscriptions h3 {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list_but_ue_inscriptions table th:first-child {
|
||||||
|
border-left: 0px;
|
||||||
|
border-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list_but_ue_inscriptions table th:last-child,
|
||||||
|
div.list_but_ue_inscriptions table td:last-child {
|
||||||
|
border-right: 1px solid salmon;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list_but_ue_inscriptions table th {
|
||||||
|
border-top: 1px solid salmon;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.list_but_ue_inscriptions table th,
|
||||||
|
div.list_but_ue_inscriptions table td {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
border-left: 1px solid salmon;
|
||||||
|
border-bottom: 1px solid salmon;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.list_but_ue_inscriptions {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.list_but_ue_inscriptions td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.list_but_ue_inscriptions td {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Formulaire edition des partitions */
|
/* Formulaire edition des partitions */
|
||||||
form#editpart table {
|
form#editpart table {
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
|
17
app/static/js/moduleimpl_inscriptions_stats.js
Normal file
17
app/static/js/moduleimpl_inscriptions_stats.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/* Page Inscriptions aux modules et UE du semestre
|
||||||
|
*/
|
||||||
|
|
||||||
|
function change_ue_inscr(elt) {
|
||||||
|
let url = "";
|
||||||
|
if (elt.checked) {
|
||||||
|
url = elt.dataset.url_inscr;
|
||||||
|
} else {
|
||||||
|
url = elt.dataset.url_desinscr;
|
||||||
|
}
|
||||||
|
$.post(url,
|
||||||
|
{},
|
||||||
|
function (result) {
|
||||||
|
sco_message("changement inscription UE enregistré");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
@ -50,7 +50,7 @@ from app.but import jury_but_view
|
|||||||
|
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import ScolarNews
|
from app.models import ScolarNews, Scolog
|
||||||
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
from app.models.but_refcomp import ApcNiveau, ApcParcours
|
||||||
from app.models.config import ScoDocSiteConfig
|
from app.models.config import ScoDocSiteConfig
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
@ -1600,10 +1600,12 @@ sco_publish(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etud_desinscrit_ue")
|
@bp.route(
|
||||||
|
"/etud_desinscrit_ue/<int:etudid>/<int:formsemestre_id>/<int:ue_id>",
|
||||||
|
methods=["GET", "POST"],
|
||||||
|
)
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoEtudInscrit)
|
@permission_required(Permission.ScoEtudInscrit)
|
||||||
@scodoc7func
|
|
||||||
def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
||||||
"""
|
"""
|
||||||
- En classique: désinscrit l'etudiant de tous les modules de cette UE dans ce semestre.
|
- En classique: désinscrit l'etudiant de tous les modules de cette UE dans ce semestre.
|
||||||
@ -1617,6 +1619,13 @@ def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
|||||||
disp = DispenseUE(ue_id=ue_id, etudid=etudid)
|
disp = DispenseUE(ue_id=ue_id, etudid=etudid)
|
||||||
db.session.add(disp)
|
db.session.add(disp)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
log(f"etud_desinscrit_ue {etud} {ue}")
|
||||||
|
Scolog.logdb(
|
||||||
|
method="etud_desinscrit_ue",
|
||||||
|
etudid=etud.id,
|
||||||
|
msg=f"Désinscription de l'UE {ue.acronyme}",
|
||||||
|
commit=True,
|
||||||
|
)
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||||
else:
|
else:
|
||||||
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(
|
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(
|
||||||
@ -1632,10 +1641,12 @@ def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/etud_inscrit_ue")
|
@bp.route(
|
||||||
|
"/etud_inscrit_ue/<int:etudid>/<int:formsemestre_id>/<int:ue_id>",
|
||||||
|
methods=["GET", "POST"],
|
||||||
|
)
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoEtudInscrit)
|
@permission_required(Permission.ScoEtudInscrit)
|
||||||
@scodoc7func
|
|
||||||
def etud_inscrit_ue(etudid, formsemestre_id, ue_id):
|
def etud_inscrit_ue(etudid, formsemestre_id, ue_id):
|
||||||
"""
|
"""
|
||||||
En classic: inscrit l'étudiant à tous les modules de cette UE dans ce semestre.
|
En classic: inscrit l'étudiant à tous les modules de cette UE dans ce semestre.
|
||||||
@ -1649,6 +1660,13 @@ def etud_inscrit_ue(etudid, formsemestre_id, ue_id):
|
|||||||
if ue.formation.is_apc():
|
if ue.formation.is_apc():
|
||||||
for disp in DispenseUE.query.filter_by(etudid=etud.id, ue_id=ue_id):
|
for disp in DispenseUE.query.filter_by(etudid=etud.id, ue_id=ue_id):
|
||||||
db.session.delete(disp)
|
db.session.delete(disp)
|
||||||
|
log(f"etud_inscrit_ue {etud} {ue}")
|
||||||
|
Scolog.logdb(
|
||||||
|
method="etud_inscrit_ue",
|
||||||
|
etudid=etud.id,
|
||||||
|
msg=f"Inscription à l'UE {ue.acronyme}",
|
||||||
|
commit=True,
|
||||||
|
)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||||
else:
|
else:
|
||||||
|
@ -34,6 +34,7 @@ from app.views import ScoData
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def refcomp(refcomp_id):
|
def refcomp(refcomp_id):
|
||||||
|
"""Le référentiel de compétences, en JSON."""
|
||||||
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
||||||
return jsonify(ref.to_dict())
|
return jsonify(ref.to_dict())
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ def refcomp(refcomp_id):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def refcomp_show(refcomp_id):
|
def refcomp_show(refcomp_id):
|
||||||
|
"""Affichage du référentiel de compétences."""
|
||||||
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
||||||
return render_template(
|
return render_template(
|
||||||
"but/refcomp_show.html",
|
"but/refcomp_show.html",
|
||||||
@ -60,6 +62,7 @@ def refcomp_show(refcomp_id):
|
|||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoChangeFormation)
|
@permission_required(Permission.ScoChangeFormation)
|
||||||
def refcomp_delete(refcomp_id):
|
def refcomp_delete(refcomp_id):
|
||||||
|
"""Suppression du référentiel de la base. Le fichier source n'est pas affecté."""
|
||||||
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
ref = ApcReferentielCompetences.query.get_or_404(refcomp_id)
|
||||||
db.session.delete(ref)
|
db.session.delete(ref)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[pytest]
|
[pytest]
|
||||||
markers =
|
markers =
|
||||||
slow: marks tests as slow (deselect with '-m "not slow"')
|
slow: marks tests as slow (deselect with '-m "not slow"')
|
||||||
|
but_gb
|
||||||
lemans
|
lemans
|
||||||
lyon
|
lyon
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ DEPT = TestConfig.DEPT_TEST
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.slow
|
@pytest.mark.slow
|
||||||
|
@pytest.mark.but_gb
|
||||||
def test_but_jury_GB(test_client):
|
def test_but_jury_GB(test_client):
|
||||||
"""Tests sur un cursus GB
|
"""Tests sur un cursus GB
|
||||||
- construction des semestres et de leurs étudianst à partir du yaml
|
- construction des semestres et de leurs étudianst à partir du yaml
|
||||||
|
Loading…
Reference in New Issue
Block a user