forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -12,6 +12,7 @@ from operator import attrgetter
|
|||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_json import as_json
|
from flask_json import as_json
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
import app
|
import app
|
||||||
from app import db, log
|
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.models.groups import group_membership
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc import sco_utils as scu
|
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}:
|
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")
|
return json_error(404, "etud non inscrit au formsemestre du groupe")
|
||||||
|
|
||||||
sco_groups.change_etud_group_in_partition(
|
try:
|
||||||
etudid, group_id, group.partition.to_dict()
|
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}
|
return {"group_id": group_id, "etudid": etudid}
|
||||||
|
|
||||||
|
|
||||||
|
@ -865,10 +865,15 @@ class DecisionsProposeesAnnee(DecisionsProposees):
|
|||||||
self.etud.id, self.formsemestre.id
|
self.etud.id, self.formsemestre.id
|
||||||
)
|
)
|
||||||
for dec_ue in self.decisions_ues.values():
|
for dec_ue in self.decisions_ues.values():
|
||||||
if dec_ue.formsemestre.id == self.formsemestre.id:
|
if (
|
||||||
|
dec_ue
|
||||||
|
and self.formsemestre
|
||||||
|
and dec_ue.formsemestre.id == self.formsemestre.id
|
||||||
|
):
|
||||||
dec_ue.erase()
|
dec_ue.erase()
|
||||||
else:
|
else:
|
||||||
for dec_ue in self.decisions_ues.values():
|
for dec_ue in self.decisions_ues.values():
|
||||||
|
if dec_ue:
|
||||||
dec_ue.erase()
|
dec_ue.erase()
|
||||||
|
|
||||||
if self.formsemestre_impair:
|
if self.formsemestre_impair:
|
||||||
|
@ -827,16 +827,32 @@ class BonusStMalo(BonusIUTRennes1):
|
|||||||
class BonusLaRocheSurYon(BonusSportAdditif):
|
class BonusLaRocheSurYon(BonusSportAdditif):
|
||||||
"""Bonus IUT de La Roche-sur-Yon
|
"""Bonus IUT de La Roche-sur-Yon
|
||||||
|
|
||||||
Si une note de bonus est saisie, l'étudiant est gratifié de 0,2 points
|
<p>
|
||||||
sur sa moyenne générale ou, en BUT, sur la moyenne de chaque UE.
|
<b>La note saisie s'applique directement</b>: si on saisit 0,2, un bonus de 0,2 points est appliqué
|
||||||
|
aux moyennes.
|
||||||
|
La valeur maximale du bonus est 1 point. Il est appliqué sur les moyennes d'UEs en BUT,
|
||||||
|
ou sur la moyenne générale dans les autres formations.
|
||||||
|
</p>
|
||||||
|
<p>Pour les <b>semestres antérieurs à janvier 2023</b>: si une note de bonus est saisie,
|
||||||
|
l'étudiant est gratifié de 0,2 points sur sa moyenne générale ou, en BUT, sur la
|
||||||
|
moyenne de chaque UE.
|
||||||
|
</p>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = "bonus_larochesuryon"
|
name = "bonus_larochesuryon"
|
||||||
displayed_name = "IUT de La Roche-sur-Yon"
|
displayed_name = "IUT de La Roche-sur-Yon"
|
||||||
seuil_moy_gen = 0.0
|
seuil_moy_gen = 0.0
|
||||||
seuil_comptage = 0.0
|
seuil_comptage = 0.0
|
||||||
proportion_point = 1e10 # le moindre point sature le bonus
|
|
||||||
bonus_max = 0.2 # à 0.2
|
def compute_bonus(self, sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan):
|
||||||
|
"""calcul du bonus, avec réglage différent suivant la date"""
|
||||||
|
if self.formsemestre.date_debut > datetime.date(2022, 12, 31):
|
||||||
|
self.proportion_point = 1.0
|
||||||
|
self.bonus_max = 1
|
||||||
|
else: # ancienne règle
|
||||||
|
self.proportion_point = 1e10 # le moindre point sature le bonus
|
||||||
|
self.bonus_max = 0.2 # à 0.2
|
||||||
|
super().compute_bonus(sem_modimpl_moys_inscrits, modimpl_coefs_etuds_no_nan)
|
||||||
|
|
||||||
|
|
||||||
class BonusLaRochelle(BonusSportAdditif):
|
class BonusLaRochelle(BonusSportAdditif):
|
||||||
|
@ -381,7 +381,11 @@ class ResultatsSemestre(ResultatsCache):
|
|||||||
was_capitalized = False
|
was_capitalized = False
|
||||||
if etudid in self.validations.ue_capitalisees.index:
|
if etudid in self.validations.ue_capitalisees.index:
|
||||||
ue_cap = self._get_etud_ue_cap(etudid, ue)
|
ue_cap = self._get_etud_ue_cap(etudid, ue)
|
||||||
if ue_cap and not np.isnan(ue_cap["moy_ue"]):
|
if (
|
||||||
|
ue_cap
|
||||||
|
and (ue_cap["moy_ue"] is not None)
|
||||||
|
and not np.isnan(ue_cap["moy_ue"])
|
||||||
|
):
|
||||||
was_capitalized = True
|
was_capitalized = True
|
||||||
if ue_cap["moy_ue"] > cur_moy_ue or np.isnan(cur_moy_ue):
|
if ue_cap["moy_ue"] > cur_moy_ue or np.isnan(cur_moy_ue):
|
||||||
moy_ue = ue_cap["moy_ue"]
|
moy_ue = ue_cap["moy_ue"]
|
||||||
|
@ -575,6 +575,17 @@ class FormSemestre(db.Model):
|
|||||||
user
|
user
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def can_change_groups(self, user: User = None) -> bool:
|
||||||
|
"""Vrai si l'utilisateur (par def. current) peut changer les groupes dans
|
||||||
|
ce semestre: vérifie permission et verrouillage.
|
||||||
|
"""
|
||||||
|
if not self.etat:
|
||||||
|
return False # semestre verrouillé
|
||||||
|
user = user or current_user
|
||||||
|
if user.has_permission(Permission.ScoEtudChangeGroups):
|
||||||
|
return True # typiquement admin, chef dept
|
||||||
|
return self.est_responsable(user)
|
||||||
|
|
||||||
def can_edit_jury(self, user: User = None):
|
def can_edit_jury(self, user: User = None):
|
||||||
"""Vrai si utilisateur (par def. current) peut saisir decision de jury
|
"""Vrai si utilisateur (par def. current) peut saisir decision de jury
|
||||||
dans ce semestre: vérifie permission et verrouillage.
|
dans ce semestre: vérifie permission et verrouillage.
|
||||||
|
@ -8,11 +8,13 @@
|
|||||||
"""ScoDoc models: Groups & partitions
|
"""ScoDoc models: Groups & partitions
|
||||||
"""
|
"""
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from app import db
|
from app import db, log
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import GROUPNAME_STR_LEN
|
from app.models import GROUPNAME_STR_LEN
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
|
|
||||||
|
|
||||||
class Partition(db.Model):
|
class Partition(db.Model):
|
||||||
@ -117,6 +119,81 @@ class Partition(db.Model):
|
|||||||
.first()
|
.first()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_etud_group(self, etud: "Identite", group: "GroupDescr") -> bool:
|
||||||
|
"""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.
|
||||||
|
Return True si changement, False s'il était déjà dans ce groupe.
|
||||||
|
"""
|
||||||
|
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 etud.id not in (e.id for e in self.formsemestre.etuds):
|
||||||
|
raise ScoValueError(
|
||||||
|
f"""étudiant {etud.nomprenom} non inscrit au formsemestre du groupe {
|
||||||
|
group.group_name}"""
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
existing_row = (
|
||||||
|
db.session.query(group_membership)
|
||||||
|
.filter_by(etudid=etud.id)
|
||||||
|
.join(GroupDescr)
|
||||||
|
.filter_by(partition_id=self.id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if existing_row:
|
||||||
|
existing_group_id = existing_row[1]
|
||||||
|
if group.id == existing_group_id:
|
||||||
|
return False
|
||||||
|
# Fait le changement avec l'ORM sinon risque élevé de blocage
|
||||||
|
existing_group = GroupDescr.query.get(existing_group_id)
|
||||||
|
db.session.commit()
|
||||||
|
group.etuds.append(etud)
|
||||||
|
existing_group.etuds.remove(etud)
|
||||||
|
db.session.add(etud)
|
||||||
|
db.session.add(existing_group)
|
||||||
|
db.session.add(group)
|
||||||
|
else:
|
||||||
|
new_row = group_membership.insert().values(
|
||||||
|
etudid=etud.id, group_id=group.id
|
||||||
|
)
|
||||||
|
db.session.execute(new_row)
|
||||||
|
db.session.commit()
|
||||||
|
except IntegrityError:
|
||||||
|
db.session.rollback()
|
||||||
|
raise
|
||||||
|
return True
|
||||||
|
|
||||||
|
def create_group(self, group_name="", default=False) -> "GroupDescr":
|
||||||
|
"Crée un groupe dans cette partition"
|
||||||
|
if not self.formsemestre.can_change_groups():
|
||||||
|
raise AccessDenied(
|
||||||
|
"""Vous n'avez pas le droit d'effectuer cette opération,
|
||||||
|
ou bien le semestre est verrouillé !"""
|
||||||
|
)
|
||||||
|
if group_name:
|
||||||
|
group_name = group_name.strip()
|
||||||
|
if not group_name and not default:
|
||||||
|
raise ValueError("invalid group name: ()")
|
||||||
|
if not GroupDescr.check_name(self, group_name, default=default):
|
||||||
|
raise ScoValueError(
|
||||||
|
f"Le groupe {group_name} existe déjà dans cette partition"
|
||||||
|
)
|
||||||
|
numeros = [g.numero if g.numero is not None else 0 for g in self.groups]
|
||||||
|
if len(numeros) > 0:
|
||||||
|
new_numero = max(numeros) + 1
|
||||||
|
else:
|
||||||
|
new_numero = 0
|
||||||
|
group = GroupDescr(partition=self, group_name=group_name, numero=new_numero)
|
||||||
|
db.session.add(group)
|
||||||
|
db.session.commit()
|
||||||
|
log(f"create_group: created group_id={group.id}")
|
||||||
|
#
|
||||||
|
return group
|
||||||
|
|
||||||
|
|
||||||
class GroupDescr(db.Model):
|
class GroupDescr(db.Model):
|
||||||
"""Description d'un groupe d'une partition"""
|
"""Description d'un groupe d'une partition"""
|
||||||
|
@ -949,6 +949,7 @@ def do_formsemestre_validate_ue(
|
|||||||
"ue_id": ue_id,
|
"ue_id": ue_id,
|
||||||
"semestre_id": semestre_id,
|
"semestre_id": semestre_id,
|
||||||
"is_external": is_external,
|
"is_external": is_external,
|
||||||
|
"moy_ue": moy_ue,
|
||||||
}
|
}
|
||||||
if date:
|
if date:
|
||||||
args["event_date"] = date
|
args["event_date"] = date
|
||||||
@ -965,12 +966,11 @@ def do_formsemestre_validate_ue(
|
|||||||
cursor.execute("delete from scolar_formsemestre_validation where " + cond, args)
|
cursor.execute("delete from scolar_formsemestre_validation where " + cond, args)
|
||||||
# insert
|
# insert
|
||||||
args["code"] = code
|
args["code"] = code
|
||||||
if code == ADM:
|
if (code == ADM) and (moy_ue is None):
|
||||||
if moy_ue is None:
|
|
||||||
# stocke la moyenne d'UE capitalisée:
|
# stocke la moyenne d'UE capitalisée:
|
||||||
ue_status = nt.get_etud_ue_status(etudid, ue_id)
|
ue_status = nt.get_etud_ue_status(etudid, ue_id)
|
||||||
moy_ue = ue_status["moy"] if ue_status else ""
|
moy_ue = ue_status["moy"] if ue_status else ""
|
||||||
args["moy_ue"] = moy_ue
|
|
||||||
log("formsemestre_validate_ue: create %s" % args)
|
log("formsemestre_validate_ue: create %s" % args)
|
||||||
if code is not None:
|
if code is not None:
|
||||||
scolar_formsemestre_validation_create(cnx, args)
|
scolar_formsemestre_validation_create(cnx, args)
|
||||||
|
@ -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)
|
ue = UniteEns.query.get(ue_id)
|
||||||
flash(f"UE créée (code {ue.ue_code})")
|
flash(f"UE créée (code {ue.ue_code})")
|
||||||
else:
|
else:
|
||||||
|
if not tf[2]["numero"]:
|
||||||
|
tf[2]["numero"] = 0
|
||||||
do_ue_edit(tf[2])
|
do_ue_edit(tf[2])
|
||||||
flash("UE modifiée")
|
flash("UE modifiée")
|
||||||
|
|
||||||
|
@ -793,6 +793,7 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
{tf[1]}
|
{tf[1]}
|
||||||
"""
|
"""
|
||||||
elif tf[0] == -1:
|
elif tf[0] == -1:
|
||||||
|
if formsemestre:
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.formsemestre_status",
|
"notes.formsemestre_status",
|
||||||
@ -800,6 +801,8 @@ def do_formsemestre_createwithmodules(edit=False, formsemestre: FormSemestre = N
|
|||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
||||||
else:
|
else:
|
||||||
if tf[2]["gestion_compensation_lst"]:
|
if tf[2]["gestion_compensation_lst"]:
|
||||||
tf[2]["gestion_compensation"] = True
|
tf[2]["gestion_compensation"] = True
|
||||||
|
@ -34,7 +34,6 @@ Optimisation possible:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
import operator
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
@ -45,15 +44,14 @@ from flask import g, request
|
|||||||
from flask import url_for, make_response
|
from flask import url_for, make_response
|
||||||
from sqlalchemy.sql import text
|
from sqlalchemy.sql import text
|
||||||
|
|
||||||
from app import db
|
from app import cache, db, log
|
||||||
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, Identite
|
from app.models import FormSemestre, Identite, Scolog
|
||||||
from app.models import GROUPNAME_STR_LEN, SHORT_STR_LEN
|
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.sco_utils as scu
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app import log, cache
|
|
||||||
from app.scodoc.scolog import logdb
|
from app.scodoc.scolog import logdb
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
@ -94,7 +92,7 @@ groupEditor = ndb.EditableTable(
|
|||||||
group_list = groupEditor.list
|
group_list = groupEditor.list
|
||||||
|
|
||||||
|
|
||||||
def get_group(group_id: int) -> dict:
|
def get_group(group_id: int) -> dict: # OBSOLETE !
|
||||||
"""Returns group object, with partition"""
|
"""Returns group object, with partition"""
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
"""SELECT gd.id AS group_id, gd.*, p.id AS partition_id, p.*
|
||||||
@ -124,7 +122,7 @@ def group_delete(group_id: int):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_partition(partition_id):
|
def get_partition(partition_id): # OBSOLETE
|
||||||
r = ndb.SimpleDictFetch(
|
r = ndb.SimpleDictFetch(
|
||||||
"""SELECT p.id AS partition_id, p.*
|
"""SELECT p.id AS partition_id, p.*
|
||||||
FROM partition p
|
FROM partition p
|
||||||
@ -200,7 +198,7 @@ def get_formsemestre_etuds_groups(formsemestre_id: int) -> dict:
|
|||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def get_partition_groups(partition):
|
def get_partition_groups(partition): # OBSOLETE !
|
||||||
"""List of groups in this partition (list of dicts).
|
"""List of groups in this partition (list of dicts).
|
||||||
Some groups may be empty."""
|
Some groups may be empty."""
|
||||||
return ndb.SimpleDictFetch(
|
return ndb.SimpleDictFetch(
|
||||||
@ -637,7 +635,7 @@ def _comp_etud_origin(etud: dict, cur_formsemestre: FormSemestre):
|
|||||||
return "" # parcours normal, ne le signale pas
|
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.
|
"""Inscrit l'étudiant au groupe.
|
||||||
Return True if ok, False si deja inscrit.
|
Return True if ok, False si deja inscrit.
|
||||||
Warning:
|
Warning:
|
||||||
@ -664,55 +662,33 @@ def set_group(etudid: int, group_id: int) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def change_etud_group_in_partition(etudid: int, group_id: int, partition: dict = None):
|
def change_etud_group_in_partition(etudid: int, group: GroupDescr) -> bool:
|
||||||
"""Inscrit etud au groupe de cette partition,
|
"""Inscrit etud au groupe
|
||||||
et le desinscrit d'autres groupes de cette partition.
|
(et le désinscrit d'autres groupes de cette partition)
|
||||||
|
Return True si changement, False s'il était déjà dans ce groupe.
|
||||||
"""
|
"""
|
||||||
log("change_etud_group_in_partition: etudid=%s group_id=%s" % (etudid, group_id))
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
# 0- La partition
|
if not group.partition.set_etud_group(etud, group):
|
||||||
group = get_group(group_id)
|
return # pas de changement
|
||||||
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)
|
|
||||||
|
|
||||||
# 3- log
|
# - log
|
||||||
formsemestre_id = partition["formsemestre_id"]
|
formsemestre: FormSemestre = group.partition.formsemestre
|
||||||
cnx = ndb.GetDBConnexion()
|
log(f"change_etud_group_in_partition: etudid={etudid} group={group}")
|
||||||
logdb(
|
Scolog.logdb(
|
||||||
cnx,
|
|
||||||
method="changeGroup",
|
method="changeGroup",
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
msg="formsemestre_id=%s,partition_name=%s, group_name=%s"
|
msg=f"""formsemestre_id={formsemestre.id}, partition_name={
|
||||||
% (formsemestre_id, partition["partition_name"], group["group_name"]),
|
group.partition.partition_name or ""}, group_name={group.group_name or ""}""",
|
||||||
|
commit=True,
|
||||||
)
|
)
|
||||||
cnx.commit()
|
|
||||||
|
|
||||||
# 5- Update parcours
|
# - Update parcours
|
||||||
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
|
if group.partition.partition_name == scu.PARTITION_PARCOURS:
|
||||||
formsemestre.update_inscriptions_parcours_from_groups()
|
formsemestre.update_inscriptions_parcours_from_groups()
|
||||||
|
|
||||||
# 6- invalidate cache
|
# - invalidate cache
|
||||||
sco_cache.invalidate_formsemestre(
|
sco_cache.invalidate_formsemestre(
|
||||||
formsemestre_id=formsemestre_id
|
formsemestre_id=formsemestre.id
|
||||||
) # > change etud group
|
) # > change etud group
|
||||||
|
|
||||||
|
|
||||||
@ -729,7 +705,6 @@ def setGroups(
|
|||||||
|
|
||||||
Ne peux pas modifier les groupes des partitions non éditables.
|
Ne peux pas modifier les groupes des partitions non éditables.
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_formsemestre
|
|
||||||
|
|
||||||
def xml_error(msg, code=404):
|
def xml_error(msg, code=404):
|
||||||
data = (
|
data = (
|
||||||
@ -739,26 +714,27 @@ def setGroups(
|
|||||||
response.headers["Content-Type"] = scu.XML_MIMETYPE
|
response.headers["Content-Type"] = scu.XML_MIMETYPE
|
||||||
return response
|
return response
|
||||||
|
|
||||||
partition = get_partition(partition_id)
|
partition: Partition = Partition.query.get(partition_id)
|
||||||
if not partition["groups_editable"] and (groupsToCreate or groupsToDelete):
|
if not partition.groups_editable and (groupsToCreate or groupsToDelete):
|
||||||
msg = "setGroups: partition non editable"
|
msg = "setGroups: partition non editable"
|
||||||
log(msg)
|
log(msg)
|
||||||
return xml_error(msg, code=403)
|
return xml_error(msg, code=403)
|
||||||
formsemestre_id = partition["formsemestre_id"]
|
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
if not sco_permissions_check.can_change_groups(partition.formsemestre.id):
|
||||||
if not sco_permissions_check.can_change_groups(formsemestre_id):
|
|
||||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||||
log("***setGroups: partition_id=%s" % partition_id)
|
log("***setGroups: partition_id=%s" % partition_id)
|
||||||
log("groupsLists=%s" % groupsLists)
|
log("groupsLists=%s" % groupsLists)
|
||||||
log("groupsToCreate=%s" % groupsToCreate)
|
log("groupsToCreate=%s" % groupsToCreate)
|
||||||
log("groupsToDelete=%s" % groupsToDelete)
|
log("groupsToDelete=%s" % groupsToDelete)
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
|
||||||
if not sem["etat"]:
|
if not partition.formsemestre.etat:
|
||||||
raise AccessDenied("Modification impossible: semestre verrouillé")
|
raise AccessDenied("Modification impossible: semestre verrouillé")
|
||||||
|
|
||||||
groupsToDelete = [g for g in groupsToDelete.split(";") if g]
|
groupsToDelete = [g for g in groupsToDelete.split(";") if g]
|
||||||
|
|
||||||
etud_groups = formsemestre_get_etud_groupnames(formsemestre_id, attr="group_id")
|
etud_groups = formsemestre_get_etud_groupnames(
|
||||||
|
partition.formsemestre.id, attr="group_id"
|
||||||
|
)
|
||||||
for line in groupsLists.split("\n"): # for each group_id (one per line)
|
for line in groupsLists.split("\n"): # for each group_id (one per line)
|
||||||
fs = line.split(";")
|
fs = line.split(";")
|
||||||
group_id = fs[0].strip()
|
group_id = fs[0].strip()
|
||||||
@ -769,26 +745,23 @@ def setGroups(
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
log(f"setGroups: ignoring invalid group_id={group_id}")
|
log(f"setGroups: ignoring invalid group_id={group_id}")
|
||||||
continue
|
continue
|
||||||
group = get_group(group_id)
|
group: GroupDescr = GroupDescr.query.get_or_404(group_id)
|
||||||
# Anciens membres du groupe:
|
# Anciens membres du groupe:
|
||||||
old_members = get_group_members(group_id)
|
old_members_set = {etud.id for etud in group.etuds}
|
||||||
old_members_set = set([x["etudid"] for x in old_members])
|
|
||||||
# Place dans ce groupe les etudiants indiqués:
|
# Place dans ce groupe les etudiants indiqués:
|
||||||
for etudid_str in fs[1:-1]:
|
for etudid_str in fs[1:-1]:
|
||||||
etudid = int(etudid_str)
|
etudid = int(etudid_str)
|
||||||
if etudid in old_members_set:
|
if etudid in old_members_set:
|
||||||
old_members_set.remove(
|
# était dans ce groupe, l'enlever
|
||||||
etudid
|
old_members_set.remove(etudid)
|
||||||
) # a nouveau dans ce groupe, pas besoin de l'enlever
|
|
||||||
if (etudid not in etud_groups) or (
|
if (etudid not in etud_groups) or (
|
||||||
group_id != etud_groups[etudid].get(partition_id, "")
|
group_id != etud_groups[etudid].get(partition_id, "")
|
||||||
): # pas le meme groupe qu'actuel
|
): # 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:
|
# Retire les anciens membres:
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||||
for etudid in old_members_set:
|
for etudid in old_members_set:
|
||||||
log("removing %s from group %s" % (etudid, group_id))
|
|
||||||
ndb.SimpleQuery(
|
ndb.SimpleQuery(
|
||||||
"DELETE FROM group_membership WHERE etudid=%(etudid)s and group_id=%(group_id)s",
|
"DELETE FROM group_membership WHERE etudid=%(etudid)s and group_id=%(group_id)s",
|
||||||
{"etudid": etudid, "group_id": group_id},
|
{"etudid": etudid, "group_id": group_id},
|
||||||
@ -798,8 +771,8 @@ def setGroups(
|
|||||||
cnx,
|
cnx,
|
||||||
method="removeFromGroup",
|
method="removeFromGroup",
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
msg="formsemestre_id=%s,partition_name=%s, group_name=%s"
|
msg=f"""formsemestre_id={partition.formsemestre.id},partition_name={
|
||||||
% (formsemestre_id, partition["partition_name"], group["group_name"]),
|
partition.partition_name}, group_name={group.group_name}""",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Supprime les groupes indiqués comme supprimés:
|
# Supprime les groupes indiqués comme supprimés:
|
||||||
@ -819,10 +792,10 @@ def setGroups(
|
|||||||
return xml_error(msg, code=404)
|
return xml_error(msg, code=404)
|
||||||
# Place dans ce groupe les etudiants indiqués:
|
# Place dans ce groupe les etudiants indiqués:
|
||||||
for etudid in fs[1:-1]:
|
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
|
# Update parcours
|
||||||
formsemestre.update_inscriptions_parcours_from_groups()
|
partition.formsemestre.update_inscriptions_parcours_from_groups()
|
||||||
|
|
||||||
data = (
|
data = (
|
||||||
'<?xml version="1.0" encoding="utf-8"?><response>Groupes enregistrés</response>'
|
'<?xml version="1.0" encoding="utf-8"?><response>Groupes enregistrés</response>'
|
||||||
@ -835,6 +808,7 @@ def setGroups(
|
|||||||
def create_group(partition_id, group_name="", default=False) -> GroupDescr:
|
def create_group(partition_id, group_name="", default=False) -> GroupDescr:
|
||||||
"""Create a new group in this partition.
|
"""Create a new group in this partition.
|
||||||
If default, create default partition (with no name)
|
If default, create default partition (with no name)
|
||||||
|
Obsolete: utiliser Partition.create_group
|
||||||
"""
|
"""
|
||||||
partition = Partition.query.get_or_404(partition_id)
|
partition = Partition.query.get_or_404(partition_id)
|
||||||
if not sco_permissions_check.can_change_groups(partition.formsemestre_id):
|
if not sco_permissions_check.can_change_groups(partition.formsemestre_id):
|
||||||
@ -856,7 +830,7 @@ def create_group(partition_id, group_name="", default=False) -> GroupDescr:
|
|||||||
group = GroupDescr(partition=partition, group_name=group_name, numero=new_numero)
|
group = GroupDescr(partition=partition, group_name=group_name, numero=new_numero)
|
||||||
db.session.add(group)
|
db.session.add(group)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
log("create_group: created group_id={group.id}")
|
log(f"create_group: created group_id={group.id}")
|
||||||
#
|
#
|
||||||
return group
|
return group
|
||||||
|
|
||||||
@ -1400,11 +1374,11 @@ def groups_auto_repartition(partition_id=None):
|
|||||||
"""Reparti les etudiants dans des groupes dans une partition, en respectant le niveau
|
"""Reparti les etudiants dans des groupes dans une partition, en respectant le niveau
|
||||||
et la mixité.
|
et la mixité.
|
||||||
"""
|
"""
|
||||||
partition = get_partition(partition_id)
|
partition: Partition = Partition.query.get_or_404(partition_id)
|
||||||
if not partition["groups_editable"]:
|
if not partition.groups_editable:
|
||||||
raise AccessDenied("Partition non éditable")
|
raise AccessDenied("Partition non éditable")
|
||||||
formsemestre_id = partition["formsemestre_id"]
|
formsemestre_id = partition.formsemestre_id
|
||||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre = partition.formsemestre
|
||||||
# renvoie sur page édition groupes
|
# renvoie sur page édition groupes
|
||||||
dest_url = url_for(
|
dest_url = url_for(
|
||||||
"scolar.affect_groups", scodoc_dept=g.scodoc_dept, partition_id=partition_id
|
"scolar.affect_groups", scodoc_dept=g.scodoc_dept, partition_id=partition_id
|
||||||
@ -1427,12 +1401,14 @@ def groups_auto_repartition(partition_id=None):
|
|||||||
|
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Répartition des groupes"),
|
html_sco_header.sco_header(page_title="Répartition des groupes"),
|
||||||
"<h2>Répartition des groupes de %s</h2>" % partition["partition_name"],
|
f"""<h2>Répartition des groupes de {partition.partition_name}</h2>
|
||||||
f"<p>Semestre {formsemestre.titre_annee()}</p>",
|
<p>Semestre {formsemestre.titre_annee()}</p>",
|
||||||
"""<p class="help">Les groupes existants seront <b>effacés</b> et remplacés par
|
<p class="help">Les groupes existants seront <b>effacés</b> et remplacés par
|
||||||
ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau
|
ceux créés ici. La répartition aléatoire tente d'uniformiser le niveau
|
||||||
des groupes (en utilisant la dernière moyenne générale disponible pour
|
des groupes (en utilisant la dernière moyenne générale disponible pour
|
||||||
chaque étudiant) et de maximiser la mixité de chaque groupe.</p>""",
|
chaque étudiant) et de maximiser la mixité de chaque groupe.
|
||||||
|
</p>
|
||||||
|
""",
|
||||||
]
|
]
|
||||||
|
|
||||||
tf = TrivialFormulator(
|
tf = TrivialFormulator(
|
||||||
@ -1452,23 +1428,24 @@ def groups_auto_repartition(partition_id=None):
|
|||||||
# form submission
|
# form submission
|
||||||
log(
|
log(
|
||||||
"groups_auto_repartition( partition_id=%s partition_name=%s"
|
"groups_auto_repartition( partition_id=%s partition_name=%s"
|
||||||
% (partition_id, partition["partition_name"])
|
% (partition_id, partition.partition_name)
|
||||||
)
|
)
|
||||||
groupNames = tf[2]["groupNames"]
|
groupNames = tf[2]["groupNames"]
|
||||||
group_names = sorted(set([x.strip() for x in groupNames.split(",")]))
|
group_names = sorted({x.strip() for x in groupNames.split(",")})
|
||||||
# Détruit les groupes existant de cette partition
|
# Détruit les groupes existant de cette partition
|
||||||
for old_group in get_partition_groups(partition):
|
for group in partition.groups:
|
||||||
group_delete(old_group["group_id"])
|
db.session.delete(group)
|
||||||
|
db.session.commit()
|
||||||
# Crée les nouveaux groupes
|
# Crée les nouveaux groupes
|
||||||
group_ids = []
|
groups = []
|
||||||
for group_name in group_names:
|
for group_name in group_names:
|
||||||
if group_name.strip():
|
if group_name.strip():
|
||||||
group_ids.append(create_group(partition_id, group_name).id)
|
groups.append(partition.create_group(group_name))
|
||||||
#
|
#
|
||||||
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||||
identdict = nt.identdict
|
identdict = nt.identdict
|
||||||
# build: { civilite : liste etudids trie par niveau croissant }
|
# build: { civilite : liste etudids trie par niveau croissant }
|
||||||
civilites = set([x["civilite"] for x in identdict.values()])
|
civilites = {x["civilite"] for x in identdict.values()}
|
||||||
listes = {}
|
listes = {}
|
||||||
for civilite in civilites:
|
for civilite in civilites:
|
||||||
listes[civilite] = [
|
listes[civilite] = [
|
||||||
@ -1481,16 +1458,19 @@ def groups_auto_repartition(partition_id=None):
|
|||||||
# affect aux groupes:
|
# affect aux groupes:
|
||||||
n = len(identdict)
|
n = len(identdict)
|
||||||
igroup = 0
|
igroup = 0
|
||||||
nbgroups = len(group_ids)
|
nbgroups = len(groups)
|
||||||
while n > 0:
|
while n > 0:
|
||||||
|
log(f"n={n}")
|
||||||
for civilite in civilites:
|
for civilite in civilites:
|
||||||
|
log(f"civilite={civilite}")
|
||||||
if len(listes[civilite]):
|
if len(listes[civilite]):
|
||||||
n -= 1
|
n -= 1
|
||||||
etudid = listes[civilite].pop()[1]
|
etudid = listes[civilite].pop()[1]
|
||||||
group_id = group_ids[igroup]
|
group = groups[igroup]
|
||||||
igroup = (igroup + 1) % nbgroups
|
igroup = (igroup + 1) % nbgroups
|
||||||
change_etud_group_in_partition(etudid, group_id, partition)
|
log(f"in {etudid} in group {group.id}")
|
||||||
log("%s in group %s" % (etudid, group_id))
|
change_etud_group_in_partition(etudid, group)
|
||||||
|
log(f"{etudid} in group {group.id}")
|
||||||
return flask.redirect(dest_url)
|
return flask.redirect(dest_url)
|
||||||
|
|
||||||
|
|
||||||
@ -1498,8 +1478,6 @@ def _get_prev_moy(etudid, formsemestre_id):
|
|||||||
"""Donne la derniere moyenne generale calculee pour cette étudiant,
|
"""Donne la derniere moyenne generale calculee pour cette étudiant,
|
||||||
ou 0 si on n'en trouve pas (nouvel inscrit,...).
|
ou 0 si on n'en trouve pas (nouvel inscrit,...).
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_cursus_dut
|
|
||||||
|
|
||||||
info = sco_etud.get_etud_info(etudid=etudid, filled=True)
|
info = sco_etud.get_etud_info(etudid=etudid, filled=True)
|
||||||
if not info:
|
if not info:
|
||||||
raise ScoValueError("etudiant invalide: etudid=%s" % etudid)
|
raise ScoValueError("etudiant invalide: etudid=%s" % etudid)
|
||||||
@ -1520,10 +1498,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
|
Si la partition existe déjà, ses groupes sont mis à jour (les groupes devenant
|
||||||
vides ne sont pas supprimés).
|
vides ne sont pas supprimés).
|
||||||
"""
|
"""
|
||||||
|
# A RE-ECRIRE pour utiliser les modèles.
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
|
|
||||||
partition_name = str(partition_name)
|
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(
|
ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||||
args={"formsemestre_id": formsemestre_id}
|
args={"formsemestre_id": formsemestre_id}
|
||||||
)
|
)
|
||||||
@ -1542,20 +1521,17 @@ def create_etapes_partition(formsemestre_id, partition_name="apo_etapes"):
|
|||||||
pid = partition_create(
|
pid = partition_create(
|
||||||
formsemestre_id, partition_name=partition_name, redirect=False
|
formsemestre_id, partition_name=partition_name, redirect=False
|
||||||
)
|
)
|
||||||
partition = get_partition(pid)
|
partition: Partition = Partition.query.get(pid)
|
||||||
groups = get_partition_groups(partition)
|
groups = partition.groups
|
||||||
groups_by_names = {g["group_name"]: g for g in groups}
|
groups_by_names = {g["group_name"]: g for g in groups}
|
||||||
for etape in etapes:
|
for etape in etapes:
|
||||||
if not (etape in groups_by_names):
|
if etape not in groups_by_names:
|
||||||
new_group = create_group(pid, etape)
|
new_group = create_group(pid, etape)
|
||||||
g = get_group(new_group.id) # XXX transition: recupere old style dict
|
groups_by_names[etape] = new_group
|
||||||
groups_by_names[etape] = g
|
|
||||||
# Place les etudiants dans les groupes
|
# Place les etudiants dans les groupes
|
||||||
for i in ins:
|
for i in ins:
|
||||||
if i["etape"]:
|
if i["etape"]:
|
||||||
change_etud_group_in_partition(
|
change_etud_group_in_partition(i["etudid"], groups_by_names[i["etape"]])
|
||||||
i["etudid"], groups_by_names[i["etape"]]["group_id"], partition
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def do_evaluation_listeetuds_groups(
|
def do_evaluation_listeetuds_groups(
|
||||||
|
@ -639,10 +639,10 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
fields = adm_get_fields(titles, formsemestre_id)
|
fields = adm_get_fields(titles, formsemestre_id)
|
||||||
idx_nom = None
|
idx_nom = None
|
||||||
idx_prenom = None
|
idx_prenom = None
|
||||||
for idx in fields:
|
for idx, field in fields.items():
|
||||||
if fields[idx][0] == "nom":
|
if field[0] == "nom":
|
||||||
idx_nom = idx
|
idx_nom = idx
|
||||||
if fields[idx][0] == "prenom":
|
if field[0] == "prenom":
|
||||||
idx_prenom = idx
|
idx_prenom = idx
|
||||||
if (idx_nom is None) or (idx_prenom is None):
|
if (idx_nom is None) or (idx_prenom is None):
|
||||||
log("fields indices=" + ", ".join([str(x) for x in fields]))
|
log("fields indices=" + ", ".join([str(x) for x in fields]))
|
||||||
@ -664,21 +664,20 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
# Retrouve l'étudiant parmi ceux du semestre par (nom, prenom)
|
# Retrouve l'étudiant parmi ceux du semestre par (nom, prenom)
|
||||||
nom = adm_normalize_string(line[idx_nom])
|
nom = adm_normalize_string(line[idx_nom])
|
||||||
prenom = adm_normalize_string(line[idx_prenom])
|
prenom = adm_normalize_string(line[idx_prenom])
|
||||||
if not (nom, prenom) in etuds_by_nomprenom:
|
if (nom, prenom) not in etuds_by_nomprenom:
|
||||||
log(
|
msg = f"""Étudiant <b>{line[idx_nom]} {line[idx_prenom]} inexistant</b>"""
|
||||||
"unable to find %s %s among members" % (line[idx_nom], line[idx_prenom])
|
diag.append(msg)
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
etud = etuds_by_nomprenom[(nom, prenom)]
|
etud = etuds_by_nomprenom[(nom, prenom)]
|
||||||
cur_adm = sco_etud.admission_list(cnx, args={"etudid": etud["etudid"]})[0]
|
cur_adm = sco_etud.admission_list(cnx, args={"etudid": etud["etudid"]})[0]
|
||||||
# peuple les champs presents dans le tableau
|
# peuple les champs presents dans le tableau
|
||||||
args = {}
|
args = {}
|
||||||
for idx in fields:
|
for idx, field in fields.items():
|
||||||
field_name, convertor = fields[idx]
|
field_name, convertor = field
|
||||||
if field_name in modifiable_fields:
|
if field_name in modifiable_fields:
|
||||||
try:
|
try:
|
||||||
val = convertor(line[idx])
|
val = convertor(line[idx])
|
||||||
except ValueError:
|
except ValueError as exc:
|
||||||
raise ScoFormatError(
|
raise ScoFormatError(
|
||||||
'scolars_import_admission: valeur invalide, ligne %d colonne %s: "%s"'
|
'scolars_import_admission: valeur invalide, ligne %d colonne %s: "%s"'
|
||||||
% (nline, field_name, line[idx]),
|
% (nline, field_name, line[idx]),
|
||||||
@ -687,7 +686,7 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
),
|
),
|
||||||
)
|
) from exc
|
||||||
if val is not None: # note: ne peut jamais supprimer une valeur
|
if val is not None: # note: ne peut jamais supprimer une valeur
|
||||||
args[field_name] = val
|
args[field_name] = val
|
||||||
if args:
|
if args:
|
||||||
@ -723,7 +722,7 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
|
|||||||
group = GroupDescr.query.get(group_id)
|
group = GroupDescr.query.get(group_id)
|
||||||
if group.partition.groups_editable:
|
if group.partition.groups_editable:
|
||||||
sco_groups.change_etud_group_in_partition(
|
sco_groups.change_etud_group_in_partition(
|
||||||
args["etudid"], group_id
|
args["etudid"], group
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
log("scolars_import_admission: partition non editable")
|
log("scolars_import_admission: partition non editable")
|
||||||
|
@ -36,13 +36,12 @@ from flask import url_for, g, request
|
|||||||
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
|
||||||
from app import log
|
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.gen_tables import GenTable
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc import sco_formations
|
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
from app.scodoc import sco_formsemestre_inscriptions
|
from app.scodoc import sco_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_groups
|
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)
|
(la liste doit avoir été vérifiée au préalable)
|
||||||
En option: inscrit aux mêmes groupes que dans le semestre origine
|
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: FormSemestre = FormSemestre.query.get(sem["formsemestre_id"])
|
||||||
formsemestre.setup_parcours_groups()
|
formsemestre.setup_parcours_groups()
|
||||||
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
|
log(f"do_inscrit (inscrit_groupes={inscrit_groupes}): {etudids}")
|
||||||
@ -220,11 +220,8 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
|
|||||||
|
|
||||||
# Inscrit aux groupes
|
# Inscrit aux groupes
|
||||||
for partition_group in partition_groups:
|
for partition_group in partition_groups:
|
||||||
sco_groups.change_etud_group_in_partition(
|
group: GroupDescr = GroupDescr.query.get(partition_group["group_id"])
|
||||||
etudid,
|
sco_groups.change_etud_group_in_partition(etudid, group)
|
||||||
partition_group["group_id"],
|
|
||||||
partition_group,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def do_desinscrit(sem, etudids):
|
def do_desinscrit(sem, etudids):
|
||||||
|
@ -84,6 +84,8 @@ def SU(s: str) -> str:
|
|||||||
s = html.unescape(s)
|
s = html.unescape(s)
|
||||||
# Remplace les <br> par des <br/>
|
# Remplace les <br> par des <br/>
|
||||||
s = re.sub(r"<br\s*>", "<br/>", s)
|
s = re.sub(r"<br\s*>", "<br/>", s)
|
||||||
|
# And substitute unicode characters not supported by ReportLab
|
||||||
|
s = s.replace("‐", "-")
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +142,9 @@ def check_access_diretud(formsemestre_id, required_permission=Permission.ScoImpl
|
|||||||
|
|
||||||
|
|
||||||
def can_change_groups(formsemestre_id: int) -> bool:
|
def can_change_groups(formsemestre_id: int) -> bool:
|
||||||
"Vrai si l'utilisateur peut changer les groupes dans ce semestre"
|
"""Vrai si l'utilisateur peut changer les groupes dans ce semestre
|
||||||
|
Obsolete: utiliser FormSemestre.can_change_groups
|
||||||
|
"""
|
||||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
if not formsemestre.etat:
|
if not formsemestre.etat:
|
||||||
return False # semestre verrouillé
|
return False # semestre verrouillé
|
||||||
|
@ -2339,14 +2339,14 @@ def formsemestre_validation_but(
|
|||||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
etud = Identite.get_etud(etudid)
|
|
||||||
nb_etuds = formsemestre.etuds.count()
|
|
||||||
# la route ne donne pas le type d'etudid pour pouvoir construire des URLs
|
# la route ne donne pas le type d'etudid pour pouvoir construire des URLs
|
||||||
# provisoires avec NEXT et PREV
|
# provisoires avec NEXT et PREV
|
||||||
try:
|
try:
|
||||||
etudid = int(etudid)
|
etudid = int(etudid)
|
||||||
except ValueError:
|
except ValueError as exc:
|
||||||
abort(404, "invalid etudid")
|
raise ScoValueError("adresse invalide") from exc
|
||||||
|
etud = Identite.get_etud(etudid)
|
||||||
|
nb_etuds = formsemestre.etuds.count()
|
||||||
read_only = not formsemestre.can_edit_jury()
|
read_only = not formsemestre.can_edit_jury()
|
||||||
|
|
||||||
# --- Navigation
|
# --- Navigation
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.4.97"
|
SCOVERSION = "9.4.99"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user