forked from ScoDoc/ScoDoc
correction etat branche + tests unitaire ✅ + tests api ✅
This commit is contained in:
parent
a63e14ce06
commit
4d72fec42d
@ -400,8 +400,8 @@ def justif_import(justif_id: int = None):
|
||||
return json_error(404, err.args[0])
|
||||
|
||||
|
||||
@bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
|
||||
@api_web_bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["GET"])
|
||||
@bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["POST"])
|
||||
@api_web_bp.route("/justificatif/export/<int:justif_id>/<filename>", methods=["POST"])
|
||||
@scodoc
|
||||
@login_required
|
||||
@permission_required(Permission.ScoView)
|
||||
|
@ -33,10 +33,7 @@ import pandas as pd
|
||||
from app import db
|
||||
from app import models
|
||||
from app.models import (
|
||||
DispenseUE,
|
||||
FormSemestre,
|
||||
FormSemestreInscription,
|
||||
Identite,
|
||||
Module,
|
||||
ModuleImpl,
|
||||
ModuleUECoef,
|
||||
@ -218,31 +215,6 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
|
||||
)
|
||||
|
||||
|
||||
def load_dispense_ues(
|
||||
formsemestre: FormSemestre, etudids: pd.Index, ues: list[UniteEns]
|
||||
) -> set[tuple[int, int]]:
|
||||
"""Construit l'ensemble des
|
||||
etudids = modimpl_inscr_df.index, # les etudids
|
||||
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
|
||||
|
||||
Résultat: set de (etudid, ue_id).
|
||||
"""
|
||||
dispense_ues = set()
|
||||
ue_sem_by_code = {ue.ue_code: ue for ue in ues}
|
||||
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
|
||||
# puis filtre sur inscrits et code d'UE UE
|
||||
for dispense_ue in DispenseUE.query.join(
|
||||
Identite, FormSemestreInscription
|
||||
).filter_by(formsemestre_id=formsemestre.id):
|
||||
if dispense_ue.etudid in etudids:
|
||||
# UE dans le semestre avec même code ?
|
||||
ue = ue_sem_by_code.get(dispense_ue.ue.ue_code)
|
||||
if ue is not None:
|
||||
dispense_ues.add((dispense_ue.etudid, ue.id))
|
||||
|
||||
return dispense_ues
|
||||
|
||||
|
||||
def compute_ue_moys_apc(
|
||||
sem_cube: np.array,
|
||||
etuds: list,
|
||||
|
@ -72,7 +72,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
||||
modimpl.module.ue.type != UE_SPORT
|
||||
for modimpl in self.formsemestre.modimpls_sorted
|
||||
]
|
||||
self.dispense_ues = moy_ue.load_dispense_ues(
|
||||
self.dispense_ues = DispenseUE.load_formsemestre_dispense_ues_set(
|
||||
self.formsemestre, self.modimpl_inscr_df.index, self.ues
|
||||
)
|
||||
self.etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
||||
|
@ -256,12 +256,23 @@ class UniteEns(db.Model):
|
||||
|
||||
class DispenseUE(db.Model):
|
||||
"""Dispense d'UE
|
||||
Utilisé en PCC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
|
||||
Utilisé en APC (BUT) pour indiquer les étudiants redoublants avec une UE capitalisée
|
||||
qu'ils ne refont pas.
|
||||
La dispense d'UE n'est PAS une validation:
|
||||
- elle n'est pas affectée par les décisions de jury (pas effacée)
|
||||
- elle est associée à un formsemestre
|
||||
- elle ne permet pas la délivrance d'ECTS ou du diplôme.
|
||||
|
||||
On utilise cette dispense et non une "inscription" par souci d'efficacité:
|
||||
en général, la grande majorité des étudiants suivront toutes les UEs de leur parcours,
|
||||
la dispense étant une exception.
|
||||
"""
|
||||
|
||||
__table_args__ = (db.UniqueConstraint("ue_id", "etudid"),)
|
||||
__table_args__ = (db.UniqueConstraint("formsemestre_id", "ue_id", "etudid"),)
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
formsemestre_id = formsemestre_id = db.Column(
|
||||
db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True
|
||||
)
|
||||
ue_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey(UniteEns.id, ondelete="CASCADE"),
|
||||
@ -280,3 +291,25 @@ class DispenseUE(db.Model):
|
||||
def __repr__(self) -> str:
|
||||
return f"""<{self.__class__.__name__} {self.id} etud={
|
||||
repr(self.etud)} ue={repr(self.ue)}>"""
|
||||
|
||||
@classmethod
|
||||
def load_formsemestre_dispense_ues_set(
|
||||
cls, formsemestre: "FormSemestre", etudids: pd.Index, ues: list[UniteEns]
|
||||
) -> set[tuple[int, int]]:
|
||||
"""Construit l'ensemble des
|
||||
etudids = modimpl_inscr_df.index, # les etudids
|
||||
ue_ids : modimpl_coefs_df.index, # les UE du formsemestre sans les UE bonus sport
|
||||
|
||||
Résultat: set de (etudid, ue_id).
|
||||
"""
|
||||
# Prend toutes les dispenses obtenues par des étudiants de ce formsemestre,
|
||||
# puis filtre sur inscrits et ues
|
||||
ue_ids = {ue.id for ue in ues}
|
||||
dispense_ues = {
|
||||
(dispense_ue.etudid, dispense_ue.ue_id)
|
||||
for dispense_ue in DispenseUE.query.filter_by(
|
||||
formsemestre_id=formsemestre.id
|
||||
)
|
||||
if dispense_ue.etudid in etudids and dispense_ue.ue_id in ue_ids
|
||||
}
|
||||
return dispense_ues
|
||||
|
@ -36,7 +36,7 @@ from flask_login import current_user
|
||||
|
||||
from app import db, log
|
||||
|
||||
from app.models import ModuleImpl, ScolarNews
|
||||
from app.models import Evaluation, ModuleImpl, ScolarNews
|
||||
from app.models.evaluations import evaluation_enrich_dict, check_evaluation_args
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -1621,10 +1621,24 @@ def etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
||||
ue = UniteEns.query.get_or_404(ue_id)
|
||||
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if ue.formation.is_apc():
|
||||
if DispenseUE.query.filter_by(etudid=etudid, ue_id=ue_id).count() == 0:
|
||||
disp = DispenseUE(ue_id=ue_id, etudid=etudid)
|
||||
if (
|
||||
DispenseUE.query.filter_by(
|
||||
formsemestre_id=formsemestre_id, etudid=etudid, ue_id=ue_id
|
||||
).count()
|
||||
== 0
|
||||
):
|
||||
disp = DispenseUE(
|
||||
formsemestre_id=formsemestre_id, ue_id=ue_id, etudid=etudid
|
||||
)
|
||||
db.session.add(disp)
|
||||
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} de {formsemestre.titre_annee()}",
|
||||
commit=True,
|
||||
)
|
||||
sco_cache.invalidate_formsemestre(formsemestre_id=formsemestre.id)
|
||||
else:
|
||||
sco_moduleimpl_inscriptions.do_etud_desinscrit_ue_classic(
|
||||
|
@ -10,58 +10,86 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'dbcf2175e87f'
|
||||
down_revision = '5c7b208355df'
|
||||
revision = "dbcf2175e87f"
|
||||
down_revision = "5c7b208355df"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('justificatifs',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
||||
sa.Column('etat', sa.Integer(), nullable=False),
|
||||
sa.Column('entry_date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('raison', sa.Text(), nullable=True),
|
||||
sa.Column('fichier', sa.Text(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
op.create_table(
|
||||
"justificatifs",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column(
|
||||
"date_debut",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"date_fin",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("etudid", sa.Integer(), nullable=False),
|
||||
sa.Column("etat", sa.Integer(), nullable=False),
|
||||
sa.Column(
|
||||
"entry_date",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.Column("raison", sa.Text(), nullable=True),
|
||||
sa.Column("fichier", sa.Text(), nullable=True),
|
||||
sa.ForeignKeyConstraint(["etudid"], ["identite.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(op.f('ix_justificatifs_etudid'), 'justificatifs', ['etudid'], unique=False)
|
||||
op.create_table('assiduites',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('date_debut', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('date_fin', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('moduleimpl_id', sa.Integer(), nullable=True),
|
||||
sa.Column('etudid', sa.Integer(), nullable=False),
|
||||
sa.Column('etat', sa.Integer(), nullable=False),
|
||||
sa.Column('desc', sa.Text(), nullable=True),
|
||||
sa.Column('entry_date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True),
|
||||
sa.ForeignKeyConstraint(['etudid'], ['identite.id'], ondelete='CASCADE'),
|
||||
sa.ForeignKeyConstraint(['moduleimpl_id'], ['notes_moduleimpl.id'], ondelete='SET NULL'),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
op.create_index(
|
||||
op.f("ix_justificatifs_etudid"), "justificatifs", ["etudid"], unique=False
|
||||
)
|
||||
op.create_table(
|
||||
"assiduites",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column(
|
||||
"date_debut",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"date_fin",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("moduleimpl_id", sa.Integer(), nullable=True),
|
||||
sa.Column("etudid", sa.Integer(), nullable=False),
|
||||
sa.Column("etat", sa.Integer(), nullable=False),
|
||||
sa.Column("desc", sa.Text(), nullable=True),
|
||||
sa.Column(
|
||||
"entry_date",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.ForeignKeyConstraint(["etudid"], ["identite.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["moduleimpl_id"], ["notes_moduleimpl.id"], ondelete="SET NULL"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index(
|
||||
op.f("ix_assiduites_etudid"), "assiduites", ["etudid"], unique=False
|
||||
)
|
||||
op.create_index(op.f('ix_assiduites_etudid'), 'assiduites', ['etudid'], unique=False)
|
||||
op.drop_constraint('dispenseUE_formsemestre_id_ue_id_etudid_key', 'dispenseUE', type_='unique')
|
||||
op.drop_index('ix_dispenseUE_formsemestre_id', table_name='dispenseUE')
|
||||
op.create_unique_constraint(None, 'dispenseUE', ['ue_id', 'etudid'])
|
||||
op.drop_constraint('dispenseUE_formsemestre_id_fkey', 'dispenseUE', type_='foreignkey')
|
||||
op.drop_column('dispenseUE', 'formsemestre_id')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('dispenseUE', sa.Column('formsemestre_id', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key('dispenseUE_formsemestre_id_fkey', 'dispenseUE', 'notes_formsemestre', ['formsemestre_id'], ['id'])
|
||||
op.drop_constraint(None, 'dispenseUE', type_='unique')
|
||||
op.create_index('ix_dispenseUE_formsemestre_id', 'dispenseUE', ['formsemestre_id'], unique=False)
|
||||
op.create_unique_constraint('dispenseUE_formsemestre_id_ue_id_etudid_key', 'dispenseUE', ['formsemestre_id', 'ue_id', 'etudid'])
|
||||
op.drop_index(op.f('ix_assiduites_etudid'), table_name='assiduites')
|
||||
op.drop_table('assiduites')
|
||||
op.drop_index(op.f('ix_justificatifs_etudid'), table_name='justificatifs')
|
||||
op.drop_table('justificatifs')
|
||||
op.drop_index(op.f("ix_assiduites_etudid"), table_name="assiduites")
|
||||
op.drop_table("assiduites")
|
||||
op.drop_index(op.f("ix_justificatifs_etudid"), table_name="justificatifs")
|
||||
op.drop_table("justificatifs")
|
||||
# ### end Alembic commands ###
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.4.35"
|
||||
SCOVERSION = "9.4.36"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
@ -342,21 +342,23 @@ def test_list_justificatifs(api_headers):
|
||||
check_failure_get(f"/justificatif/list/{FAUX}", api_headers)
|
||||
|
||||
|
||||
def get_export(id: int, fname: str, api_headers):
|
||||
def post_export(id: int, fname: str, api_headers):
|
||||
url: str = API_URL + f"/justificatif/export/{id}/{fname}"
|
||||
res = requests.get(url, headers=api_headers)
|
||||
res = requests.post(url, headers=api_headers)
|
||||
return res
|
||||
|
||||
|
||||
def test_export(api_headers):
|
||||
# Bon fonctionnement
|
||||
|
||||
assert get_export(1, "test_api_justificatif.txt", api_headers).status_code == 200
|
||||
assert post_export(1, "test_api_justificatif.txt", api_headers).status_code == 200
|
||||
|
||||
# Mauvais fonctionnement
|
||||
assert get_export(FAUX, "test_api_justificatif.txt", api_headers).status_code == 404
|
||||
assert get_export(1, "blabla.txt", api_headers).status_code == 404
|
||||
assert get_export(2, "blabla.txt", api_headers).status_code == 404
|
||||
assert (
|
||||
post_export(FAUX, "test_api_justificatif.txt", api_headers).status_code == 404
|
||||
)
|
||||
assert post_export(1, "blabla.txt", api_headers).status_code == 404
|
||||
assert post_export(2, "blabla.txt", api_headers).status_code == 404
|
||||
|
||||
|
||||
def test_remove_justificatif(api_headers):
|
||||
|
@ -123,7 +123,13 @@ def test_ue_moy(test_client):
|
||||
modimpl.module.ue.type != UE_SPORT for modimpl in formsemestre.modimpls_sorted
|
||||
]
|
||||
etud_moy_ue = moy_ue.compute_ue_moys_apc(
|
||||
sem_cube, etuds, modimpls, modimpl_inscr_df, modimpl_coefs_df, modimpl_mask
|
||||
sem_cube,
|
||||
etuds,
|
||||
modimpls,
|
||||
modimpl_inscr_df,
|
||||
modimpl_coefs_df,
|
||||
modimpl_mask,
|
||||
set(),
|
||||
)
|
||||
assert etud_moy_ue[ue1.id][etudid] == n1
|
||||
assert etud_moy_ue[ue2.id][etudid] == n1
|
||||
|
Loading…
Reference in New Issue
Block a user