Début de travaux pour améliorer le backend groupes/partitions.

This commit is contained in:
Emmanuel Viennet 2023-07-05 19:15:33 +02:00
parent 90c1454b21
commit 0824598aa4
7 changed files with 91 additions and 78 deletions

View File

@ -12,6 +12,7 @@ from operator import attrgetter
from flask import g, request
from flask_json import as_json
from flask_login import login_required
from sqlalchemy.exc import IntegrityError
import app
from app import db, log
@ -23,6 +24,7 @@ from app.models import GroupDescr, Partition, Scolog
from app.models.groups import group_membership
from app.scodoc import sco_cache
from app.scodoc import sco_groups
from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission
from app.scodoc import sco_utils as scu
@ -182,10 +184,12 @@ def set_etud_group(etudid: int, group_id: int):
if etud.id not in {e.id for e in group.partition.formsemestre.etuds}:
return json_error(404, "etud non inscrit au formsemestre du groupe")
sco_groups.change_etud_group_in_partition(
etudid, group_id, group.partition.to_dict()
)
try:
sco_groups.change_etud_group_in_partition(etudid, group)
except ScoValueError as exc:
return json_error(404, exc.args[0])
except IntegrityError:
return json_error(404, "échec de l'enregistrement")
return {"group_id": group_id, "etudid": etudid}

View File

@ -8,11 +8,13 @@
"""ScoDoc models: Groups & partitions
"""
from operator import attrgetter
from sqlalchemy.exc import IntegrityError
from app import db
from app.models import SHORT_STR_LEN
from app.models import GROUPNAME_STR_LEN
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import ScoValueError
class Partition(db.Model):
@ -117,6 +119,40 @@ class Partition(db.Model):
.first()
)
def set_etud_group(self, etudid: int, group: "GroupDescr"):
"""Affect etudid to group_id in given partition.
Raises IntegrityError si conflit,
or ValueError si ce group_id n'est pas dans cette partition
ou que l'étudiant n'est pas inscrit au semestre.
"""
if not group.id in (g.id for g in self.groups):
raise ScoValueError(
f"""Le groupe {group.id} n'est pas dans la partition {self.partition_name or "tous"}"""
)
if etudid not in (e.id for e in self.formsemestre.etuds):
raise ScoValueError(
f"etudiant {etudid} non inscrit au formsemestre du groupe {group.id}"
)
try:
existing_row = (
db.session.query(group_membership)
.filter_by(etudid=etudid)
.join(GroupDescr)
.filter_by(partition_id=self.id)
.first()
)
if existing_row:
existing_row.update({"group_id": group.id})
else:
new_row = group_membership.insert().values(
etudid=etudid, group_id=group.id
)
db.session.execute(new_row)
db.session.commit()
except IntegrityError:
db.session.rollback()
raise
class GroupDescr(db.Model):
"""Description d'un groupe d'une partition"""

View File

@ -546,6 +546,8 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
ue = UniteEns.query.get(ue_id)
flash(f"UE créée (code {ue.ue_code})")
else:
if not tf[2]["numero"]:
tf[2]["numero"] = 0
do_ue_edit(tf[2])
flash("UE modifiée")

View File

