2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2021-01-01 17:51:08 +01:00
# Copyright (c) 1999 - 2021 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
#
##############################################################################
2021-06-16 18:18:32 +02:00
""" Ajout/Modification/Suppression UE
2020-09-26 16:19:37 +02:00
"""
2021-06-19 23:21:37 +02:00
import app . scodoc . notesdb as ndb
import app . scodoc . sco_utils as scu
from app . scodoc . notes_log import log
from app . scodoc . TrivialFormulator import TrivialFormulator , TF
from app . scodoc . gen_tables import GenTable
from app . scodoc . sco_permissions import Permission
from app . scodoc . sco_exceptions import ScoValueError , ScoLockedFormError
from app . scodoc import html_sco_header
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 sco_codes_parcours
2021-07-19 19:53:01 +02:00
from app . scodoc import sco_edit_formation
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_edit_matiere
from app . scodoc import sco_edit_module
2021-07-19 19:53:01 +02:00
from app . scodoc import sco_etud
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formsemestre
from app . scodoc import sco_groups
from app . scodoc import sco_moduleimpl
from app . scodoc import sco_news
from app . scodoc import sco_permissions
from app . scodoc import sco_preferences
from app . scodoc import sco_tag_module
2020-09-26 16:19:37 +02:00
2021-06-16 18:18:32 +02:00
_ueEditor = ndb . EditableTable (
" notes_ue " ,
" ue_id " ,
(
" ue_id " ,
" formation_id " ,
" acronyme " ,
" numero " ,
" titre " ,
" type " ,
" ue_code " ,
" ects " ,
" is_external " ,
" code_apogee " ,
" coefficient " ,
) ,
sortkey = " numero " ,
input_formators = { " type " : ndb . int_null_is_zero } ,
output_formators = {
" numero " : ndb . int_null_is_zero ,
" ects " : ndb . float_null_is_null ,
" coefficient " : ndb . float_null_is_zero ,
} ,
)
def do_ue_list ( context , * args , * * kw ) :
" list UEs "
cnx = ndb . GetDBConnexion ( )
return _ueEditor . list ( cnx , * args , * * kw )
def do_ue_create ( context , args , REQUEST ) :
" create an ue "
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formations
2021-06-16 18:18:32 +02:00
cnx = ndb . GetDBConnexion ( )
# check duplicates
2021-06-17 00:08:37 +02:00
ues = do_ue_list (
context , { " formation_id " : args [ " formation_id " ] , " acronyme " : args [ " acronyme " ] }
2021-06-16 18:18:32 +02:00
)
if ues :
raise ScoValueError ( ' Acronyme d \' UE " %s " déjà utilisé ! ' % args [ " acronyme " ] )
# create
2021-06-17 00:08:37 +02:00
r = _ueEditor . create ( cnx , args )
2021-06-16 18:18:32 +02:00
# news
F = sco_formations . formation_list (
context , args = { " formation_id " : args [ " formation_id " ] }
) [ 0 ]
sco_news . add (
2021-06-19 23:21:37 +02:00
typ = sco_news . NEWS_FORM ,
2021-06-16 18:18:32 +02:00
object = args [ " formation_id " ] ,
text = " Modification de la formation %(acronyme)s " % F ,
)
return r
def do_ue_delete ( context , ue_id , delete_validations = False , REQUEST = None , force = False ) :
2021-06-19 23:21:37 +02:00
" delete UE and attached matieres (but not modules) "
from app . scodoc import sco_formations
from app . scodoc import sco_parcours_dut
2021-06-16 18:18:32 +02:00
cnx = ndb . GetDBConnexion ( )
log ( " do_ue_delete: ue_id= %s , delete_validations= %s " % ( ue_id , delete_validations ) )
# check
ue = do_ue_list ( context , { " ue_id " : ue_id } )
if not ue :
raise ScoValueError ( " UE inexistante ! " )
ue = ue [ 0 ]
2021-06-19 23:21:37 +02:00
if ue_is_locked ( context , ue [ " ue_id " ] ) :
2021-06-16 18:18:32 +02:00
raise ScoLockedFormError ( )
# Il y a-t-il des etudiants ayant validé cette UE ?
# si oui, propose de supprimer les validations
validations = sco_parcours_dut . scolar_formsemestre_validation_list (
cnx , args = { " ue_id " : ue_id }
)
if validations and not delete_validations and not force :
return scu . confirm_dialog (
context ,
" <p> %d étudiants ont validé l ' UE %s ( %s )</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p> "
% ( len ( validations ) , ue [ " acronyme " ] , ue [ " titre " ] ) ,
dest_url = " " ,
REQUEST = REQUEST ,
target_variable = " delete_validations " ,
cancel_url = " ue_list?formation_id= %s " % ue [ " formation_id " ] ,
parameters = { " ue_id " : ue_id , " dialog_confirmed " : 1 } ,
)
if delete_validations :
log ( " deleting all validations of UE %s " % ue_id )
ndb . SimpleQuery (
" DELETE FROM scolar_formsemestre_validation WHERE ue_id= %(ue_id)s " ,
{ " ue_id " : ue_id } ,
)
# delete all matiere in this UE
mats = sco_edit_matiere . do_matiere_list ( context , { " ue_id " : ue_id } )
for mat in mats :
sco_edit_matiere . do_matiere_delete ( context , mat [ " matiere_id " ] , REQUEST )
# delete uecoef and events
ndb . SimpleQuery (
" DELETE FROM notes_formsemestre_uecoef WHERE ue_id= %(ue_id)s " ,
{ " ue_id " : ue_id } ,
)
2021-07-28 17:03:54 +02:00
ndb . SimpleQuery ( " DELETE FROM scolar_events WHERE ue_id= %(ue_id)s " , { " ue_id " : ue_id } )
2021-06-16 18:18:32 +02:00
cnx = ndb . GetDBConnexion ( )
2021-06-17 00:08:37 +02:00
_ueEditor . delete ( cnx , ue_id )
2021-06-16 18:18:32 +02:00
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?):
2021-07-19 19:53:01 +02:00
sco_cache . invalidate_formsemestre ( )
2021-06-16 18:18:32 +02:00
# news
F = sco_formations . formation_list (
context , args = { " formation_id " : ue [ " formation_id " ] }
) [ 0 ]
sco_news . add (
2021-06-19 23:21:37 +02:00
typ = sco_news . NEWS_FORM ,
2021-06-16 18:18:32 +02:00
object = ue [ " formation_id " ] ,
text = " Modification de la formation %(acronyme)s " % F ,
)
#
if not force :
return REQUEST . RESPONSE . redirect (
scu . NotesURL ( ) + " /ue_list?formation_id= " + str ( ue [ " formation_id " ] )
)
else :
return None
2020-09-26 16:19:37 +02:00
def ue_create ( context , formation_id = None , REQUEST = None ) :
2021-01-01 18:40:47 +01:00
""" Creation d ' une UE """
2020-09-26 16:19:37 +02:00
return ue_edit ( context , create = True , formation_id = formation_id , REQUEST = REQUEST )
def ue_edit ( context , ue_id = None , create = False , formation_id = None , REQUEST = None ) :
2021-01-01 18:40:47 +01:00
""" Modification ou creation d ' une UE """
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formations
2020-09-26 16:19:37 +02:00
create = int ( create )
if not create :
2021-06-16 18:18:32 +02:00
U = do_ue_list ( context , args = { " ue_id " : ue_id } )
2020-09-26 16:19:37 +02:00
if not U :
raise ScoValueError ( " UE inexistante ! " )
U = U [ 0 ]
formation_id = U [ " formation_id " ]
title = " Modification de l ' UE %(titre)s " % U
initvalues = U
submitlabel = " Modifier les valeurs "
else :
title = " Création d ' une UE "
initvalues = { }
submitlabel = " Créer cette UE "
2021-06-16 18:18:32 +02:00
Fol = sco_formations . formation_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
if not Fol :
raise ScoValueError (
" Formation %s inexistante ! (si vous avez suivi un lien valide, merci de signaler le problème) "
% formation_id
)
Fo = Fol [ 0 ]
parcours = sco_codes_parcours . get_parcours_from_code ( Fo [ " type_parcours " ] )
H = [
2021-06-13 19:12:20 +02:00
html_sco_header . sco_header (
context , REQUEST , page_title = title , javascripts = [ " js/edit_ue.js " ]
) ,
2020-09-26 16:19:37 +02:00
" <h2> " + title ,
" (formation %(acronyme)s , version %(version)s )</h2> " % Fo ,
"""
< p class = " help " > Les UE sont des groupes de modules dans une formation donnée , utilisés pour l ' évaluation (on calcule des moyennes par UE et applique des seuils ( " barres " )).
< / p >
< p class = " help " > Note : L ' UE n ' a pas de coefficient associé . Seuls les < em > modules < / em > ont des coefficients .
< / p > """ ,
]
ue_types = parcours . ALLOWED_UE_TYPES
ue_types . sort ( )
2021-02-03 22:00:41 +01:00
ue_types_names = [ sco_codes_parcours . UE_TYPE_NAME [ k ] for k in ue_types ]
2020-09-26 16:19:37 +02:00
ue_types = [ str ( x ) for x in ue_types ]
fw = [
( " ue_id " , { " input_type " : " hidden " } ) ,
( " create " , { " input_type " : " hidden " , " default " : create } ) ,
( " formation_id " , { " input_type " : " hidden " , " default " : formation_id } ) ,
( " titre " , { " size " : 30 , " explanation " : " nom de l ' UE " } ) ,
( " acronyme " , { " size " : 8 , " explanation " : " abbréviation " , " allow_null " : False } ) ,
(
" numero " ,
{
" size " : 2 ,
" explanation " : " numéro (1,2,3,4) de l ' UE pour l ' ordre d ' affichage " ,
" type " : " int " ,
} ,
) ,
(
" type " ,
{
" explanation " : " type d ' UE " ,
" input_type " : " menu " ,
" allowed_values " : ue_types ,
" labels " : ue_types_names ,
} ,
) ,
(
" ects " ,
{
" size " : 4 ,
" type " : " float " ,
" title " : " ECTS " ,
" explanation " : " nombre de crédits ECTS " ,
} ,
) ,
(
" coefficient " ,
{
" size " : 4 ,
" type " : " float " ,
" title " : " Coefficient " ,
" explanation " : """ les coefficients d ' UE ne sont utilisés que lorsque
l ' option <em>Utiliser les coefficients d ' UE pour calculer la moyenne générale < / em >
est activée . Par défaut , le coefficient d ' une UE est simplement la somme des
coefficients des modules dans lesquels l ' étudiant a des notes.
""" ,
} ,
) ,
(
" ue_code " ,
{
" size " : 12 ,
" title " : " Code UE " ,
" explanation " : " code interne (optionnel). Toutes les UE partageant le même code (et le même code de formation) sont compatibles (compensation de semestres, capitalisation d ' UE). Voir liste ci-dessous. " ,
} ,
) ,
(
" code_apogee " ,
{
" title " : " Code Apogée " ,
2021-06-24 23:09:06 +02:00
" size " : 25 ,
" explanation " : " (optionnel) code élément pédagogique Apogée ou liste de codes ELP séparés par des virgules " ,
2020-09-26 16:19:37 +02:00
} ,
) ,
]
if parcours . UE_IS_MODULE :
# demande le semestre pour creer le module immediatement:
2021-07-09 17:47:06 +02:00
semestres_indices = list ( range ( 1 , parcours . NB_SEM + 1 ) )
2020-09-26 16:19:37 +02:00
fw . append (
(
" semestre_id " ,
{
" input_type " : " menu " ,
" type " : " int " ,
2021-02-04 20:02:44 +01:00
" title " : scu . strcapitalize ( parcours . SESSION_NAME ) ,
2020-09-26 16:19:37 +02:00
" explanation " : " %s de début du module dans la formation "
% parcours . SESSION_NAME ,
" labels " : [ str ( x ) for x in semestres_indices ] ,
" allowed_values " : semestres_indices ,
} ,
)
)
if create and not parcours . UE_IS_MODULE :
fw . append (
(
" create_matiere " ,
{
" input_type " : " boolcheckbox " ,
" default " : False ,
" title " : " Créer matière identique " ,
" explanation " : " créer immédiatement une matière dans cette UE (utile si on n ' utilise pas de matières) " ,
} ,
)
)
tf = TrivialFormulator (
REQUEST . URL0 , REQUEST . form , fw , initvalues = initvalues , submitlabel = submitlabel
)
if tf [ 0 ] == 0 :
X = """ <div id= " ue_list_code " ></div>
"""
2021-07-29 10:19:00 +02:00
return " \n " . join ( H ) + tf [ 1 ] + X + html_sco_header . sco_footer ( )
2020-09-26 16:19:37 +02:00
else :
if create :
if not tf [ 2 ] [ " ue_code " ] :
del tf [ 2 ] [ " ue_code " ]
if not tf [ 2 ] [ " numero " ] :
if not " semestre_id " in tf [ 2 ] :
tf [ 2 ] [ " semestre_id " ] = 0
# numero regroupant par semestre ou année:
tf [ 2 ] [ " numero " ] = next_ue_numero (
context , formation_id , int ( tf [ 2 ] [ " semestre_id " ] or 0 )
)
2021-06-16 18:18:32 +02:00
ue_id = do_ue_create ( context , tf [ 2 ] , REQUEST )
2020-09-26 16:19:37 +02:00
if parcours . UE_IS_MODULE or tf [ 2 ] [ " create_matiere " ] :
2021-06-17 00:08:37 +02:00
matiere_id = sco_edit_matiere . do_matiere_create (
context ,
{ " ue_id " : ue_id , " titre " : tf [ 2 ] [ " titre " ] , " numero " : 1 } ,
REQUEST ,
2020-09-26 16:19:37 +02:00
)
if parcours . UE_IS_MODULE :
# dans ce mode, crée un (unique) module dans l'UE:
2021-06-17 00:08:37 +02:00
_ = sco_edit_module . do_module_create (
context ,
2020-09-26 16:19:37 +02:00
{
" titre " : tf [ 2 ] [ " titre " ] ,
" code " : tf [ 2 ] [ " acronyme " ] ,
" coefficient " : 1.0 , # tous les modules auront coef 1, et on utilisera les ECTS
" ue_id " : ue_id ,
" matiere_id " : matiere_id ,
" formation_id " : formation_id ,
" semestre_id " : tf [ 2 ] [ " semestre_id " ] ,
} ,
REQUEST ,
)
else :
2021-02-01 23:54:46 +01:00
do_ue_edit ( context , tf [ 2 ] )
2020-09-26 16:19:37 +02:00
return REQUEST . RESPONSE . redirect (
2021-06-15 12:34:33 +02:00
scu . NotesURL ( ) + " /ue_list?formation_id= " + formation_id
2020-09-26 16:19:37 +02:00
)
def _add_ue_semestre_id ( context , ue_list ) :
""" ajoute semestre_id dans les ue, en regardant le premier module de chacune.
2021-01-01 18:40:47 +01:00
Les UE sans modules se voient attribuer le numero UE_SEM_DEFAULT ( 1000000 ) ,
2020-09-26 16:19:37 +02:00
qui les place à la fin de la liste .
"""
for ue in ue_list :
2021-06-16 18:18:32 +02:00
Modlist = sco_edit_module . do_module_list ( context , args = { " ue_id " : ue [ " ue_id " ] } )
2020-09-26 16:19:37 +02:00
if Modlist :
ue [ " semestre_id " ] = Modlist [ 0 ] [ " semestre_id " ]
else :
ue [ " semestre_id " ] = 1000000
def next_ue_numero ( context , formation_id , semestre_id = None ) :
""" Numero d ' une nouvelle UE dans cette formation.
Si le semestre est specifie , cherche les UE ayant des modules de ce semestre
"""
2021-06-16 18:18:32 +02:00
ue_list = do_ue_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
if not ue_list :
return 0
if semestre_id is None :
return ue_list [ - 1 ] [ " numero " ] + 1000
else :
# Avec semestre: (prend le semestre du 1er module de l'UE)
_add_ue_semestre_id ( context , ue_list )
ue_list_semestre = [ ue for ue in ue_list if ue [ " semestre_id " ] == semestre_id ]
if ue_list_semestre :
return ue_list_semestre [ - 1 ] [ " numero " ] + 10
else :
return ue_list [ - 1 ] [ " numero " ] + 1000
def ue_delete (
context , ue_id = None , delete_validations = False , dialog_confirmed = False , REQUEST = None
) :
""" Delete an UE """
2021-06-16 18:18:32 +02:00
ue = do_ue_list ( context , args = { " ue_id " : ue_id } )
2020-09-26 16:19:37 +02:00
if not ue :
raise ScoValueError ( " UE inexistante ! " )
ue = ue [ 0 ]
if not dialog_confirmed :
2021-06-02 22:40:34 +02:00
return scu . confirm_dialog (
context ,
2020-09-26 16:19:37 +02:00
" <h2>Suppression de l ' UE %(titre)s ( %(acronyme)s ))</h2> " % ue ,
dest_url = " " ,
REQUEST = REQUEST ,
parameters = { " ue_id " : ue_id } ,
cancel_url = " ue_list?formation_id= %s " % ue [ " formation_id " ] ,
)
2021-06-16 18:18:32 +02:00
return do_ue_delete (
context , ue_id , delete_validations = delete_validations , REQUEST = REQUEST
2020-09-26 16:19:37 +02:00
)
def ue_list ( context , formation_id = None , msg = " " , REQUEST = None ) :
2021-01-01 18:40:47 +01:00
""" Liste des matières et modules d ' une formation, avec liens pour
2020-09-26 16:19:37 +02:00
editer ( si non verrouillée ) .
"""
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formations
from app . scodoc import sco_formsemestre_validation
2020-09-26 16:19:37 +02:00
authuser = REQUEST . AUTHENTICATED_USER
2021-06-16 18:18:32 +02:00
F = sco_formations . formation_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
if not F :
raise ScoValueError ( " invalid formation_id " )
F = F [ 0 ]
parcours = sco_codes_parcours . get_parcours_from_code ( F [ " type_parcours " ] )
2021-06-16 18:18:32 +02:00
locked = sco_formations . formation_has_locked_sems ( context , formation_id )
2020-09-26 16:19:37 +02:00
2021-06-16 18:18:32 +02:00
ue_list = do_ue_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
# tri par semestre et numero:
_add_ue_semestre_id ( context , ue_list )
ue_list . sort ( key = lambda u : ( u [ " semestre_id " ] , u [ " numero " ] ) )
has_duplicate_ue_codes = len ( set ( [ ue [ " ue_code " ] for ue in ue_list ] ) ) != len ( ue_list )
2021-06-15 15:38:38 +02:00
perm_change = authuser . has_permission ( Permission . ScoChangeFormation )
2020-09-26 16:19:37 +02:00
# editable = (not locked) and perm_change
# On autorise maintanant la modification des formations qui ont des semestres verrouillés,
# sauf si cela affect les notes passées (verrouillées):
# - pas de modif des modules utilisés dans des semestres verrouillés
# - pas de changement des codes d'UE utilisés dans des semestres verrouillés
editable = perm_change
2021-06-12 22:43:22 +02:00
tag_editable = (
2021-06-15 15:38:38 +02:00
authuser . has_permission ( Permission . ScoEditFormationTags ) or perm_change
2021-06-12 22:43:22 +02:00
)
2020-09-26 16:19:37 +02:00
if locked :
2021-02-04 20:02:44 +01:00
lockicon = scu . icontag ( " lock32_img " , title = " verrouillé " )
2020-09-26 16:19:37 +02:00
else :
lockicon = " "
arrow_up , arrow_down , arrow_none = sco_groups . getArrowIconsTags ( context , REQUEST )
2021-02-04 20:02:44 +01:00
delete_icon = scu . icontag (
2020-09-26 16:19:37 +02:00
" delete_small_img " , title = " Supprimer (module inutilisé) " , alt = " supprimer "
)
2021-02-04 20:02:44 +01:00
delete_disabled_icon = scu . icontag (
2020-09-26 16:19:37 +02:00
" delete_small_dis_img " , title = " Suppression impossible (module utilisé) "
)
H = [
2021-06-13 19:12:20 +02:00
html_sco_header . sco_header (
context ,
2020-09-26 16:19:37 +02:00
REQUEST ,
cssstyles = [ " libjs/jQuery-tagEditor/jquery.tag-editor.css " ] ,
javascripts = [
" libjs/jinplace-1.2.1.min.js " ,
" js/ue_list.js " ,
" libjs/jQuery-tagEditor/jquery.tag-editor.min.js " ,
" libjs/jQuery-tagEditor/jquery.caret.min.js " ,
" js/module_tag_editor.js " ,
] ,
page_title = " Programme %s " % F [ " acronyme " ] ,
) ,
""" <h2>Formation %(titre)s ( %(acronyme)s ) [version %(version)s ] code %(formation_code)s """
% F ,
lockicon ,
" </h2> " ,
]
if locked :
H . append (
""" <p class= " help " >Cette formation est verrouillée car %d semestres verrouillés s ' y réferent.
Si vous souhaitez modifier cette formation ( par exemple pour y ajouter un module ) , vous devez :
< / p >
< ul class = " help " >
< li > soit créer une nouvelle version de cette formation pour pouvoir l ' éditer librement (vous pouvez passer par la fonction " Associer à une nouvelle version du programme " (menu " Semestre " ) si vous avez un semestre en cours);</li>
< li > soit déverrouiller le ou les semestres qui s ' y réfèrent (attention, en principe ces semestres sont archivés
et ne devraient pas être modifiés ) . < / li >
< / ul > """
% len ( locked )
)
if msg :
H . append ( ' <p class= " msg " > ' + msg + " </p> " )
if has_duplicate_ue_codes :
H . append (
""" <div class= " ue_warning " ><span>Attention: plusieurs UE de cette formation ont le même code. Il faut corriger cela ci-dessous, sinon les calculs d ' ECTS seront erronés !</span></div> """
)
# Description de la formation
H . append ( ' <div class= " formation_descr " > ' )
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Titre:</span><span class= " fd_v " > %(titre)s </span></div> '
% F
)
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Titre officiel:</span><span class= " fd_v " > %(titre_officiel)s </span></div> '
% F
)
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Acronyme:</span><span class= " fd_v " > %(acronyme)s </span></div> '
% F
)
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Code:</span><span class= " fd_v " > %(formation_code)s </span></div> '
% F
)
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Version:</span><span class= " fd_v " > %(version)s </span></div> '
% F
)
H . append (
' <div class= " fd_d " ><span class= " fd_t " >Type parcours:</span><span class= " fd_v " > %s </span></div> '
% parcours . __doc__
)
if parcours . UE_IS_MODULE :
H . append (
' <div class= " fd_d " ><span class= " fd_t " > </span><span class= " fd_n " >(Chaque module est une UE)</span></div> '
)
if editable :
H . append (
' <div><a href= " formation_edit?formation_id= %(formation_id)s " class= " stdlink " >modifier ces informations</a></div> '
% F
)
H . append ( " </div> " )
# Description des UE/matières/modules
H . append ( ' <div class= " formation_ue_list " > ' )
H . append ( ' <div class= " ue_list_tit " >Programme pédagogique:</div> ' )
H . append (
' <form><input type= " checkbox " class= " sco_tag_checkbox " >montrer les tags</input></form> '
)
cur_ue_semestre_id = None
iue = 0
for UE in ue_list :
if UE [ " ects " ] :
UE [ " ects_str " ] = " , %g ECTS " % UE [ " ects " ]
else :
UE [ " ects_str " ] = " "
if editable :
klass = " span_apo_edit "
else :
klass = " "
UE [ " code_apogee_str " ] = (
""" , Apo: <span class= " %s " data-url= " edit_ue_set_code_apogee " id= " %s " data-placeholder= " %s " > """
2021-02-04 20:02:44 +01:00
% ( klass , UE [ " ue_id " ] , scu . APO_MISSING_CODE_STR )
2020-09-26 16:19:37 +02:00
+ ( UE [ " code_apogee " ] or " " )
+ " </span> "
)
if cur_ue_semestre_id != UE [ " semestre_id " ] :
cur_ue_semestre_id = UE [ " semestre_id " ]
if iue > 0 :
H . append ( " </ul> " )
2021-02-03 22:00:41 +01:00
if UE [ " semestre_id " ] == sco_codes_parcours . UE_SEM_DEFAULT :
2020-09-26 16:19:37 +02:00
lab = " Pas d ' indication de semestre: "
else :
lab = " Semestre %s : " % UE [ " semestre_id " ]
H . append ( ' <div class= " ue_list_tit_sem " > %s </div> ' % lab )
H . append ( ' <ul class= " notes_ue_list " > ' )
H . append ( ' <li class= " notes_ue_list " > ' )
if iue != 0 and editable :
H . append (
2021-05-11 11:48:32 +02:00
' <a href= " ue_move?ue_id= %s &after=0 " class= " aud " > %s </a> '
2020-09-26 16:19:37 +02:00
% ( UE [ " ue_id " ] , arrow_up )
)
else :
H . append ( arrow_none )
if iue < len ( ue_list ) - 1 and editable :
H . append (
2021-05-11 11:48:32 +02:00
' <a href= " ue_move?ue_id= %s &after=1 " class= " aud " > %s </a> '
2020-09-26 16:19:37 +02:00
% ( UE [ " ue_id " ] , arrow_down )
)
else :
H . append ( arrow_none )
iue + = 1
UE [ " acro_titre " ] = str ( UE [ " acronyme " ] )
if UE [ " titre " ] != UE [ " acronyme " ] :
UE [ " acro_titre " ] + = " " + str ( UE [ " titre " ] )
H . append (
""" %(acro_titre)s <span class= " ue_code " >(code %(ue_code)s %(ects_str)s , coef. %(coefficient)3.2f %(code_apogee_str)s )</span>
< span class = " ue_coef " > < / span >
"""
% UE
)
2021-02-03 22:00:41 +01:00
if UE [ " type " ] != sco_codes_parcours . UE_STANDARD :
H . append (
' <span class= " ue_type " > %s </span> '
% sco_codes_parcours . UE_TYPE_NAME [ UE [ " type " ] ]
)
2021-06-13 18:29:53 +02:00
ue_editable = editable and not ue_is_locked ( context , UE [ " ue_id " ] )
2020-09-26 16:19:37 +02:00
if ue_editable :
H . append (
' <a class= " stdlink " href= " ue_edit?ue_id= %(ue_id)s " >modifier</a> ' % UE
)
else :
H . append ( ' <span class= " locked " >[verrouillé]</span> ' )
if not parcours . UE_IS_MODULE :
H . append ( ' <ul class= " notes_matiere_list " > ' )
2021-06-16 18:18:32 +02:00
Matlist = sco_edit_matiere . do_matiere_list ( context , args = { " ue_id " : UE [ " ue_id " ] } )
2020-09-26 16:19:37 +02:00
for Mat in Matlist :
if not parcours . UE_IS_MODULE :
H . append ( ' <li class= " notes_matiere_list " > ' )
2021-06-13 19:12:20 +02:00
if editable and not sco_edit_matiere . matiere_is_locked (
context , Mat [ " matiere_id " ]
) :
2020-09-26 16:19:37 +02:00
H . append (
' <a class= " stdlink " href= " matiere_edit?matiere_id= %(matiere_id)s " > '
% Mat
)
H . append ( " %(titre)s " % Mat )
2021-06-13 19:12:20 +02:00
if editable and not sco_edit_matiere . matiere_is_locked (
context , Mat [ " matiere_id " ]
) :
2020-09-26 16:19:37 +02:00
H . append ( " </a> " )
H . append ( ' <ul class= " notes_module_list " > ' )
2021-06-17 00:08:37 +02:00
Modlist = sco_edit_module . do_module_list (
context , args = { " matiere_id " : Mat [ " matiere_id " ] }
)
2020-09-26 16:19:37 +02:00
im = 0
for Mod in Modlist :
2021-06-13 19:12:20 +02:00
Mod [ " nb_moduleimpls " ] = sco_edit_module . module_count_moduleimpls (
context , Mod [ " module_id " ]
2020-09-26 16:19:37 +02:00
)
klass = " notes_module_list "
2021-02-04 20:02:44 +01:00
if Mod [ " module_type " ] == scu . MODULE_MALUS :
2020-09-26 16:19:37 +02:00
klass + = " module_malus "
H . append ( ' <li class= " %s " > ' % klass )
H . append ( ' <span class= " notes_module_list_buts " > ' )
if im != 0 and editable :
H . append (
2021-05-11 11:48:32 +02:00
' <a href= " module_move?module_id= %s &after=0 " class= " aud " > %s </a> '
2020-09-26 16:19:37 +02:00
% ( Mod [ " module_id " ] , arrow_up )
)
else :
H . append ( arrow_none )
if im < len ( Modlist ) - 1 and editable :
H . append (
2021-05-11 11:48:32 +02:00
' <a href= " module_move?module_id= %s &after=1 " class= " aud " > %s </a> '
2020-09-26 16:19:37 +02:00
% ( Mod [ " module_id " ] , arrow_down )
)
else :
H . append ( arrow_none )
im + = 1
if Mod [ " nb_moduleimpls " ] == 0 and editable :
H . append (
' <a class= " smallbutton " href= " module_delete?module_id= %s " > %s </a> '
% ( Mod [ " module_id " ] , delete_icon )
)
else :
H . append ( delete_disabled_icon )
H . append ( " </span> " )
2021-06-13 19:12:20 +02:00
mod_editable = editable # and not sco_edit_module.module_is_locked(context, Mod['module_id'])
2020-09-26 16:19:37 +02:00
if mod_editable :
H . append (
' <a class= " discretelink " title= " Modifier le module numéro %(numero)s , utilisé par %(nb_moduleimpls)d sessions " href= " module_edit?module_id= %(module_id)s " > '
% Mod
)
H . append (
' <span class= " formation_module_tit " > %s </span> '
2021-02-04 20:02:44 +01:00
% scu . join_words ( Mod [ " code " ] , Mod [ " titre " ] )
2020-09-26 16:19:37 +02:00
)
if mod_editable :
H . append ( " </a> " )
heurescoef = (
" %(heures_cours)s / %(heures_td)s / %(heures_tp)s , coef. %(coefficient)s "
% Mod
)
if mod_editable :
klass = " span_apo_edit "
else :
klass = " "
heurescoef + = (
' , Apo: <span class= " %s " data-url= " edit_module_set_code_apogee " id= " %s " data-placeholder= " %s " > '
2021-02-04 20:02:44 +01:00
% ( klass , Mod [ " module_id " ] , scu . APO_MISSING_CODE_STR )
2020-09-26 16:19:37 +02:00
+ ( Mod [ " code_apogee " ] or " " )
+ " </span> "
)
if tag_editable :
tag_cls = " module_tag_editor "
else :
tag_cls = " module_tag_editor_ro "
tag_mk = """ <span class= " sco_tag_edit " ><form><textarea data-module_id= " {} " class= " {} " > {} </textarea></form></span> """
tag_edit = tag_mk . format (
Mod [ " module_id " ] ,
tag_cls ,
" , " . join ( sco_tag_module . module_tag_list ( context , Mod [ " module_id " ] ) ) ,
)
H . append (
" %s %s " % ( parcours . SESSION_NAME , Mod [ " semestre_id " ] )
+ " ( %s ) " % heurescoef
+ tag_edit
)
H . append ( " </li> " )
if not Modlist :
H . append ( " <li>Aucun module dans cette matière ! " )
if editable :
H . append (
' <a class= " stdlink " href= " matiere_delete?matiere_id= %(matiere_id)s " >supprimer cette matière</a> '
% Mat
)
H . append ( " </li> " )
if editable : # and ((not parcours.UE_IS_MODULE) or len(Modlist) == 0):
H . append (
' <li> <a class= " stdlink " href= " module_create?matiere_id= %(matiere_id)s " >créer un module</a></li> '
% Mat
)
H . append ( " </ul> " )
H . append ( " </li> " )
if not Matlist :
H . append ( " <li>Aucune matière dans cette UE ! " )
if editable :
H . append (
""" <a class= " stdlink " href= " ue_delete?ue_id= %(ue_id)s " >supprimer l ' UE</a> """
% UE
)
H . append ( " </li> " )
if editable and not parcours . UE_IS_MODULE :
H . append (
' <li><a class= " stdlink " href= " matiere_create?ue_id= %(ue_id)s " >créer une matière</a> </li> '
% UE
)
if not parcours . UE_IS_MODULE :
H . append ( " </ul> " )
H . append ( " </ul> " )
if editable :
H . append (
' <ul><li><a class= " stdlink " href= " ue_create?formation_id= %s " >Ajouter une UE</a></li> '
% formation_id
)
H . append (
' <li><a href= " formation_add_malus_modules?formation_id= %(formation_id)s " class= " stdlink " >Ajouter des modules de malus dans chaque UE</a></li></ul> '
% F
)
H . append ( " </div> " ) # formation_ue_list
H . append ( " <p><ul> " )
if editable :
H . append (
"""
< li > < a class = " stdlink " href = " formation_create_new_version?formation_id= %(formation_id)s " > Créer une nouvelle version ( non verrouillée ) < / a > < / li >
"""
% F
)
H . append (
"""
< li > < a class = " stdlink " href = " formation_table_recap?formation_id= %(formation_id)s " > Table récapitulative de la formation < / a > < / li >
2021-05-11 11:48:32 +02:00
< li > < a class = " stdlink " href = " formation_export?formation_id= %(formation_id)s &format=xml " > Export XML de la formation < / a > ( permet de la sauvegarder pour l ' échanger avec un autre site)</li>
2020-09-26 16:19:37 +02:00
2021-05-11 11:48:32 +02:00
< li > < a class = " stdlink " href = " formation_export?formation_id= %(formation_id)s &format=json " > Export JSON de la formation < / a > < / li >
2020-09-26 16:19:37 +02:00
< li > < a class = " stdlink " href = " module_list?formation_id= %(formation_id)s " > Liste détaillée des modules de la formation < / a > ( debug ) < / li >
< / ul >
< / p > """
% F
)
if perm_change :
H . append (
"""
< h3 > < a name = " sems " > Semestres ou sessions de cette formation < / a > < / h3 >
< p > < ul > """
)
for sem in sco_formsemestre . do_formsemestre_list (
context , args = { " formation_id " : formation_id }
) :
H . append (
' <li><a class= " stdlink " href= " formsemestre_status?formsemestre_id= %(formsemestre_id)s " > %(titremois)s </a> '
% sem
)
if sem [ " etat " ] != " 1 " :
H . append ( " [verrouillé] " )
else :
H . append (
2021-05-11 11:48:32 +02:00
' <a class= " stdlink " href= " formsemestre_editwithmodules?formation_id= %(formation_id)s &formsemestre_id= %(formsemestre_id)s " >Modifier</a> '
2020-09-26 16:19:37 +02:00
% sem
)
H . append ( " </li> " )
H . append ( " </ul> " )
2021-06-15 15:38:38 +02:00
if authuser . has_permission ( Permission . ScoImplement ) :
2020-09-26 16:19:37 +02:00
H . append (
""" <ul>
2021-05-11 11:48:32 +02:00
< li > < a class = " stdlink " href = " formsemestre_createwithmodules?formation_id= %(formation_id)s &semestre_id=1 " > Mettre en place un nouveau semestre de formation % ( acronyme ) s < / a >
2020-09-26 16:19:37 +02:00
< / li >
< / ul > """
% F
)
# <li>(debug) <a class="stdlink" href="check_form_integrity?formation_id=%(formation_id)s">Vérifier cohérence</a></li>
2021-02-05 21:35:21 +01:00
warn , _ = sco_formsemestre_validation . check_formation_ues ( context , formation_id )
2020-09-26 16:19:37 +02:00
H . append ( warn )
2021-07-29 10:19:00 +02:00
H . append ( html_sco_header . sco_footer ( ) )
2020-09-26 16:19:37 +02:00
return " " . join ( H )
def ue_sharing_code ( context , ue_code = None , ue_id = None , hide_ue_id = None ) :
""" HTML list of UE sharing this code
Either ue_code or ue_id may be specified .
"""
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formations
2020-09-26 16:19:37 +02:00
if ue_id :
2021-06-16 18:18:32 +02:00
ue = do_ue_list ( context , args = { " ue_id " : ue_id } ) [ 0 ]
2020-09-26 16:19:37 +02:00
if not ue_code :
ue_code = ue [ " ue_code " ]
2021-06-16 18:18:32 +02:00
F = sco_formations . formation_list (
context , args = { " formation_id " : ue [ " formation_id " ] }
) [ 0 ]
2020-09-26 16:19:37 +02:00
formation_code = F [ " formation_code " ]
2021-06-16 18:18:32 +02:00
ue_list_all = do_ue_list ( context , args = { " ue_code " : ue_code } )
2020-09-26 16:19:37 +02:00
if ue_id :
# retire les UE d'autres formations:
# log('checking ucode %s formation %s' % (ue_code, formation_code))
ue_list = [ ]
for ue in ue_list_all :
2021-06-16 18:18:32 +02:00
F = sco_formations . formation_list (
context , args = { " formation_id " : ue [ " formation_id " ] }
) [ 0 ]
2020-09-26 16:19:37 +02:00
if formation_code == F [ " formation_code " ] :
ue_list . append ( ue )
else :
ue_list = ue_list_all
if hide_ue_id : # enlève l'ue de depart
ue_list = [ ue for ue in ue_list if ue [ " ue_id " ] != hide_ue_id ]
if not ue_list :
if ue_id :
return """ <span class= " ue_share " >Seule UE avec code %s </span> """ % ue_code
else :
return """ <span class= " ue_share " >Aucune UE avec code %s </span> """ % ue_code
H = [ ]
if ue_id :
H . append ( ' <span class= " ue_share " >Autres UE avec le code %s :</span> ' % ue_code )
else :
H . append ( ' <span class= " ue_share " >UE avec le code %s :</span> ' % ue_code )
H . append ( " <ul> " )
for ue in ue_list :
2021-06-16 18:18:32 +02:00
F = sco_formations . formation_list (
context , args = { " formation_id " : ue [ " formation_id " ] }
) [ 0 ]
2020-09-26 16:19:37 +02:00
H . append (
' <li> %s ( %s ) dans <a class= " stdlink " href= " ue_list?formation_id= %s " > %s ( %s )</a>, version %s </li> '
% (
ue [ " acronyme " ] ,
ue [ " titre " ] ,
F [ " formation_id " ] ,
F [ " acronyme " ] ,
F [ " titre " ] ,
F [ " version " ] ,
)
)
H . append ( " </ul> " )
return " \n " . join ( H )
def do_ue_edit ( context , args , bypass_lock = False , dont_invalidate_cache = False ) :
" edit an UE "
# check
ue_id = args [ " ue_id " ]
2021-06-16 18:18:32 +02:00
ue = do_ue_list ( context , { " ue_id " : ue_id } ) [ 0 ]
2021-06-13 18:29:53 +02:00
if ( not bypass_lock ) and ue_is_locked ( context , ue [ " ue_id " ] ) :
2020-09-26 16:19:37 +02:00
raise ScoLockedFormError ( )
# check: acronyme unique dans cette formation
2021-07-09 17:47:06 +02:00
if " acronyme " in args :
2020-09-26 16:19:37 +02:00
new_acro = args [ " acronyme " ]
2021-06-17 00:08:37 +02:00
ues = do_ue_list (
context , { " formation_id " : ue [ " formation_id " ] , " acronyme " : new_acro }
2020-09-26 16:19:37 +02:00
)
if ues and ues [ 0 ] [ " ue_id " ] != ue_id :
raise ScoValueError ( ' Acronyme d \' UE " %s " déjà utilisé ! ' % args [ " acronyme " ] )
# On ne peut pas supprimer le code UE:
2021-07-09 17:47:06 +02:00
if " ue_code " in args and not args [ " ue_code " ] :
2020-09-26 16:19:37 +02:00
del args [ " ue_code " ]
2021-06-15 13:59:56 +02:00
cnx = ndb . GetDBConnexion ( )
2021-06-17 00:08:37 +02:00
_ueEditor . edit ( cnx , args )
2020-09-26 16:19:37 +02:00
if not dont_invalidate_cache :
# Invalide les semestres utilisant cette formation:
2021-07-19 19:53:01 +02:00
sco_edit_formation . invalidate_sems_in_formation ( ue [ " formation_id " ] )
2020-09-26 16:19:37 +02:00
# essai edition en ligne:
def edit_ue_set_code_apogee ( context , id = None , value = None , REQUEST = None ) :
" set UE code apogee "
ue_id = id
value = value . strip ( " -_ \t " )
log ( " edit_ue_set_code_apogee: ue_id= %s code_apogee= %s " % ( ue_id , value ) )
2021-06-16 18:18:32 +02:00
ues = do_ue_list ( context , args = { " ue_id " : ue_id } )
2020-09-26 16:19:37 +02:00
if not ues :
return " ue invalide "
do_ue_edit (
context ,
{ " ue_id " : ue_id , " code_apogee " : value } ,
bypass_lock = True ,
dont_invalidate_cache = False ,
)
if not value :
2021-02-04 20:02:44 +01:00
value = scu . APO_MISSING_CODE_STR
2020-09-26 16:19:37 +02:00
return value
2021-06-13 18:29:53 +02:00
def ue_is_locked ( context , ue_id ) :
""" True if UE should not be modified
( contains modules used in a locked formsemestre )
"""
r = ndb . SimpleDictFetch (
""" SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ue . ue_id = mod . ue_id
AND mi . module_id = mod . module_id AND mi . formsemestre_id = sem . formsemestre_id
AND ue . ue_id = % ( ue_id ) s AND sem . etat = 0
""" ,
{ " ue_id " : ue_id } ,
)
return len ( r ) > 0
2020-09-26 16:19:37 +02:00
# ---- Table recap formation
def formation_table_recap ( context , formation_id , format = " html " , REQUEST = None ) :
2021-01-16 11:49:02 +01:00
""" Table recapitulant formation. """
2021-06-19 23:21:37 +02:00
from app . scodoc import sco_formations
2021-06-16 18:18:32 +02:00
F = sco_formations . formation_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
if not F :
raise ScoValueError ( " invalid formation_id " )
F = F [ 0 ]
T = [ ]
2021-06-16 18:18:32 +02:00
ue_list = do_ue_list ( context , args = { " formation_id " : formation_id } )
2020-09-26 16:19:37 +02:00
for UE in ue_list :
2021-06-16 18:18:32 +02:00
Matlist = sco_edit_matiere . do_matiere_list ( context , args = { " ue_id " : UE [ " ue_id " ] } )
2020-09-26 16:19:37 +02:00
for Mat in Matlist :
2021-06-17 00:08:37 +02:00
Modlist = sco_edit_module . do_module_list (
context , args = { " matiere_id " : Mat [ " matiere_id " ] }
)
2020-09-26 16:19:37 +02:00
for Mod in Modlist :
2021-06-13 19:12:20 +02:00
Mod [ " nb_moduleimpls " ] = sco_edit_module . module_count_moduleimpls (
context , Mod [ " module_id " ]
2020-09-26 16:19:37 +02:00
)
#
T . append (
{
" UE_acro " : UE [ " acronyme " ] ,
" Mat_tit " : Mat [ " titre " ] ,
" Mod_tit " : Mod [ " abbrev " ] or Mod [ " titre " ] ,
" Mod_code " : Mod [ " code " ] ,
" Mod_coef " : Mod [ " coefficient " ] ,
" Mod_sem " : Mod [ " semestre_id " ] ,
" nb_moduleimpls " : Mod [ " nb_moduleimpls " ] ,
" heures_cours " : Mod [ " heures_cours " ] ,
" heures_td " : Mod [ " heures_td " ] ,
" heures_tp " : Mod [ " heures_tp " ] ,
" ects " : Mod [ " ects " ] ,
}
)
columns_ids = [
" UE_acro " ,
" Mat_tit " ,
" Mod_tit " ,
" Mod_code " ,
" Mod_coef " ,
" Mod_sem " ,
" nb_moduleimpls " ,
" heures_cours " ,
" heures_td " ,
" heures_tp " ,
" ects " ,
]
titles = {
" UE_acro " : " UE " ,
" Mat_tit " : " Matière " ,
" Mod_tit " : " Module " ,
" Mod_code " : " Code " ,
" Mod_coef " : " Coef. " ,
" Mod_sem " : " Sem. " ,
" nb_moduleimpls " : " Nb utilisé " ,
" heures_cours " : " Cours (h) " ,
" heures_td " : " TD (h) " ,
" heures_tp " : " TP (h) " ,
" ects " : " ECTS " ,
}
title = (
""" Formation %(titre)s ( %(acronyme)s ) [version %(version)s ] code %(formation_code)s """
% F
)
tab = GenTable (
columns_ids = columns_ids ,
rows = T ,
titles = titles ,
2021-02-05 21:35:21 +01:00
origin = " Généré par %s le " % scu . VERSION . SCONAME
+ scu . timedate_human_repr ( )
+ " " ,
2020-09-26 16:19:37 +02:00
caption = title ,
html_caption = title ,
html_class = " table_leftalign " ,
base_url = " %s ?formation_id= %s " % ( REQUEST . URL0 , formation_id ) ,
page_title = title ,
html_title = " <h2> " + title + " </h2> " ,
pdf_title = title ,
2021-07-28 17:03:54 +02:00
preferences = sco_preferences . SemPreferences ( ) ,
2020-09-26 16:19:37 +02:00
)
return tab . make_page ( context , format = format , REQUEST = REQUEST )
def ue_list_semestre_ids ( context , ue ) :
""" Liste triée des numeros de semestres des modules dans cette UE
Il est recommandable que tous les modules d ' une UE aient le même indice de semestre.
Mais cela n ' a pas toujours été le cas dans les programmes pédagogiques officiels,
aussi ScoDoc laisse le choix .
"""
2021-06-16 18:18:32 +02:00
Modlist = sco_edit_module . do_module_list ( context , args = { " ue_id " : ue [ " ue_id " ] } )
2020-09-26 16:19:37 +02:00
return sorted ( list ( set ( [ mod [ " semestre_id " ] for mod in Modlist ] ) ) )