2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2023-01-02 13:16:27 +01:00
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Emmanuel Viennet emmanuel.viennet@viennet.net
#
##############################################################################
""" Opérations d ' inscriptions aux semestres et modules
"""
2023-03-08 22:23:55 +01:00
import collections
2021-06-19 23:21:37 +02:00
import time
2021-08-01 10:16:16 +02:00
import flask
2022-09-03 11:41:56 +02:00
from flask import flash , url_for , g , request
2021-07-11 17:37:12 +02:00
2022-09-30 22:43:39 +02:00
from app import db
2022-02-13 15:50:16 +01:00
from app . comp import res_sem
2022-03-27 22:25:00 +02:00
from app . comp . res_compat import NotesTableCompat
2022-09-30 22:43:39 +02:00
from app . models import Formation , FormSemestre , FormSemestreInscription , Scolog
2022-09-03 11:41:56 +02:00
from app . models . etudiants import Identite
from app . models . groups import GroupDescr
2022-09-30 22:43:39 +02:00
from app . models . validations import ScolarEvent
2021-06-19 23:21:37 +02:00
import app . scodoc . sco_utils as scu
2021-08-29 19:57:32 +02:00
from app import log
2021-06-19 23:21:37 +02:00
from app . scodoc . scolog import logdb
2021-11-03 16:15:39 +01:00
from app . scodoc . sco_exceptions import ScoException , ScoValueError
2023-02-12 13:36:47 +01:00
from app . scodoc . codes_cursus import UE_STANDARD , UE_SPORT , UE_TYPE_NAME
2021-06-19 23:21:37 +02:00
import app . scodoc . notesdb as ndb
2022-02-13 15:50:16 +01:00
from app . scodoc . TrivialFormulator import TrivialFormulator
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_find_etud
from app . scodoc import sco_formsemestre
from app . scodoc import sco_moduleimpl
from app . scodoc import sco_groups
from app . scodoc import sco_etud
2021-07-19 19:53:01 +02:00
from app . scodoc import sco_cache
2021-06-19 23:21:37 +02:00
from app . scodoc import html_sco_header
2020-09-26 16:19:37 +02:00
2021-06-16 16:59:31 +02:00
# --- Gestion des inscriptions aux semestres
_formsemestre_inscriptionEditor = ndb . EditableTable (
" notes_formsemestre_inscription " ,
" formsemestre_inscription_id " ,
( " formsemestre_inscription_id " , " etudid " , " formsemestre_id " , " etat " , " etape " ) ,
sortkey = " formsemestre_id " ,
2021-09-27 22:54:23 +02:00
insert_ignore_conflicts = True ,
2021-06-16 16:59:31 +02:00
)
2021-08-19 10:28:35 +02:00
def do_formsemestre_inscription_list ( * args , * * kw ) :
2021-06-16 16:59:31 +02:00
" list formsemestre_inscriptions "
cnx = ndb . GetDBConnexion ( )
return _formsemestre_inscriptionEditor . list ( cnx , * args , * * kw )
2021-08-19 10:28:35 +02:00
def do_formsemestre_inscription_listinscrits ( formsemestre_id ) :
2021-12-17 23:50:34 +01:00
""" Liste les inscrits (état I) à ce semestre et cache le résultat.
Result : [ { " etudid " : , " formsemestre_id " : , " etat " : , " etape " : } ]
"""
2021-07-19 19:53:01 +02:00
r = sco_cache . SemInscriptionsCache . get ( formsemestre_id )
2021-06-21 10:17:16 +02:00
if r is None :
# retreive list
r = do_formsemestre_inscription_list (
2022-09-30 22:43:39 +02:00
args = { " formsemestre_id " : formsemestre_id , " etat " : scu . INSCRIT }
2021-06-21 10:17:16 +02:00
)
2021-07-19 19:53:01 +02:00
sco_cache . SemInscriptionsCache . set ( formsemestre_id , r )
2021-06-21 10:17:16 +02:00
return r
2021-08-21 00:24:51 +02:00
def do_formsemestre_inscription_create ( args , method = None ) :
2021-06-16 16:59:31 +02:00
" create a formsemestre_inscription (and sco event) "
cnx = ndb . GetDBConnexion ( )
2022-09-03 11:41:56 +02:00
log ( f " do_formsemestre_inscription_create: args= { args } " )
2021-06-16 16:59:31 +02:00
sems = sco_formsemestre . do_formsemestre_list (
2021-08-19 10:28:35 +02:00
{ " formsemestre_id " : args [ " formsemestre_id " ] }
2021-06-16 16:59:31 +02:00
)
if len ( sems ) != 1 :
2022-09-03 11:41:56 +02:00
raise ScoValueError ( f " code de semestre invalide: { args [ ' formsemestre_id ' ] } " )
2021-06-16 16:59:31 +02:00
sem = sems [ 0 ]
# check lock
2021-08-10 12:57:38 +02:00
if not sem [ " etat " ] :
2021-06-16 16:59:31 +02:00
raise ScoValueError ( " inscription: semestre verrouille " )
#
r = _formsemestre_inscriptionEditor . create ( cnx , args )
# Evenement
2021-06-19 23:21:37 +02:00
sco_etud . scolar_events_create (
2021-06-16 16:59:31 +02:00
cnx ,
args = {
" etudid " : args [ " etudid " ] ,
" event_date " : time . strftime ( " %d / % m/ % Y " ) ,
" formsemestre_id " : args [ " formsemestre_id " ] ,
" event_type " : " INSCRIPTION " ,
} ,
)
# Log etudiant
logdb (
cnx ,
method = method ,
etudid = args [ " etudid " ] ,
2022-09-03 11:41:56 +02:00
msg = f " inscription en semestre { args [ ' formsemestre_id ' ] } " ,
2021-06-16 16:59:31 +02:00
commit = False ,
)
#
2022-09-03 11:41:56 +02:00
sco_cache . invalidate_formsemestre ( formsemestre_id = args [ " formsemestre_id " ] )
2021-06-16 16:59:31 +02:00
return r
2021-08-21 00:24:51 +02:00
def do_formsemestre_inscription_delete ( oid , formsemestre_id = None ) :
2021-06-16 16:59:31 +02:00
" delete formsemestre_inscription "
cnx = ndb . GetDBConnexion ( )
_formsemestre_inscriptionEditor . delete ( cnx , oid )
2022-09-03 11:41:56 +02:00
sco_cache . invalidate_formsemestre ( formsemestre_id = formsemestre_id )
2021-06-16 16:59:31 +02:00
2021-11-03 16:15:39 +01:00
def do_formsemestre_demission (
etudid ,
formsemestre_id ,
event_date = None ,
2022-09-30 22:43:39 +02:00
etat_new = scu . DEMISSION , # DEMISSION or DEF
operation_method = " dem_etudiant " ,
2021-11-03 16:15:39 +01:00
event_type = " DEMISSION " ,
) :
" Démission ou défaillance d ' un étudiant "
# marque 'D' ou DEF dans l'inscription au semestre et ajoute
# un "evenement" scolarite
2022-09-30 22:43:39 +02:00
if etat_new not in ( scu . DEF , scu . DEMISSION ) :
raise ScoValueError ( " nouveau code d ' état invalide " )
try :
event_date_iso = ndb . DateDMYtoISO ( event_date )
except ValueError as exc :
raise ScoValueError ( " format de date invalide " ) from exc
etud : Identite = Identite . query . filter_by (
id = etudid , dept_id = g . scodoc_dept_id
) . first_or_404 ( )
2021-11-03 16:15:39 +01:00
# check lock
2022-09-30 22:43:39 +02:00
formsemestre : FormSemestre = FormSemestre . query . filter_by (
id = formsemestre_id , dept_id = g . scodoc_dept_id
) . first_or_404 ( )
if not formsemestre . etat :
2021-11-03 16:15:39 +01:00
raise ScoValueError ( " Modification impossible: semestre verrouille " )
#
2022-09-30 22:43:39 +02:00
if formsemestre_id not in ( inscr . formsemestre_id for inscr in etud . inscriptions ( ) ) :
raise ScoValueError ( " étudiant non inscrit dans ce semestre ! " )
inscr = next (
inscr
for inscr in etud . inscriptions ( )
if inscr . formsemestre_id == formsemestre_id
)
inscr . etat = etat_new
db . session . add ( inscr )
Scolog . logdb ( method = operation_method , etudid = etudid )
event = ScolarEvent (
etudid = etudid ,
event_date = event_date_iso ,
formsemestre_id = formsemestre_id ,
event_type = event_type ,
2021-11-03 16:15:39 +01:00
)
2022-09-30 22:43:39 +02:00
db . session . add ( event )
db . session . commit ( )
2023-02-23 21:48:40 +01:00
sco_cache . invalidate_formsemestre (
formsemestre_id = formsemestre_id
) # > démission ou défaillance
2022-09-30 22:43:39 +02:00
if etat_new == scu . DEMISSION :
flash ( " Démission enregistrée " )
elif etat_new == scu . DEF :
flash ( " Défaillance enregistrée " )
2021-11-03 16:15:39 +01:00
2021-08-21 00:24:51 +02:00
def do_formsemestre_inscription_edit ( args = None , formsemestre_id = None ) :
2021-06-16 16:59:31 +02:00
" edit a formsemestre_inscription "
cnx = ndb . GetDBConnexion ( )
_formsemestre_inscriptionEditor . edit ( cnx , args )
2021-07-19 19:53:01 +02:00
sco_cache . invalidate_formsemestre (
formsemestre_id = formsemestre_id
2023-02-23 21:48:40 +01:00
) # > modif inscription semestre
2021-06-16 16:59:31 +02:00
2021-09-02 18:05:22 +02:00
def do_formsemestre_desinscription ( etudid , formsemestre_id ) :
2021-06-16 16:59:31 +02:00
""" Désinscription d ' un étudiant.
Si semestre extérieur et dernier inscrit , suppression de ce semestre .
"""
2021-06-21 10:17:16 +02:00
from app . scodoc import sco_formsemestre_edit
2022-09-03 11:41:56 +02:00
formsemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
etud = Identite . query . get_or_404 ( etudid )
2021-06-16 16:59:31 +02:00
# -- check lock
2022-09-03 11:41:56 +02:00
if not formsemestre . etat :
raise ScoValueError ( " désinscription impossible: semestre verrouille " )
2021-06-16 16:59:31 +02:00
2022-09-03 11:41:56 +02:00
# -- Si decisions de jury, désinscription interdite
2022-02-13 15:50:16 +01:00
nt : NotesTableCompat = res_sem . load_formsemestre_results ( formsemestre )
2021-06-16 16:59:31 +02:00
if nt . etud_has_decision ( etudid ) :
raise ScoValueError (
2022-09-03 11:41:56 +02:00
""" désinscription impossible: l ' étudiant {etud.nomprenom} a
une décision de jury ( la supprimer avant si nécessaire ) """
2021-06-16 16:59:31 +02:00
)
2021-06-19 23:21:37 +02:00
insem = do_formsemestre_inscription_list (
2021-08-19 10:28:35 +02:00
args = { " formsemestre_id " : formsemestre_id , " etudid " : etudid }
2021-06-16 16:59:31 +02:00
)
if not insem :
2022-09-03 11:41:56 +02:00
raise ScoValueError ( f " { etud . nomprenom } n ' est pas inscrit au semestre ! " )
2021-06-16 16:59:31 +02:00
insem = insem [ 0 ]
# -- desinscription de tous les modules
cnx = ndb . GetDBConnexion ( )
cursor = cnx . cursor ( cursor_factory = ndb . ScoDocCursor )
cursor . execute (
2021-08-22 15:36:17 +02:00
""" SELECT Im.id AS moduleimpl_inscription_id
2022-09-03 11:41:56 +02:00
FROM notes_moduleimpl_inscription Im , notes_moduleimpl M
WHERE Im . etudid = % ( etudid ) s
2021-08-22 15:36:17 +02:00
and Im . moduleimpl_id = M . id
and M . formsemestre_id = % ( formsemestre_id ) s
""" ,
2021-06-16 16:59:31 +02:00
{ " etudid " : etudid , " formsemestre_id " : formsemestre_id } ,
)
res = cursor . fetchall ( )
moduleimpl_inscription_ids = [ x [ 0 ] for x in res ]
for moduleimpl_inscription_id in moduleimpl_inscription_ids :
sco_moduleimpl . do_moduleimpl_inscription_delete (
2021-08-20 01:09:55 +02:00
moduleimpl_inscription_id , formsemestre_id = formsemestre_id
2021-06-16 16:59:31 +02:00
)
2022-09-03 11:41:56 +02:00
# -- désincription du semestre
2021-06-16 16:59:31 +02:00
do_formsemestre_inscription_delete (
2021-08-21 00:24:51 +02:00
insem [ " formsemestre_inscription_id " ] , formsemestre_id = formsemestre_id
2021-06-16 16:59:31 +02:00
)
2023-02-23 21:48:40 +01:00
sco_cache . invalidate_formsemestre ( formsemestre_id = formsemestre_id )
2021-06-16 16:59:31 +02:00
# --- Semestre extérieur
2022-09-03 11:41:56 +02:00
if formsemestre . modalite == " EXT " :
2021-06-19 23:21:37 +02:00
inscrits = do_formsemestre_inscription_list (
2021-08-19 10:28:35 +02:00
args = { " formsemestre_id " : formsemestre_id }
2021-06-16 16:59:31 +02:00
)
nbinscrits = len ( inscrits )
if nbinscrits == 0 :
log (
2022-09-03 11:41:56 +02:00
f """ do_formsemestre_desinscription:
suppression du semestre extérieur { formsemestre } """
2021-06-16 16:59:31 +02:00
)
2022-09-03 11:41:56 +02:00
flash ( " Semestre exterieur supprimé " )
2021-08-21 00:24:51 +02:00
sco_formsemestre_edit . do_formsemestre_delete ( formsemestre_id )
2021-06-16 16:59:31 +02:00
2021-09-02 18:05:22 +02:00
logdb (
cnx ,
method = " formsemestre_desinscription " ,
etudid = etudid ,
msg = " desinscription semestre %s " % formsemestre_id ,
commit = False ,
)
2021-06-16 16:59:31 +02:00
2020-09-26 16:19:37 +02:00
def do_formsemestre_inscription_with_modules (
formsemestre_id ,
etudid ,
2022-12-02 16:54:43 +01:00
group_ids : list = None ,
2022-09-30 22:43:39 +02:00
etat = scu . INSCRIT ,
2020-12-01 16:46:45 +01:00
etape = None ,
2020-09-26 16:19:37 +02:00
method = " inscription_with_modules " ,
) :
2020-12-01 16:46:45 +01:00
""" Inscrit cet etudiant à ce semestre et TOUS ses modules STANDARDS
2020-09-26 16:19:37 +02:00
( donc sauf le sport )
"""
2022-12-02 16:54:43 +01:00
group_ids = group_ids or [ ]
if isinstance ( group_ids , int ) :
group_ids = [ group_ids ]
2022-05-29 17:34:03 +02:00
formsemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
2020-09-26 16:19:37 +02:00
# inscription au semestre
args = { " formsemestre_id " : formsemestre_id , " etudid " : etudid }
if etat is not None :
args [ " etat " ] = etat
2022-12-04 16:06:32 +01:00
if etape is not None :
args [ " etape " ] = etape
2021-08-21 00:24:51 +02:00
do_formsemestre_inscription_create ( args , method = method )
2020-09-26 16:19:37 +02:00
log (
2022-12-06 13:06:50 +01:00
f """ do_formsemestre_inscription_with_modules: etudid= {
etudid } formsemestre_id = { formsemestre_id } """
2020-09-26 16:19:37 +02:00
)
# inscriptions aux groupes
# 1- inscrit au groupe 'tous'
2021-07-29 16:31:15 +02:00
group_id = sco_groups . get_default_group ( formsemestre_id )
2021-08-19 10:28:35 +02:00
sco_groups . set_group ( etudid , group_id )
2020-09-26 16:19:37 +02:00
gdone = { group_id : 1 } # empeche doublons
# 2- inscrit aux groupes
for group_id in group_ids :
2022-12-06 13:06:50 +01:00
if group_id and group_id not in gdone :
2022-05-26 23:45:57 +02:00
group = GroupDescr . query . get_or_404 ( group_id )
2022-08-25 12:47:17 +02:00
sco_groups . set_group ( etudid , group_id )
gdone [ group_id ] = 1
2020-09-26 16:19:37 +02:00
2022-05-29 17:34:03 +02:00
# Inscription à tous les modules de ce semestre
2021-10-15 14:00:51 +02:00
modimpls = sco_moduleimpl . moduleimpl_withmodule_list (
2021-08-20 01:09:55 +02:00
formsemestre_id = formsemestre_id
2021-01-17 22:31:28 +01:00
)
2020-09-26 16:19:37 +02:00
for mod in modimpls :
if mod [ " ue " ] [ " type " ] != UE_SPORT :
2021-01-17 22:31:28 +01:00
sco_moduleimpl . do_moduleimpl_inscription_create (
2020-09-26 16:19:37 +02:00
{ " moduleimpl_id " : mod [ " moduleimpl_id " ] , " etudid " : etudid } ,
formsemestre_id = formsemestre_id ,
)
2022-05-29 17:34:03 +02:00
# Mise à jour des inscriptions aux parcours:
formsemestre . update_inscriptions_parcours_from_groups ( )
2020-09-26 16:19:37 +02:00
def formsemestre_inscription_with_modules_etud (
2021-09-27 10:20:10 +02:00
formsemestre_id , etudid = None , group_ids = None
2020-09-26 16:19:37 +02:00
) :
""" Form. inscription d ' un étudiant au semestre.
Si etudid n ' est pas specifié, form. choix etudiant.
"""
2021-08-22 13:24:36 +02:00
if etudid is None :
2020-09-26 16:19:37 +02:00
return sco_find_etud . form_search_etud (
title = " Choix de l ' étudiant à inscrire dans ce semestre " ,
add_headers = True ,
2021-08-22 14:23:58 +02:00
dest_url = " notes.formsemestre_inscription_with_modules_etud " ,
2020-09-26 16:19:37 +02:00
parameters = { " formsemestre_id " : formsemestre_id } ,
2021-08-22 14:23:58 +02:00
parameters_keys = " formsemestre_id " ,
2020-09-26 16:19:37 +02:00
)
return formsemestre_inscription_with_modules (
2021-09-27 10:20:10 +02:00
etudid , formsemestre_id , group_ids = group_ids
2020-09-26 16:19:37 +02:00
)
2021-08-21 00:24:51 +02:00
def formsemestre_inscription_with_modules_form ( etudid , only_ext = False ) :
2020-09-26 16:19:37 +02:00
""" Formulaire inscription de l ' etud dans l ' un des semestres existants.
Si only_ext , ne montre que les semestre extérieurs .
"""
2022-09-27 12:21:19 +02:00
etud : Identite = Identite . query . filter_by (
id = etudid , dept_id = g . scodoc_dept_id
) . first_or_404 ( )
2021-06-13 23:37:14 +02:00
H = [
2021-07-29 16:31:15 +02:00
html_sco_header . sco_header ( ) ,
2022-09-27 12:21:19 +02:00
f " <h2>Inscription de { etud . nomprenom } " ,
2021-06-13 23:37:14 +02:00
]
2020-09-26 16:19:37 +02:00
if only_ext :
H . append ( " dans un semestre extérieur " )
H . append (
""" </h2>
2022-09-27 12:21:19 +02:00
< p class = " help " > L ' étudiant sera inscrit à <em>tous</em> les modules du semestre
2020-09-26 16:19:37 +02:00
choisi ( sauf Sport & amp ; Culture ) .
< / p >
< h3 > Choisir un semestre : < / h3 > """
)
2022-09-27 12:21:19 +02:00
footer = html_sco_header . sco_footer ( )
# sems = sco_formsemestre.do_formsemestre_list(args={"etat": "1"})
formsemestres = (
FormSemestre . query . filter_by ( etat = True , dept_id = g . scodoc_dept_id )
. join ( Formation )
. order_by (
Formation . acronyme ,
FormSemestre . semestre_id ,
FormSemestre . modalite ,
FormSemestre . date_debut ,
)
. all ( )
)
if len ( formsemestres ) :
2020-09-26 16:19:37 +02:00
H . append ( " <ul> " )
2022-09-27 12:21:19 +02:00
for formsemestre in formsemestres :
# Ne propose que les semestres où etudid n'est pas déjà inscrit
if formsemestre . id not in {
ins . formsemestre_id for ins in etud . inscriptions ( )
} :
if ( not only_ext ) or ( formsemestre . modalite == " EXT " ) :
2020-09-26 16:19:37 +02:00
H . append (
2022-09-27 12:21:19 +02:00
f """
< li >
< a class = " stdlink " href = " {
url_for ( " notes.formsemestre_inscription_with_modules " ,
scodoc_dept = g . scodoc_dept ,
etudid = etudid , formsemestre_id = formsemestre . id
) } " > { formsemestre.titre_mois()}</a>
< / li >
2020-09-26 16:19:37 +02:00
"""
)
H . append ( " </ul> " )
else :
H . append ( " <p>aucune session de formation !</p> " )
H . append (
2022-09-27 12:21:19 +02:00
f """ <h3>ou</h3> <a class= " stdlink " href= " {
url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid )
} " >retour à la fiche de {etud.nomprenom} </a> " " "
2020-09-26 16:19:37 +02:00
)
2022-09-27 12:21:19 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
def formsemestre_inscription_with_modules (
2021-09-27 10:20:10 +02:00
etudid , formsemestre_id , group_ids = None , multiple_ok = False
2020-09-26 16:19:37 +02:00
) :
"""
Inscription de l ' etud dans ce semestre.
Formulaire avec choix groupe .
"""
log (
2022-09-25 16:30:02 +02:00
f """ formsemestre_inscription_with_modules: etudid= { etudid } formsemestre_id= {
formsemestre_id } group_ids = { group_ids } """
2020-09-26 16:19:37 +02:00
)
if multiple_ok :
multiple_ok = int ( multiple_ok )
2022-09-25 16:30:02 +02:00
formsemestre : FormSemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
etud : Identite = Identite . query . get_or_404 ( etudid )
if etud . dept_id != formsemestre . dept_id :
raise ScoValueError ( " l ' étudiant n ' est pas dans ce département " )
2020-09-26 16:19:37 +02:00
H = [
2021-06-13 23:37:14 +02:00
html_sco_header . html_sem_header (
2022-09-25 16:30:02 +02:00
f " Inscription de { etud . nomprenom } dans ce semestre " ,
2020-09-26 16:19:37 +02:00
)
]
2022-09-25 16:30:02 +02:00
footer = html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
# Check 1: déjà inscrit ici ?
2022-09-25 16:30:02 +02:00
inscr = FormSemestreInscription . query . filter_by (
etudid = etud . id , formsemestre_id = formsemestre . id
) . first ( )
if inscr is not None :
2020-09-26 16:19:37 +02:00
H . append (
2022-09-25 16:30:02 +02:00
f """
< p class = " warning " > { etud . nomprenom } est déjà inscrit
dans le semestre { formsemestre . titre_mois ( ) }
< / p >
< ul >
< li > < a href = " { url_for( " scolar . ficheEtud " , scodoc_dept=g.scodoc_dept, etudid=etudid)
} " class= " stdlink " >retour à la fiche de {etud.nomprenom} </a>
< / li >
< li > < a href = " { url_for(
2021-07-11 17:37:12 +02:00
" notes.formsemestre_status " ,
scodoc_dept = g . scodoc_dept ,
formsemestre_id = formsemestre_id ,
2022-09-25 16:30:02 +02:00
) } " class= " stdlink " >retour au tableau de bord de { formsemestre.titre_mois()}</a></li>
< / ul >
"""
2020-09-26 16:19:37 +02:00
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
# Check 2: déjà inscrit dans un semestre recouvrant les même dates ?
# Informe et propose dé-inscriptions
2021-08-21 00:24:51 +02:00
others = est_inscrit_ailleurs ( etudid , formsemestre_id )
2020-09-26 16:19:37 +02:00
if others and not multiple_ok :
l = [ ]
for s in others :
l . append (
2022-09-25 16:30:02 +02:00
f """ <a class= " discretelink " href= " {
url_for ( " notes.formsemestre_status " ,
scodoc_dept = g . scodoc_dept , formsemestre_id = s [ ' formsemestre_id ' ] )
} " > {s['titremois']} </a> " " "
2020-09-26 16:19:37 +02:00
)
H . append (
2022-09-25 16:30:02 +02:00
f """ <p class= " warning " >Attention: { etud . nomprenom } est déjà inscrit sur
la même période dans : { " , " . join ( l ) } .
< / p > """
2020-09-26 16:19:37 +02:00
)
H . append ( " <ul> " )
for s in others :
H . append (
2022-09-25 16:30:02 +02:00
f """ <li><a href= " {
url_for ( " notes.formsemestre_desinscription " , scodoc_dept = g . scodoc_dept ,
formsemestre_id = s [ " formsemestre_id " ] , etudid = etudid )
} " class= " stdlink " >désinscrire de {s["titreannee"]}
< / li > """
2020-09-26 16:19:37 +02:00
)
H . append ( " </ul> " )
H . append (
2022-09-25 16:30:02 +02:00
f """ <p><a href= " { url_for ( " notes.formsemestre_inscription_with_modules " ,
scodoc_dept = g . scodoc_dept , etudid = etudid , formsemestre_id = formsemestre_id ,
multiple_ok = 1 ,
group_ids = group_ids )
} " >Continuer quand même l ' inscription</a>
< / p > """
# was sco_groups.make_query_groups(group_ids)
2020-09-26 16:19:37 +02:00
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
#
if group_ids is not None :
# OK, inscription
do_formsemestre_inscription_with_modules (
formsemestre_id ,
etudid ,
group_ids = group_ids ,
2022-09-30 22:43:39 +02:00
etat = scu . INSCRIT ,
2020-09-26 16:19:37 +02:00
method = " formsemestre_inscription_with_modules " ,
)
2021-07-31 18:01:10 +02:00
return flask . redirect (
2021-07-11 17:37:12 +02:00
url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid )
)
2020-09-26 16:19:37 +02:00
else :
# formulaire choix groupe
H . append (
2022-09-25 16:30:02 +02:00
f """ <form method= " GET " name= " groupesel " action= " { request . base_url } " >
< input type = " hidden " name = " etudid " value = " {etudid} " >
< input type = " hidden " name = " formsemestre_id " value = " {formsemestre_id} " >
2020-09-26 16:19:37 +02:00
"""
)
2021-08-19 10:28:35 +02:00
H . append ( sco_groups . form_group_choice ( formsemestre_id , allow_none = True ) )
2020-09-26 16:19:37 +02:00
#
H . append (
"""
< input type = " submit " value = " Inscrire " / >
< p > Note : l ' étudiant sera inscrit dans les groupes sélectionnés</p>
< / form >
"""
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
2021-09-27 10:20:10 +02:00
def formsemestre_inscription_option ( etudid , formsemestre_id ) :
2020-12-01 16:46:45 +01:00
""" Dialogue pour (dés)inscription à des modules optionnels. """
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( formsemestre_id )
2021-08-10 12:57:38 +02:00
if not sem [ " etat " ] :
2020-09-26 16:19:37 +02:00
raise ScoValueError ( " Modification impossible: semestre verrouille " )
2021-08-22 13:24:36 +02:00
etud = sco_etud . get_etud_info ( etudid = etudid , filled = True ) [ 0 ]
2022-02-13 15:50:16 +01:00
formsemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
nt : NotesTableCompat = res_sem . load_formsemestre_results ( formsemestre )
2020-09-26 16:19:37 +02:00
2022-09-25 16:30:02 +02:00
footer = html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
H = [
2021-07-29 16:31:15 +02:00
html_sco_header . sco_header ( )
2020-09-26 16:19:37 +02:00
+ " <h2>Inscription de %s aux modules de %s ( %s - %s )</h2> "
% ( etud [ " nomprenom " ] , sem [ " titre_num " ] , sem [ " date_debut " ] , sem [ " date_fin " ] )
]
# Cherche les moduleimpls et les inscriptions
2021-10-15 14:00:51 +02:00
mods = sco_moduleimpl . moduleimpl_withmodule_list ( formsemestre_id = formsemestre_id )
2021-08-20 01:09:55 +02:00
inscr = sco_moduleimpl . do_moduleimpl_inscription_list ( etudid = etudid )
2020-09-26 16:19:37 +02:00
# Formulaire
2023-03-08 22:23:55 +01:00
modimpls_by_ue_ids = collections . defaultdict ( list ) # ue_id : [ moduleimpl_id ]
modimpls_by_ue_names = collections . defaultdict ( list ) # ue_id : [ moduleimpl_name ]
2020-09-26 16:19:37 +02:00
ues = [ ]
2020-12-02 01:00:23 +01:00
ue_ids = set ( )
2020-09-26 16:19:37 +02:00
initvalues = { }
for mod in mods :
ue_id = mod [ " ue " ] [ " ue_id " ]
if not ue_id in ue_ids :
ues . append ( mod [ " ue " ] )
ue_ids . add ( ue_id )
modimpls_by_ue_ids [ ue_id ] . append ( mod [ " moduleimpl_id " ] )
modimpls_by_ue_names [ ue_id ] . append (
2022-02-14 10:05:55 +01:00
" %s %s " % ( mod [ " module " ] [ " code " ] or " " , mod [ " module " ] [ " titre " ] or " " )
2020-09-26 16:19:37 +02:00
)
2021-09-28 09:14:04 +02:00
vals = scu . get_request_args ( )
2021-09-27 10:20:10 +02:00
if not vals . get ( " tf_submitted " , False ) :
2020-09-26 16:19:37 +02:00
# inscrit ?
for ins in inscr :
if ins [ " moduleimpl_id " ] == mod [ " moduleimpl_id " ] :
key = " moduleimpls_ %s " % ue_id
if key in initvalues :
2021-08-30 23:28:15 +02:00
initvalues [ key ] . append ( str ( mod [ " moduleimpl_id " ] ) )
2020-09-26 16:19:37 +02:00
else :
2021-08-30 23:28:15 +02:00
initvalues [ key ] = [ str ( mod [ " moduleimpl_id " ] ) ]
2020-09-26 16:19:37 +02:00
break
descr = [
( " formsemestre_id " , { " input_type " : " hidden " } ) ,
( " etudid " , { " input_type " : " hidden " } ) ,
]
for ue in ues :
ue_id = ue [ " ue_id " ]
ue_descr = ue [ " acronyme " ]
if ue [ " type " ] != UE_STANDARD :
ue_descr + = " <em> %s </em> " % UE_TYPE_NAME [ ue [ " type " ] ]
ue_status = nt . get_etud_ue_status ( etudid , ue_id )
2022-02-12 22:57:46 +01:00
if ue_status and ue_status [ " is_capitalized " ] :
2021-08-19 10:28:35 +02:00
sem_origin = sco_formsemestre . get_formsemestre ( ue_status [ " formsemestre_id " ] )
2022-05-26 23:45:57 +02:00
ue_descr + = (
' <a class= " discretelink " href= " formsemestre_bulletinetud?formsemestre_id= %s &etudid= %s " title= " %s " >(capitalisée le %s ) '
% (
sem_origin [ " formsemestre_id " ] ,
etudid ,
sem_origin [ " titreannee " ] ,
ndb . DateISOtoDMY ( ue_status [ " event_date " ] ) ,
)
2020-09-26 16:19:37 +02:00
)
descr . append (
(
" sec_ %s " % ue_id ,
{
" input_type " : " separator " ,
2021-08-30 23:28:15 +02:00
" title " : """ <b> %s :</b> <a href= " # " onclick= " chkbx_select( ' %s ' , true); " >inscrire</a> | <a href= " # " onclick= " chkbx_select( ' %s ' , false); " >désinscrire</a> à tous les modules """
2020-09-26 16:19:37 +02:00
% ( ue_descr , ue_id , ue_id ) ,
} ,
)
)
descr . append (
(
" moduleimpls_ %s " % ue_id ,
{
" input_type " : " checkbox " ,
" title " : " " ,
" dom_id " : ue_id ,
2021-08-30 23:28:15 +02:00
" allowed_values " : [ str ( x ) for x in modimpls_by_ue_ids [ ue_id ] ] ,
2020-09-26 16:19:37 +02:00
" labels " : modimpls_by_ue_names [ ue_id ] ,
" vertical " : True ,
} ,
)
)
H . append (
""" <script type= " text/javascript " >
function chkbx_select ( field_id , state ) {
var elems = document . getElementById ( field_id ) . getElementsByTagName ( " input " ) ;
for ( var i = 0 ; i < elems . length ; i + + ) {
elems [ i ] . checked = state ;
}
}
< / script >
"""
)
tf = TrivialFormulator (
2021-09-18 10:10:02 +02:00
request . base_url ,
2021-09-27 16:42:14 +02:00
scu . get_request_args ( ) ,
2020-09-26 16:19:37 +02:00
descr ,
initvalues ,
cancelbutton = " Annuler " ,
method = " post " ,
submitlabel = " Modifier les inscriptions " ,
cssclass = " inscription " ,
name = " tf " ,
)
if tf [ 0 ] == 0 :
H . append (
2022-09-25 16:30:02 +02:00
"""
< p > Voici la liste des modules du semestre choisi . < / p >
< p >
Les modules cochés sont ceux dans lesquels l ' étudiant est inscrit.
Vous pouvez l ' inscrire ou le désincrire d ' un ou plusieurs modules .
< / p >
< p > Attention : cette méthode ne devrait être utilisée que pour les modules
< b > optionnels < / b > ( ou les activités culturelles et sportives ) et pour désinscrire
les étudiants dispensés ( UE validées ) .
< / p >
2020-09-26 16:19:37 +02:00
"""
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + " \n " + tf [ 1 ] + footer
2020-09-26 16:19:37 +02:00
elif tf [ 0 ] == - 1 :
2021-07-31 18:01:10 +02:00
return flask . redirect (
2021-07-11 17:37:12 +02:00
url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid )
2020-09-26 16:19:37 +02:00
)
else :
# Inscriptions aux modules choisis
# il faut desinscrire des modules qui ne figurent pas
# et inscrire aux autres, sauf si deja inscrit
a_desinscrire = { } . fromkeys ( [ x [ " moduleimpl_id " ] for x in mods ] )
insdict = { }
for ins in inscr :
insdict [ ins [ " moduleimpl_id " ] ] = ins
for ue in ues :
ue_id = ue [ " ue_id " ]
2021-08-30 23:28:15 +02:00
for moduleimpl_id in [ int ( x ) for x in tf [ 2 ] [ " moduleimpls_ %s " % ue_id ] ] :
2021-07-09 17:47:06 +02:00
if moduleimpl_id in a_desinscrire :
2020-09-26 16:19:37 +02:00
del a_desinscrire [ moduleimpl_id ]
# supprime ceux auxquel pas inscrit
2021-08-30 23:28:15 +02:00
moduleimpls_a_desinscrire = list ( a_desinscrire . keys ( ) )
for moduleimpl_id in moduleimpls_a_desinscrire :
2021-07-09 17:47:06 +02:00
if moduleimpl_id not in insdict :
2020-09-26 16:19:37 +02:00
del a_desinscrire [ moduleimpl_id ]
2020-12-02 01:00:23 +01:00
a_inscrire = set ( )
2020-09-26 16:19:37 +02:00
for ue in ues :
ue_id = ue [ " ue_id " ]
2021-08-30 23:28:15 +02:00
a_inscrire . update (
int ( x ) for x in tf [ 2 ] [ " moduleimpls_ %s " % ue_id ]
) # conversion en int !
2020-09-26 16:19:37 +02:00
# supprime ceux auquel deja inscrit:
for ins in inscr :
if ins [ " moduleimpl_id " ] in a_inscrire :
a_inscrire . remove ( ins [ " moduleimpl_id " ] )
# dict des modules:
modsdict = { }
for mod in mods :
modsdict [ mod [ " moduleimpl_id " ] ] = mod
#
if ( not a_inscrire ) and ( not a_desinscrire ) :
H . append (
""" <h3>Aucune modification à effectuer</h3>
2021-07-11 17:37:12 +02:00
< p > < a class = " stdlink " href = " %s " > retour à la fiche étudiant < / a > < / p >
"""
% url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid )
2020-09-26 16:19:37 +02:00
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
H . append ( " <h3>Confirmer les modifications:</h3> " )
if a_desinscrire :
H . append (
" <p> %s va être <b>désinscrit %s </b> des modules:<ul><li> "
% ( etud [ " nomprenom " ] , etud [ " ne " ] )
)
H . append (
" </li><li> " . join (
[
" %s ( %s ) "
% (
modsdict [ x ] [ " module " ] [ " titre " ] ,
2022-02-14 10:05:55 +01:00
modsdict [ x ] [ " module " ] [ " code " ] or " (module sans code) " ,
2020-09-26 16:19:37 +02:00
)
for x in a_desinscrire
]
)
+ " </p> "
)
H . append ( " </li></ul> " )
if a_inscrire :
H . append (
" <p> %s va être <b>inscrit %s </b> aux modules:<ul><li> "
% ( etud [ " nomprenom " ] , etud [ " ne " ] )
)
H . append (
" </li><li> " . join (
[
" %s ( %s ) "
% (
modsdict [ x ] [ " module " ] [ " titre " ] ,
2022-02-14 10:05:55 +01:00
modsdict [ x ] [ " module " ] [ " code " ] or " (module sans code) " ,
2020-09-26 16:19:37 +02:00
)
for x in a_inscrire
]
)
+ " </p> "
)
H . append ( " </li></ul> " )
2021-08-30 23:28:15 +02:00
modulesimpls_ainscrire = " , " . join ( str ( x ) for x in a_inscrire )
modulesimpls_adesinscrire = " , " . join ( str ( x ) for x in a_desinscrire )
2020-09-26 16:19:37 +02:00
H . append (
""" <form action= " do_moduleimpl_incription_options " >
< input type = " hidden " name = " etudid " value = " %s " / >
< input type = " hidden " name = " modulesimpls_ainscrire " value = " %s " / >
< input type = " hidden " name = " modulesimpls_adesinscrire " value = " %s " / >
< input type = " submit " value = " Confirmer " / >
2021-07-11 17:37:12 +02:00
< input type = " button " value = " Annuler " onclick = " document.location= ' %s ' ; " / >
2020-09-26 16:19:37 +02:00
< / form >
"""
% (
etudid ,
modulesimpls_ainscrire ,
modulesimpls_adesinscrire ,
2021-07-11 17:37:12 +02:00
url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid ) ,
2020-09-26 16:19:37 +02:00
)
)
2022-09-25 16:30:02 +02:00
return " \n " . join ( H ) + footer
2020-09-26 16:19:37 +02:00
def do_moduleimpl_incription_options (
2021-09-27 10:20:10 +02:00
etudid , modulesimpls_ainscrire , modulesimpls_adesinscrire
2020-09-26 16:19:37 +02:00
) :
"""
Effectue l ' inscription et la description aux modules optionnels
"""
2021-08-30 23:28:15 +02:00
if isinstance ( modulesimpls_ainscrire , int ) :
modulesimpls_ainscrire = str ( modulesimpls_ainscrire )
if isinstance ( modulesimpls_adesinscrire , int ) :
modulesimpls_adesinscrire = str ( modulesimpls_adesinscrire )
2020-09-26 16:19:37 +02:00
if modulesimpls_ainscrire :
2021-08-30 23:28:15 +02:00
a_inscrire = [ int ( x ) for x in modulesimpls_ainscrire . split ( " , " ) ]
2020-09-26 16:19:37 +02:00
else :
a_inscrire = [ ]
if modulesimpls_adesinscrire :
2021-08-30 23:28:15 +02:00
a_desinscrire = [ int ( x ) for x in modulesimpls_adesinscrire . split ( " , " ) ]
2020-09-26 16:19:37 +02:00
else :
a_desinscrire = [ ]
# inscriptions
for moduleimpl_id in a_inscrire :
# verifie que ce module existe bien
2021-10-15 14:00:51 +02:00
mods = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = moduleimpl_id )
2020-09-26 16:19:37 +02:00
if len ( mods ) != 1 :
raise ScoValueError (
" inscription: invalid moduleimpl_id: %s " % moduleimpl_id
)
mod = mods [ 0 ]
2021-01-17 22:31:28 +01:00
sco_moduleimpl . do_moduleimpl_inscription_create (
2020-09-26 16:19:37 +02:00
{ " moduleimpl_id " : moduleimpl_id , " etudid " : etudid } ,
formsemestre_id = mod [ " formsemestre_id " ] ,
)
# desinscriptions
for moduleimpl_id in a_desinscrire :
# verifie que ce module existe bien
2021-10-15 14:00:51 +02:00
mods = sco_moduleimpl . moduleimpl_list ( moduleimpl_id = moduleimpl_id )
2020-09-26 16:19:37 +02:00
if len ( mods ) != 1 :
raise ScoValueError (
" desinscription: invalid moduleimpl_id: %s " % moduleimpl_id
)
mod = mods [ 0 ]
2021-01-17 22:31:28 +01:00
inscr = sco_moduleimpl . do_moduleimpl_inscription_list (
2021-08-20 01:09:55 +02:00
moduleimpl_id = moduleimpl_id , etudid = etudid
2020-09-26 16:19:37 +02:00
)
if not inscr :
raise ScoValueError (
2020-12-02 01:00:23 +01:00
" pas inscrit a ce module ! (etudid= %s , moduleimpl_id= %s ) "
2020-09-26 16:19:37 +02:00
% ( etudid , moduleimpl_id )
)
oid = inscr [ 0 ] [ " moduleimpl_inscription_id " ]
2021-01-17 22:31:28 +01:00
sco_moduleimpl . do_moduleimpl_inscription_delete (
2021-08-20 01:09:55 +02:00
oid , formsemestre_id = mod [ " formsemestre_id " ]
2020-09-26 16:19:37 +02:00
)
2021-09-27 10:20:10 +02:00
H = [
html_sco_header . sco_header ( ) ,
""" <h3>Modifications effectuées</h3>
< p > < a class = " stdlink " href = " %s " >
Retour à la fiche étudiant < / a > < / p >
"""
% url_for ( " scolar.ficheEtud " , scodoc_dept = g . scodoc_dept , etudid = etudid ) ,
html_sco_header . sco_footer ( ) ,
]
return " \n " . join ( H )
2020-09-26 16:19:37 +02:00
2021-08-21 00:24:51 +02:00
def est_inscrit_ailleurs ( etudid , formsemestre_id ) :
2020-09-26 16:19:37 +02:00
""" Vrai si l ' étudiant est inscrit dans un semestre en même
temps que celui indiqué ( par formsemestre_id ) .
Retourne la liste des semestres concernés ( ou liste vide ) .
"""
2021-08-22 13:24:36 +02:00
etud = sco_etud . get_etud_info ( etudid = etudid , filled = True ) [ 0 ]
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( formsemestre_id )
2020-09-26 16:19:37 +02:00
debut_s = sem [ " dateord " ]
2021-06-19 23:21:37 +02:00
fin_s = ndb . DateDMYtoISO ( sem [ " date_fin " ] )
2020-09-26 16:19:37 +02:00
r = [ ]
for s in etud [ " sems " ] :
if s [ " formsemestre_id " ] != formsemestre_id :
debut = s [ " dateord " ]
2021-06-19 23:21:37 +02:00
fin = ndb . DateDMYtoISO ( s [ " date_fin " ] )
2020-09-26 16:19:37 +02:00
if debut < fin_s and fin > debut_s :
r . append ( s ) # intersection
return r
2021-08-21 00:24:51 +02:00
def list_inscrits_ailleurs ( formsemestre_id ) :
2020-09-26 16:19:37 +02:00
""" Liste des etudiants inscrits ailleurs en même temps que formsemestre_id.
Pour chacun , donne la liste des semestres .
{ etudid : [ liste de sems ] }
"""
2022-02-13 15:50:16 +01:00
formsemestre = FormSemestre . query . get_or_404 ( formsemestre_id )
nt : NotesTableCompat = res_sem . load_formsemestre_results ( formsemestre )
2020-09-26 16:19:37 +02:00
etudids = nt . get_etudids ( )
d = { }
for etudid in etudids :
2021-08-21 00:24:51 +02:00
d [ etudid ] = est_inscrit_ailleurs ( etudid , formsemestre_id )
2020-09-26 16:19:37 +02:00
return d
2021-09-27 10:20:10 +02:00
def formsemestre_inscrits_ailleurs ( formsemestre_id ) :
2020-09-26 16:19:37 +02:00
""" Page listant les étudiants inscrits dans un autre semestre
dont les dates recouvrent le semestre indiqué .
"""
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre . get_formsemestre ( formsemestre_id )
2020-09-26 16:19:37 +02:00
H = [
2021-06-13 23:37:14 +02:00
html_sco_header . html_sem_header (
" Inscriptions multiples parmi les étudiants du semestre " ,
2020-09-26 16:19:37 +02:00
)
]
2021-08-21 00:24:51 +02:00
insd = list_inscrits_ailleurs ( formsemestre_id )
2020-09-26 16:19:37 +02:00
# liste ordonnée par nom
etudlist = [
2021-08-22 13:24:36 +02:00
sco_etud . get_etud_info ( etudid = etudid , filled = True ) [ 0 ]
2020-09-26 16:19:37 +02:00
for etudid in insd . keys ( )
if insd [ etudid ]
]
etudlist . sort ( key = lambda x : x [ " nom " ] )
if etudlist :
H . append ( " <ul> " )
for etud in etudlist :
H . append (
2021-07-11 17:37:12 +02:00
' <li><a href= " %s " class= " discretelink " > %s </a> : '
% (
url_for (
" scolar.ficheEtud " ,
scodoc_dept = g . scodoc_dept ,
etudid = etud [ " etudid " ] ,
) ,
etud [ " nomprenom " ] ,
)
2020-09-26 16:19:37 +02:00
)
l = [ ]
for s in insd [ etud [ " etudid " ] ] :
l . append (
' <a class= " discretelink " href= " formsemestre_status?formsemestre_id= %(formsemestre_id)s " > %(titremois)s </a> '
% s
)
H . append ( " , " . join ( l ) )
H . append ( " </li> " )
H . append ( " </ul> " )
H . append ( " <p>Total: %d étudiants concernés.</p> " % len ( etudlist ) )
H . append (
2022-10-02 23:43:29 +02:00
""" <p class= " help " >Ces étudiants sont inscrits dans le semestre sélectionné et aussi dans d ' autres semestres qui se déroulent en même temps ! <br>Sauf exception, cette situation est anormale:</p>
2020-09-26 16:19:37 +02:00
< ul >
< li > vérifier que les dates des semestres se suivent sans se chevaucher < / li >
< li > ou si besoin désinscrire le ( s ) étudiant ( s ) de l ' un des semestres (via leurs fiches individuelles).</li>
< / ul >
"""
)
else :
H . append ( """ <p>Aucun étudiant en inscription multiple (c ' est normal) !</p> """ )
2021-07-29 10:19:00 +02:00
return " \n " . join ( H ) + html_sco_header . sco_footer ( )