@ -48,9 +48,9 @@ from sqlalchemy.sql import text
from app import db
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre, Identite
from app.models import FormSemestre, Identite, Scolog
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
from app.models.groups import GroupDescr, Partition
from app.models.groups import GroupDescr, Partition, group_membership
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import log, cache
@ -94,7 +94,7 @@ groupEditor = ndb.EditableTable(
group_list = groupEditor.list
def get_group(group_id: int) -> dict:
def get_group(group_id: int) -> dict: # OBSOLETE !
"""Returns group object, with partition"""
r = ndb.SimpleDictFetch(
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
@ -124,7 +124,7 @@ def group_delete(group_id: int):
)
def get_partition(partition_id):
def get_partition(partition_id): # OBSOLETE
r = ndb.SimpleDictFetch(
"""SELECT p.id AS partition_id, p.*
FROM partition p
@ -200,7 +200,7 @@ def get_formsemestre_etuds_groups(formsemestre_id: int) -> dict:
return d
def get_partition_groups(partition):
def get_partition_groups(partition): # OBSOLETE !
"""List of groups in this partition (list of dicts).
Some groups may be empty."""
return ndb.SimpleDictFetch(
@ -637,7 +637,7 @@ def _comp_etud_origin(etud: dict, cur_formsemestre: FormSemestre):
return "" # parcours normal, ne le signale pas
def set_group(etudid: int, group_id: int) -> bool:
def set_group(etudid: int, group_id: int) -> bool: # OBSOLETE !
"""Inscrit l'étudiant au groupe.
Return True if ok, False si deja inscrit.
Warning:
@ -664,55 +664,31 @@ def set_group(etudid: int, group_id: int) -> bool:
return True
def change_etud_group_in_partition(etudid: int, group_id: int, partition: dict = None):
"""Inscrit etud au groupe de cette partition,
et le desinscrit d'autres groupes de cette partition.
def change_etud_group_in_partition(etudid: int, group: GroupDescr):
"""Inscrit etud au groupe
(et le desinscrit d'autres groupes de cette partition.)
"""
log("change_etud_group_in_partition: etudid=%s group_id=%s" % (etudid, group_id))
# 0- La partition
group = get_group(group_id)
if partition:
# verifie que le groupe est bien dans cette partition:
if group["partition_id"] != partition["partition_id"]:
raise ValueError(
"inconsistent group/partition (group_id=%s, partition_id=%s)"
% (group_id, partition["partition_id"])
)
else:
partition = get_partition(group["partition_id"])
# 1- Supprime membership dans cette partition
ndb.SimpleQuery(
"""DELETE FROM group_membership gm
WHERE EXISTS
(SELECT 1 FROM group_descr gd
WHERE gm.etudid = %(etudid)s
AND gm.group_id = gd.id
AND gd.partition_id = %(partition_id)s)
""",
{"etudid": etudid, "partition_id": partition["partition_id"]},
)
# 2- associe au nouveau groupe
set_group(etudid, group_id)
log(f"change_etud_group_in_partition: etudid={etudid} group={group}")
# 3- log
formsemestre_id = partition["formsemestre_id"]
cnx = ndb.GetDBConnexion()
logdb(
cnx,
group.partition.set_etud_group(etudid, group)
# - log
formsemestre: FormSemestre = group.partition.formsemestre
Scolog.logdb(
method="changeGroup",
etudid=etudid,
msg="formsemestre_id=%s,partition_name=%s, group_name=%s"
% (formsemestre_id, partition["partition_name"], group["group_name"]),
msg=f"""formsemestre_id={formsemestre.id}, partition_name={
group.partition.partition_name or ""}, group_name={group.group_name or ""}""",
commit=True,
)
cnx.commit()
# 5- Update parcours
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
formsemestre.update_inscriptions_parcours_from_groups()
# - Update parcours
if group.partition.partition_name == scu.PARTITION_PARCOURS:
formsemestre.update_inscriptions_parcours_from_groups()
# 6- invalidate cache
# - invalidate cache
sco_cache.invalidate_formsemestre(
formsemestre_id=formsemestre_id
formsemestre_id=formsemestre.id
) # > change etud group
@ -769,7 +745,7 @@ def setGroups(
except ValueError:
log(f"setGroups: ignoring invalid group_id={group_id}")
continue
group = get_group(group_id)
group: GroupDescr = GroupDescr.query.get(group_id)
# Anciens membres du groupe:
old_members = get_group_members(group_id)
old_members_set = set([x["etudid"] for x in old_members])
@ -783,7 +759,7 @@ def setGroups(
if (etudid not in etud_groups) or (
group_id != etud_groups[etudid].get(partition_id, "")
): # pas le meme groupe qu'actuel
change_etud_group_in_partition(etudid, group_id, partition)
change_etud_group_in_partition(etudid, group)
# Retire les anciens membres:
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@ -819,7 +795,7 @@ def setGroups(
return xml_error(msg, code=404)
# Place dans ce groupe les etudiants indiqués:
for etudid in fs[1:-1]:
change_etud_group_in_partition(etudid, group.id, partition)
change_etud_group_in_partition(etudid, group.id)
# Update parcours
formsemestre.update_inscriptions_parcours_from_groups()
@ -1460,10 +1436,10 @@ def groups_auto_repartition(partition_id=None):
for old_group in get_partition_groups(partition):
group_delete(old_group["group_id"])
# Crée les nouveaux groupes
group_ids = []
groups = []
for group_name in group_names:
if group_name.strip():
group_ids.append(create_group(partition_id, group_name).id)
groups.append(create_group(partition_id, group_name))
#
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
identdict = nt.identdict
@ -1481,16 +1457,16 @@ def groups_auto_repartition(partition_id=None):
# affect aux groupes:
n = len(identdict)
igroup = 0
nbgroups = len(group_ids)
nbgroups = len(groups)
while n > 0:
for civilite in civilites:
if len(listes[civilite]):
n -= 1
etudid = listes[civilite].pop()[1]
group_id = group_ids[igroup]
group = groups[igroup]
igroup = (igroup + 1) % nbgroups
change_etud_group_in_partition(etudid, group_id, partition)
log("%s in group %s" % (etudid, group_id))
change_etud_group_in_partition(etudid, group)
log("%s in group %s" % (etudid, group.id))
return flask.redirect(dest_url)
@ -1520,10 +1496,11 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
Si la partition existe déjà, ses groupes sont mis à jour (les groupes devenant
vides ne sont pas supprimés).
"""
# A RE-ECRIRE pour utiliser les modèles.
from app.scodoc import sco_formsemestre_inscriptions
partition_name = str(partition_name)
log("create_etapes_partition(%s)" % formsemestre_id)
log(f"create_etapes_partition({formsemestre_id})")
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
args={"formsemestre_id": formsemestre_id}
)
@ -1542,20 +1519,17 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
pid = partition_create(
formsemestre_id, partition_name=partition_name, redirect=False
)
partition = get_partition(pid)
groups = get_partition_groups(partition)
partition: Partition = Partition.query.get(pid)
groups = partition.groups
groups_by_names = {g["group_name"]: g for g in groups}
for etape in etapes:
if not (etape in groups_by_names):
if etape not in groups_by_names:
new_group = create_group(pid, etape)
g = get_group(new_group.id) # XXX transition: recupere old style dict
groups_by_names[etape] = g
groups_by_names[etape] = new_group
# Place les etudiants dans les groupes
for i in ins:
if i["etape"]:
change_etud_group_in_partition(
i["etudid"], groups_by_names[i["etape"]]["group_id"], partition
)
change_etud_group_in_partition(i["etudid"], groups_by_names[i["etape"]])
def do_evaluation_listeetuds_groups(

View File

@ -723,7 +723,7 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
group = GroupDescr.query.get(group_id)
if group.partition.groups_editable:
sco_groups.change_etud_group_in_partition(
args["etudid"], group_id
args["etudid"], group
)
else:
log("scolars_import_admission: partition non editable")

View File

@ -36,13 +36,12 @@ from flask import url_for, g, request
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app import log
from app.models import Formation, FormSemestre
from app.models import Formation, FormSemestre, GroupDescr
from app.scodoc.gen_tables import GenTable
from app.scodoc import html_sco_header
from app.scodoc import sco_cache
from app.scodoc import codes_cursus
from app.scodoc import sco_etud
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups
@ -177,6 +176,7 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
(la liste doit avoir été vérifiée au préalable)
En option: inscrit aux mêmes groupes que dans le semestre origine
"""
# TODO à ré-écrire pour utiliser le smodèle, notamment GroupDescr
formsemestre: FormSemestre = FormSemestre.query.get(sem["formsemestre_id"])
formsemestre.setup_parcours_groups()
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
@ -220,11 +220,8 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
# Inscrit aux groupes
for partition_group in partition_groups:
sco_groups.change_etud_group_in_partition(
etudid,
partition_group["group_id"],
partition_group,
)
group: GroupDescr = GroupDescr.query.get(partition_group["group_id"])
sco_groups.change_etud_group_in_partition(etudid, group)
def do_desinscrit(sem, etudids):

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.4.97"
SCOVERSION = "9.4.98"
SCONAME = "ScoDoc"