[Scodoc-devel] [SVN] Scolar : [1273] - Integration code pour gerer "nom usuel" en plus des nom et prenom ( utile pour les etudiants qui se font appeler autrement que ce qu' indique leur etat civil);

eviennet at lipn.univ-paris13.fr eviennet at lipn.univ-paris13.fr
Dim 6 Oct 17:18:20 CEST 2013


Une pièce jointe HTML a été nettoyée...
URL: <https://www-rt.iutv.univ-paris13.fr/pipermail/scodoc-devel/attachments/20131006/ff139966/attachment-0001.html>
-------------- section suivante --------------
Modified: branches/ScoDoc7/ZNotes.py
===================================================================
--- branches/ScoDoc7/ZNotes.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/ZNotes.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -1334,7 +1334,6 @@
             if nb_abs:
                 nb_abs_just = self.Absences.CountAbsJust(etudid=etudid, debut=debut_sem, fin=fin_sem, moduleimpl_id=moduleimpl_id)
                 etud = self.getEtudInfo(etudid=etudid, filled=True)[0]
-                # nomprenom = etud['nomprenom'] # .capitalize() + ' ' + etud['nom']
                 T.append({
                     'nomprenom' : etud['nomprenom'],
                     'just' : nb_abs_just,

Modified: branches/ScoDoc7/ZScolar.py
===================================================================
--- branches/ScoDoc7/ZScolar.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/ZScolar.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -839,8 +839,9 @@
         
         formsemestre_id = group['formsemestre_id']
         #
-        columns_ids=['nom', 'prenom' ] # colonnes a inclure
-        titles = { 'nom' : 'Nom', 'prenom' : 'Pr\xE9nom',
+        columns_ids=['nom_disp', 'prenom' ] # colonnes a inclure
+        titles = { 'nom_disp' : 'Nom',
+                   'prenom' : 'Pr\xE9nom',
                    'email' : 'Mail',
                    'etat':'Etat',
                    'etudid':'etudid',
@@ -877,7 +878,7 @@
                 etud['_email_target'] = 'mailto:' + etud['email']
             else:
                 etud['_email_target'] = ''
-            etud['_nom_target'] = 'ficheEtud?etudid=' + etud['etudid']
+            etud['_nom_disp_target'] = 'ficheEtud?etudid=' + etud['etudid']
             etud['_prenom_target'] = 'ficheEtud?etudid=' + etud['etudid']
 
             etud['_nom_td_attrs'] = 'id="%s" class="etudinfo"' % (etud['etudid'])
@@ -964,7 +965,7 @@
             if not members:
                 return ''            
             keys = ['etudid', 'code_nip', 'etat',
-                    'sexe', 'nom','prenom',
+                    'sexe', 'nom', 'nom_usuel', 'prenom',
                     'inscriptionstr']
             if with_paiement:
                 keys.append('paiementinscription')
@@ -1019,7 +1020,7 @@
             etud = self.getEtudInfo(etudid=m['etudid'],filled=True)[0]
             etuds.append(etud)
         # tri par nom
-        etuds.sort( lambda x,y: cmp((x['nom'],x['prenom']),(y['nom'],y['prenom'])) )
+        etuds.sort( lambda x,y: cmp((x['nom_disp'],x['prenom']),(y['nom_disp'],y['prenom'])) )
         return etuds
         
     # -------------------------- INFOS SUR ETUDIANTS --------------------------
@@ -1049,12 +1050,6 @@
             % (etudid, code_nip, code_ine))
         return self.ScoErrorResponse( 'unknown student', format=format, REQUEST=REQUEST)
     
-    #
-    security.declareProtected(ScoView, 'nomprenom')
-    def nomprenom(self, etud):
-        "formatte sexe/nom/prenom pour affichages"
-        return ' '.join([ format_sexe(etud['sexe']), format_prenom(etud['prenom']), format_nom(etud['nom'])])
-    
     security.declareProtected(ScoView, "chercheEtud")
     def chercheEtud(self, expnom=None,
                     dest_url='ficheEtud',
@@ -1151,6 +1146,7 @@
         cnx = self.GetDBConnexion()
         #open('/tmp/t','w').write( str(etuds) )
         for etud in etuds:
+            scolars.format_etud_ident(etud)
             etudid = etud['etudid']
             adrs = scolars.adresse_list(cnx, {'etudid':etudid})
             if not adrs:
@@ -1162,18 +1158,7 @@
                 if len(adrs) > 1:
                     log('fillEtudsInfo: etudid=%s a %d adresses'%(etudid,len(adrs)))
             etud.update(adr)
-            etud['nom'] = format_nom(etud['nom'])
-            etud['prenom'] = format_nom(etud['prenom'])
-            etud['sexe'] = format_sexe(etud['sexe'])
-            etud['nomprenom'] = self.nomprenom(etud) # M. Pierre DUPONT
-            if etud['sexe'] == 'M.':
-                etud['ne'] = ''
-            else:
-                etud['ne'] = 'e'
-            if etud['email']:
-                etud['emaillink'] = '<a class="stdlink" href="mailto:%s">%s</a>'%(etud['email'],etud['email'])
-            else:
-                etud['emaillink'] = '<em>(pas d\'adresse e-mail)</em>'
+            
             # Semestres dans lesquel il est inscrit
             ins = self.Notes.do_formsemestre_inscription_list({'etudid':etudid})
             etud['ins'] = ins
@@ -1258,14 +1243,14 @@
             # etudiant non trouv\xE9: message d'erreur
             d = {
                 'etudid' : etudid,
-                'nom' : '?', 'prenom' : '?', 'sexe' : '?', 'email' : '?',
+                'nom' : '?', 'nom_usuel' : '', 'prenom' : '?', 'sexe' : '?', 'email' : '?',
                 'error' : 'code etudiant inconnu' }
             return sendResult(REQUEST, d, name='etudiant', format=format, force_outer_xml_tag=False)
         d = {}
         etud = etuds[0]
         self.fillEtudsInfo([etud])
         
-        for a in ('etudid', 'code_nip', 'code_ine', 'nom', 'prenom', 'sexe',
+        for a in ('etudid', 'code_nip', 'code_ine', 'nom', 'nom_usuel', 'prenom', 'sexe',
                   'nomprenom', 'email',
                   'domicile', 'codepostaldomicile', 'villedomicile', 'paysdomicile', 'telephone', 'telephonemobile', 'fax',
                   'bac', 'specialite', 'annee_bac',
@@ -1423,17 +1408,17 @@
     
     security.declareProtected(ScoEtudChangeAdr, 'formChangeCoordonnees')
     def formChangeCoordonnees(self,etudid,REQUEST):
-        "edit coordonnes etudiant"
+        "edit coordonnees etudiant"
         cnx = self.GetDBConnexion()        
-        etud = scolars.etudident_list(cnx, {'etudid':etudid})[0]
+        etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0]
         adrs = scolars.adresse_list(cnx, {'etudid':etudid})
         if adrs:
             adr = adrs[0]
         else:
             adr = {} # no data for this student
-        H = [ '<h2><font color="#FF0000">Changement des coordonn\xE9es de </font> %(prenom)s %(nom)s</h2><p>' % etud ]
+        H = [ '<h2><font color="#FF0000">Changement des coordonn\xE9es de </font> %(nomprenom)s</h2><p>' % etud ]
         header = self.sco_header(
-            REQUEST, page_title='Changement adresse de %(prenom)s %(nom)s' % etud )
+            REQUEST, page_title='Changement adresse de %(nomprenom)s' % etud )
         
         tf = TrivialFormulator(
             REQUEST.URL0, REQUEST.form, 
@@ -1469,7 +1454,7 @@
         if not self.Notes.can_change_groups(REQUEST, formsemestre_id):
             raise ScoValueError("Vous n'avez pas le droit d'effectuer cette op\xE9ration !")
         cnx = self.GetDBConnexion()
-        etud = scolars.etudident_list(cnx, {'etudid':etudid})[0]
+        etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0]
         sem = self.Notes.do_formsemestre_list({'formsemestre_id':formsemestre_id})[0]
         ins = self.Notes.do_formsemestre_inscription_list(
             { 'etudid'  : etudid, 'formsemestre_id' : formsemestre_id })[0]
@@ -1480,9 +1465,9 @@
             raise ScoValueError('Modification impossible: semestre verrouille')
         #
         etud['semtitre'] = sem['titremois']
-        H = [ '<h2><font color="#FF0000">Changement de groupe de</font> %(prenom)s %(nom)s (semestre %(semtitre)s)</h2><p>' % etud ]
+        H = [ '<h2><font color="#FF0000">Changement de groupe de</font> %(nomprenom)s (semestre %(semtitre)s)</h2><p>' % etud ]
         header = self.sco_header(
-            REQUEST, page_title='Changement de groupe de %(prenom)s %(nom)s'%etud)
+            REQUEST, page_title='Changement de groupe de %(nomprenom)s' % etud)
         # Liste des groupes existants
         raise NotImplementedError # XXX utiliser form_group_choice ou supprimer completement ?
         #
@@ -1700,7 +1685,7 @@
                         operation_method=''):
         "Formulaire d\xE9mission ou d\xE9faillance Etudiant"
         cnx = self.GetDBConnexion()    
-        etud = scolars.etudident_list(cnx, {'etudid':etudid})[0]
+        etud = self.getEtudInfo(etudid=etudid, filled=1, REQUEST=REQUEST)[0]
         sem = self.Notes.do_formsemestre_list({'formsemestre_id':formsemestre_id})[0]
         if sem['etat'] != '1':
             raise ScoValueError('Modification impossible: semestre verrouille')
@@ -1712,8 +1697,8 @@
         #
         header = self.sco_header(
             REQUEST,
-            page_title = '%(operation_name)s de  %(prenom)s %(nom)s (du semestre %(semtitre)s)'%etud)
-        H = [ '<h2><font color="#FF0000">%(operation_name)s de</font> %(prenom)s %(nom)s (semestre %(semtitre)s)</h2><p>' % etud ]
+            page_title = '%(operation_name)s de  %(nomprenom)s (du semestre %(semtitre)s)'%etud)
+        H = [ '<h2><font color="#FF0000">%(operation_name)s de</font> %(nomprenom)s (semestre %(semtitre)s)</h2><p>' % etud ]
         H.append("""<form action="%s" method="GET">
         <b>Date de la %s (J/M/AAAA): </b>
         """ % (operation_method, operation_name.lower()))
@@ -1949,6 +1934,7 @@
             ('adm_id', { 'input_type' : 'hidden' }),
 
             ('nom',       { 'size' : 25, 'title' : 'Nom', 'allow_null':False }),
+            ('nom_usuel', { 'size' : 25, 'title' : 'Nom usuel', 'allow_null':True }),
             ('prenom',    { 'size' : 25, 'title' : 'Pr\xE9nom', 'allow_null':CONFIG.ALLOW_NULL_PRENOM }),
             ('sexe',      { 'input_type' : 'menu', 'labels' : ['H','F'],
                             'allowed_values' : ['MR','MME'], 'title' : 'Genre' }),
@@ -2139,12 +2125,12 @@
         cnx = self.GetDBConnexion()
         H = [ self.Notes.html_sem_header(REQUEST, 'Etudiants du %s' % group['group_name'], sem),
               '<table class="sortable" id="listegroupe">',
-              '<tr><th>Nom</th><th>Pr\xE9nom</th><th>Mail</th><th>NIP (ScoDoc)</th><th>Apog\xE9e</th></tr>' ]
+              '<tr><th>Nom</th><th>Nom usuel</th><th>Pr\xE9nom</th><th>Mail</th><th>NIP (ScoDoc)</th><th>Apog\xE9e</th></tr>' ]
         nerrs = 0 # nombre d'anomalies d\xE9tect\xE9es
         nfix = 0 # nb codes changes
         nmailmissing = 0 # nb etuds sans mail
         for t in members:
-            nom, prenom, etudid, email, code_nip = t['nom'], t['prenom'], t['etudid'], t['email'], t['code_nip']
+            nom, nom_usuel, prenom, etudid, email, code_nip = t['nom'], t['nom_usuel'], t['prenom'], t['etudid'], t['email'], t['code_nip']
             infos = sco_portal_apogee.get_infos_apogee(self, nom, prenom)
             if not infos:
                 info_apogee = '<b>Pas d\'information</b> (<a href="etudident_edit_form?etudid=%s">Modifier identit\xE9</a>)' % etudid
@@ -2188,8 +2174,8 @@
                 else:
                     mailstat = 'inconnu'
                     nmailmissing += 1
-            H.append( '<tr><td><a href="ficheEtud?etudid=%s">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %
-                          (etudid, nom, prenom, mailstat, code_nip, info_apogee) )
+            H.append( '<tr><td><a href="ficheEtud?etudid=%s">%s</a></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %
+                          (etudid, nom, nom_usuel, prenom, mailstat, code_nip, info_apogee) )
         H.append('</table>')
         H.append('<ul>')
         if nfix:

Modified: branches/ScoDoc7/config/postupgrade-db.py
===================================================================
--- branches/ScoDoc7/config/postupgrade-db.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/config/postupgrade-db.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -368,6 +368,10 @@
     check_field(cnx, 'notes_evaluation', 'numero',
                 ['alter table notes_evaluation add column numero int DEFAULT 0',
                  ])
+    # add nom_usuel to identite
+    check_field(cnx, 'identite', 'nom_usuel',
+                ['alter table identite add column nom_usuel text DEFAULT NULL',
+                 ])
     # Add here actions to performs after upgrades:
     
     cnx.commit()

Modified: branches/ScoDoc7/html_sidebar.py
===================================================================
--- branches/ScoDoc7/html_sidebar.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/html_sidebar.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -89,7 +89,7 @@
         params.update(etud)
         # compte les absences du semestre en cours
         H.append("""<h2 id="insidebar-etud"><a href="%(ScoURL)s/ficheEtud?etudid=%(etudid)s" class="sidebar">
-    <font color="#FF0000">%(sexe)s %(nom)s</font></a>
+    <font color="#FF0000">%(sexe)s %(nom_disp)s</font></a>
     </h2>
     <b>Absences</b>""" % params)
         if etud['cursem']:

Modified: branches/ScoDoc7/misc/createtables.sql
===================================================================
--- branches/ScoDoc7/misc/createtables.sql	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/misc/createtables.sql	2013-10-06 15:18:20 UTC (rev 1273)
@@ -57,6 +57,7 @@
     photo_filename text,
     code_nip text UNIQUE, -- code NIP Apogee (may be null)
     code_ine text UNIQUE  -- code INE Apogee    
+    nom_usuel text -- optionnel (si present, affich\xE9 \xE0 la place du nom)
 )  WITH OIDS;
 
 CREATE TABLE adresse (

Modified: branches/ScoDoc7/misc/format_import_etudiants.txt
===================================================================
--- branches/ScoDoc7/misc/format_import_etudiants.txt	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/misc/format_import_etudiants.txt	2013-10-06 15:18:20 UTC (rev 1273)
@@ -6,8 +6,9 @@
 Code_INE;     text;     identite;   1; code INE
 #
 nom;          text;     identite;   0;  nom de l'etudiant
+nom_usuel; text;    identite;   1;  nom usuel (si different)
 prenom;       text;     identite;   0;  prenom de l'etudiant
-sexe (MR ou MLLE);         text;     identite;   0;  sexe ('MR' ou 'MLLE')
+sexe (H ou F);         text;     identite;   0;  sexe ('H' ou 'F')
 date_naissance;text;identite;   1;  date de naissance (jj/mm/aaaa)
 lieu_naissance;text;identite; 1; lieu de naissance
 nationalite;  text;     identite;   1;  nationalite

Modified: branches/ScoDoc7/notes_table.py
===================================================================
--- branches/ScoDoc7/notes_table.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/notes_table.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -121,9 +121,11 @@
             i = scolars.etudident_list( cnx, { 'etudid' : x['etudid'] } )[0]
             self.identdict[x['etudid']] = i
             self.inscrdict[x['etudid']] = x
-            x['nomp'] = i['nom'] + i['prenom'] # pour tri
+            x['nomp'] = (i['nom_usuel'] or i['nom']) + i['prenom'] # pour tri
+        
         # Tri les etudids par NOM
         self.inscrlist.sort( lambda x,y: cmp(x['nomp'],y['nomp']) )
+        
         # { etudid : rang dans l'ordre alphabetique }
         rangalpha = {}
         for i in range(len(self.inscrlist)):
@@ -271,17 +273,21 @@
         else:
             # Tri par ordre alphabetique de NOM
             return [ x['etudid'] for x in self.inscrlist ]
-
+    
     def get_sexnom(self,etudid):
-        return self.identdict[etudid]['sexe'] + ' ' + self.identdict[etudid]['nom'].upper()
+        "M. DUPONT"
+        etud =  self.identdict[etudid]
+        return etud['sexe'] + ' ' + (etud['nom_usuel'] or etud['nom']).upper()
+    
     def get_nom_short(self, etudid):
         "formatte nom d'un etud (pour table recap)"
-        return self.identdict[etudid]['nom'].upper() + ' ' + self.identdict[etudid]['prenom'][:2].capitalize() + '.'
-
+        etud =  self.identdict[etudid]
+        return (etud['nom_usuel'] or etud['nom']).upper() + ' ' + etud['prenom'][:2].capitalize() + '.'
+    
     def get_nom_long(self, etudid):
-        "formatte nom d'un etud"
+        "formatte nom d'un etud:  M. Pierre DUPONT"
         etud =  self.identdict[etudid]
-        return ' '.join([ scolars.format_sexe(etud['sexe']), scolars.format_prenom(etud['prenom']), scolars.format_nom(etud['nom'])])
+        return ' '.join([ scolars.format_sexe(etud['sexe']), scolars.format_prenom(etud['prenom']), scolars.format_nom(etud['nom_usuel'] or etud['nom'])])
 
     def get_displayed_etud_code(self, etudid):
         'code \xE0 afficher sur les listings "anonymes"'

Deleted: branches/ScoDoc7/sco_formsemestre_validation_old.py
===================================================================
--- branches/ScoDoc7/sco_formsemestre_validation_old.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_formsemestre_validation_old.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -1,762 +0,0 @@
-# -*- mode: python -*-
-# -*- coding: iso8859-15 -*-
-
-##############################################################################
-#
-# Gestion scolarite IUT
-#
-# Copyright (c) 2001 - 2006 Emmanuel Viennet.  All rights reserved.
-#
-# 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 at viennet.net
-#
-##############################################################################
-
-"""Semestres: formulaire valisation semestre et UE
-"""
-import urllib, time, datetime
-
-from notesdb import *
-from sco_utils import *
-from notes_log import log
-from notes_table import *
-
-"""
-Sx est valid\xE9 si moyenne g\xE9n\xE9 >=10 et UE>=8
-UE valid\xE9e si >=10 (n'a de sens que si le semestre n'est pas valid\xE9)
-
-         Si Sx est valid\xE9
-         |  alors si Sx-1 est valid\xE9
-         |        |  alors passage en Sx+1
-         |        |  sinon /* compensation  AUTOMATIQUE pour obtenir Sx-1 */
-         |        |        si (Sx + Sx+1) est valid\xE9
-         |        |        |  /* on met ensemble les notes des 2
-         |        |        |     semestres pour obtenir de nouvelles
-         |        |        |     moyennes et UEs > 8 */
-         |        |        |  alors passage en Sx+1 avec Sx et Sx-1
-         |        |        |  sinon refaire les UE manquantes de Sx-1
-         |        |        |        /* redoublement de Sx-1 */
-         |        |        finsi
-         |        finsi
-         |  sinon si Sx-1 est valid\xE9
-         |        |  alors passage en Sx+1
-         |        |  sinon refaire les UE non valid\xE9es
-         |        |        des semestres Sx et Sx+1 /* redoublement */
-         |        finsi
-         finsi
-
-
-Arret\xE9 du 13 ao\xFBt 2005
-
-* Art 19.
-
-Les unit\xE9s d'enseignement sont d\xE9finitivement acquises et
-capitalisables d\xE8s lors que l'\xE9tudiant y a obtenu la
-moyenne. L'acquisition de l'unit\xE9 d'enseignement emporte
-l'acquisition des cr\xE9dits europ\xE9ens correspondants.
-
-Toute unit\xE9 d'enseignement capitalis\xE9e est prise en compte dans le
-dispositif de compensation, au m\xEAme titre et dans les m\xEAmes conditions
-que les autres unit\xE9s d'enseignement.
-
-Dans le cas de redoublement d'un semestre, si un \xE9tudiant ayant acquis
-une unit\xE9 d'enseignement souhaite, notamment pour am\xE9liorer les
-conditions de r\xE9ussite de sa formation, suivre les enseignements de
-cette unit\xE9 d'enseignement et se repr\xE9senter au contr\xF4le des
-connaissances correspondant, la compensation prend en compte le
-r\xE9sultat le plus favorable pour l'\xE9tudiant.
-[XXX non impl\xE9ment\xE9: il faudrait chercher si plusieurs semestres "adjacents"
- et prendre en compte le "meilleur" r\xE9sultat:
- => calcul d'une moyenne g\xE9n\xE9rale avec de UE obtenues dans des
- ann\xE9es (ou semestres) diff\xE9rentes.
- ]
-
-* Art. 20.
-La validation d'un semestre est acquise de droit lorsque l'\xE9tudiant a
-obtenu \xE0 la fois :
-a) Une moyenne g\xE9n\xE9rale \xE9gale ou sup\xE9rieure \xE0 10 sur 20 et une moyenne
-\xE9gale ou sup\xE9rieure \xE0 8 sur 20 dans chacune des UE;
-
-b) La validation des semestres pr\xE9c\xE9dents, lorsqu'ils existent.
-
-Lorsque les conditions pos\xE9es ci-dessus ne sont pas remplies, la
-validation est assur\xE9e, sauf opposition de l'\xE9tudiant, par une
-compensation organis\xE9e entre deux semestres cons\xE9cutifs sur la base
-d'une moyenne g\xE9n\xE9rale \xE9gale ou sup\xE9rieure \xE0 10 sur 20 et d'une
-moyenne \xE9gale ou sup\xE9rieure \xE0 8 sur 20 dans chacune des UE
-constitutives de ces semestres. Le semestre servant \xE0 compenser ne
-peut \xEAtre utilis\xE9 qu'une fois au cours du cursus.
-
-En outre, le directeur de l'IUT peut prononcer la validation d'un
-semestre sur proposition du jury.  La validation de tout semestre
-donne lieu \xE0 l'obtention de l'ensemble des UE qui le composent et des
-cr\xE9dits europ\xE9ens correspondants. [=> si un semestre est valid\xE9, les UE le sont]
-
-
-* Art. 21.
-La poursuite d'\xE9tudes dans un nouveau semestre est de droit pour tout
-\xE9tudiant \xE0 qui ne manque au maximum que la validation d'un seul
-semestre de son cursus.
-[ XXX a prendre en compte dans le formulaire de passage ]
-
-* Art. 22.
-Le redoublement est de droit dans les cas o\xF9 :
-
- - l'\xE9tudiant a obtenu la moyenne g\xE9n\xE9rale et lorsque celle-ci ne
- suffit pas pour remplir la condition pos\xE9e au a de l'article 20
- ci-dessus ; [ donc au moins une UE < 8 ]
-
- - l'\xE9tudiant a rempli la condition pos\xE9e au a de l'article 20
- ci-dessus dans un des deux semestres utilis\xE9s dans le processus de
- compensation. [ un semestre acquis normalement ]
-
-En outre, l'\xE9tudiant peut \xEAtre autoris\xE9 \xE0 redoubler par d\xE9cision du
-directeur de l'IUT, sur proposition du jury de passage ou du jury de
-d\xE9livrance pour l'obtention du dipl\xF4me universitaire de technologie.
-
-
-Durant la totalit\xE9 du cursus conduisant au dipl\xF4me universitaire de
-technologie, l'\xE9tudiant ne peut \xEAtre autoris\xE9 \xE0 redoubler plus de deux
-semestres. En cas de force majeure d\xFBment justifi\xE9e et appr\xE9ci\xE9e par
-le directeur de l'IUT, un redoublement suppl\xE9mentaire peut \xEAtre
-autoris\xE9.
-
-
-* Art. 24. ~ Le DUT (...) est d\xE9livr\xE9 par le pr\xE9sident de l'universit\xE9
-(...)
-La d\xE9livrance du dipl\xF4me universitaire de technologie donne lieu \xE0 l'obtention
-de l'ensemble des unit\xE9s d~enseignement qui le composent et les cr\xE9dits
-correspondants.
-
-
-* Art. 25.
-
-Les unit\xE9s d'enseignement dans lesquelles la moyenne de 10 a \xE9t\xE9
-obtenue sont capitalisables en vue de la reprise d'\xE9tudes en
-formation continue.
-
-Les \xE9tudiants qui sortent de l'IUT sans avoir obtenu le dipl\xF4me
-universitaire de technologie re\xE7oivent une attestation d'\xE9tudes
-comportant la liste des unit\xE9s d'enseignement capitalisables qu'ils
-ont acquises, ainsi que les cr\xE9dits europ\xE9ens correspondants, d\xE9livr\xE9e
-par le directeur de l'IUT.
-
-
------- ADIUT:
-Un accord sur certains termes semble acquis et afin de respecter
-la terminologie Apog\xE9e, nous utiliserons, lorsque le semestre est
-valid\xE9, les 3 possibilit\xE9s suivantes :
- - Admis (validation de droit)
- - Admis par compensation (validation par compensation entre semestres)
- - Admis jury (validation par le jury)
-
-Lorsque le semestre n'est pas valid\xE9, l'\xE9tudiant est en attente d'une
-d\xE9cision qui sera prise au semestre suivant.
-
-
-Codes \xE0 utiliser:
-- Jury S1:
- . ADM  (auto) moyenne gen., barres UE, assiduit\xE9: sem. valid\xE9
- . ATT  (auto) pas moy., barres UE ok, assiduit\xE9: d\xE9cision en attente de S2
- . ATB  (auto) pb barre UE (qq soit moy gen), assiduit\xE9: d\xE9cision en attente de S2
- . ATJ  (manu) pb assiduit\xE9. d\xE9cision en attente de S2
- . ATJ* (manu) manque note. attente jury "d\xE9cal\xE9"
-
-- Codes \xE9tat UEs: (toujours calcul\xE9 automatiquement)
- . ADM  moy UE >= 10 (qq soit resultat semestre)
- . CMP  moy UE < 10 et semestre valid\xE9 (ADC, ADJ, ADM)
- . AJ   moy UE < 10 et semestre non valid\xE9 (ATT, ATB, ATJ, AJ, NAR)
-
-
-- Codes \xE9tat semestres:
- . AJ   redoublement
- . NAR  r\xE9orientation (non admis \xE0 redoubler)
- . ADC  admis par compensation (eg moy(S1, S2) > 10)
- . ADJ  admis par Jury
-
-"""
-
-def formsemestre_validation_form(
-    self, # ZNotes instance
-    formsemestre_id, etudid=None,
-    REQUEST=None):
-    """formulaire valisation semestre et UE
-    Si etudid, traite un seul \xE9tudiant !
-    """
-
-    cnx = self.GetDBConnexion()
-    sem = self.do_formsemestre_list(args={ 'formsemestre_id' : formsemestre_id } )[0]
-    if sem['etat'] != '1':
-        header = self.sco_header(REQUEST,
-                                 page_title="Semestre verrouill\xE9")
-        footer = self.sco_footer(self, REQUEST)
-        return header + '<p>Semestre verrouill\xE9</p>' + footer
-
-    nt = self._getNotesCache().get_NotesTable(self, formsemestre_id)
-    ues = nt.get_ues()
-    T = nt.get_table_moyennes_triees()
-    # Traite toute la promo ou un seul \xE9tudiant ?
-    if REQUEST.form.has_key('etudid'):
-        etudid = REQUEST.form['etudid']
-    if etudid:
-        # restreint T a cet \xE9tudiant (validation individuelle)
-        for t in T:
-            if t[-1] == etudid:
-                ok = 1
-                break
-        if not ok:
-            raise ScoValueError('etudid invalide (%s)' % etudid)
-        T = [t]
-        valid_individuelle = True            
-        # XXX OK, la suite est inachevee (modif titre, prise en compte de l'etat
-        # XXX courant de l'etudiant (sem et ue d\xE9j\xE0 valid\xE9es) pour initialiser le form.            
-    else:
-        valid_individuelle = False
-
-    # ------- Validations au dessus des barres
-    # sem_must_valid = { etudid : True|False }
-    # ue_must_valid = { ue_id : { etudid : True|False } }
-    sem_must_valid, ue_must_valid = _compute_barres( ues, nt, T )
-
-    # ------- Traitement des donn\xE9es formulaire
-    date_jury = REQUEST.form.get('date_jury', time.strftime('%d/%m/%Y') )
-    msg = ''
-    # --------------   Soumission du formulaire
-    if REQUEST.form.get('tf-submitted',False):
-        # recupere infos du form
-        semvalid, semcomp, form_sem_decision, uevalid, uevalid_byetud = \
-                  _get_validation_form_state( REQUEST, ues, T, sem_must_valid, ue_must_valid )
-        # verification coh\xE9rence d\xE9cision semestre/UE
-        inconsistent_etuds = []
-        for etudid in semvalid.keys():
-            if semvalid[etudid]:
-                # le semestre valide, donc les UE doivent valider
-                for ue_id in uevalid.keys():
-                    if not uevalid[ue_id][etudid]:
-                        inconsistent_etuds.append(nt.get_nom_short(etudid))            
-        #
-        nbvalid = len([ x for x in semvalid.values() if x ])            
-        msg = """<ul class="tf-msg">"""
-        if inconsistent_etuds:
-            msg += '<li class="tf-msg">D\xE9cisions incoh\xE9rentes pour les \xE9tudiants suivants ! (si le semestre est valid\xE9, toutes les UE doivent l\'\xEAtre)<ul><li>' + '</li><li>'.join( inconsistent_etuds ) + '</li></ul></li>'
-        msg += '<li class="tf-msg">%d \xE9tudiants valident le semestre</li>' % nbvalid
-        date_ok = False
-        try:
-            junk = DateDMYtoISO(date_jury)
-            if junk:
-                date_ok = True
-        except:
-            pass
-        if not date_ok:
-            msg += '<li class="tf-msg">la date est incorrecte !</li>'
-        if len(inconsistent_etuds)==0 and date_ok:
-            msg += '<input type="submit" name="go" value="OK, valider ces d\xE9cisions"/>'
-        msg += '</ul>'
-    else:
-        # premier passage: initialise le form avec decisions ant\xE9rieures s'il y en a
-        semvalid = {} # etudid: 0 ou 1
-        semcomp = {} # etudid : id semestre utilise pour compenser, or None
-        form_sem_decision = {} # etudid : 'O', 'N' ou formsemestre_id util. pour compenser
-        uevalid = {} # ue_id : { etudid : True|False }
-        uevalid_byetud = {} # etudid : [ ue_id, ... ]
-        for ue in ues:
-            uevalid[ue['ue_id']] = {}
-        #
-        for t in T:
-            etudid = t[-1]
-            sem_d, ue_ids, semcomp[etudid] = self._formsemestre_get_decision(
-                cnx, etudid, formsemestre_id )
-            if sem_d == 2: # semestre valid\xE9
-                semvalid[etudid] = 1
-                if not semcomp[etudid]:
-                    form_sem_decision[etudid] = 'O'
-                else:
-                    form_sem_decision[etudid] = semcomp[etudid]
-                for ue_id in uevalid.keys(): # valide tt les UE
-                    uevalid[ue_id][etudid] = 1
-            else:
-                semvalid[etudid] = 0
-                form_sem_decision[etudid] = 'N'
-                for ue_id in ue_ids:
-                    if uevalid.has_key(ue_id):
-                        # test car la formation peut avoir ete modifie
-                        # apres saisie des decisions !
-                        uevalid[ue_id][etudid] = 1
-        #open('/tmp/toto','a').write('\n'+str(uevalid)+'\n')
-    #
-    if REQUEST.form.get('go',False) and len(inconsistent_etuds)==0:
-        # OK, validation
-        return _do_formsemestre_validation( self,
-            formsemestre_id, semvalid, semcomp,
-            uevalid_byetud, date_jury,
-            REQUEST=REQUEST)
-
-    # --- HTML head
-    footer = self.sco_footer(self, REQUEST)
-    if valid_individuelle:
-        nomprenom = self.nomprenom(nt.identdict[etudid])
-        header = self.sco_header(REQUEST,
-                                 page_title='Validation du semestre %s pour %s'
-                                 % (sem['titreannee'],nomprenom))
-        H = [ """<h2>Validation (Jury) du semestre %s pour %s</h2>
-        <p>Utiliser ce formulaire apr\xE8s la <b>d\xE9cision d\xE9finitive du jury</b>.</p>
-        <p>Attention: les d\xE9cisions prises ici remplacent et annulent les pr\xE9c\xE9dentes s'il y en avait !</p>
-        """ % (sem['titreannee'],nomprenom)]
-    else:
-        header = self.sco_header(REQUEST,
-                                 page_title="Validation du semestre "+sem['titreannee'])
-        H = [ """<h2>Validation (Jury) du semestre %s</h2>
-
-
-        <h3>Attention: gestion des compensation inter-semestre en cours de d\xE9veloppement</h3>
-        <p style="color:red"><em>Il est pr\xE9f\xE9rable d'attendre quelques jours avant
-        de valider ce formulaire</em></p>
-
-
-        <p>Utiliser ce formulaire apr\xE8s la d\xE9cision d\xE9finitive du jury.</p>
-        <p>Les \xE9tudiants au dessus des "barres" vont valider automatiquement le semestre ou certaines UE.
-        </p><p>Pour valider des \xE9tudiants sous les barres, cocher les cases correspondantes.</p>
-        <p>Un semestre peut \xEAtre valid\xE9 automatiquement, ou sur d\xE9cision du jury (choisir "Admis"), ou, si le parcours le permet, par compensation avec l'un des semestre propos\xE9 dans le menu (<b>vous devez v\xE9rifier les notes</b>, car le calcul des moyennes n'est pas pris en compte: on propose ici tous les semestres possibles)</p>
-        <p>Attention: les d\xE9cisions prises ici remplacent et annulent les pr\xE9c\xE9dentes s'il y en avait !</p>
-        <p>Attention: le formulaire va affecter TOUS LES ETUDIANTS !</p>
-        """ % sem['titreannee'] ]
-
-    H.append( """
-    <form class="formvalidsemestre" method="POST">
-    <input type="hidden" name="tf-submitted" value="1"/>
-    <input type="hidden" name="formsemestre_id" value="%s"/>
-    %s
-    <p>Date du jury (j/m/a): <input type="text" name="date_jury" size="12" value="%s" /></p>
-    <table class="notes_recapcomplet">
-    <tr class="recap_row_tit"><td class="recap_tit">Rg</td><td class="recap_tit">Nom</td><td class="fvs_tit">Moy</td>
-    <td class="fvs_tit_chk">Semestre</td>
-    """ % (formsemestre_id,msg,date_jury) )        
-
-    for ue in ues:
-        if ue['type'] != UE_SPORT:
-            H.append('<td class="fvs_tit">%s</td><td class="fvs_tit_chk">valid\xE9e</td>' % ue['acronyme'])
-    H.append('</tr>')
-    # --- Generate form
-    ir = 0
-    for t in T:
-        etudid = t[-1]
-        if ir % 2 == 0:
-            cls = 'recap_row_even'
-        else:
-            cls = 'recap_row_odd'
-        ir += 1
-        if sem_must_valid[etudid]:
-            moycls = 'fvs_val'
-        else:
-            moycls = 'fvs_val_inf'
-        if semvalid.has_key(etudid): # dans le formulaire
-            if semvalid[etudid]:
-                sem_checked, sem_unchecked = "checked", ""
-                sem_is_valid = True
-            else:
-                sem_checked, sem_unchecked = "", "checked"
-                sem_is_valid = False
-        elif sem_must_valid[etudid]:
-            sem_checked, sem_unchecked = "checked", ""
-            sem_is_valid = True
-        else:
-            sem_checked, sem_unchecked = "", "checked"
-            sem_is_valid = False
-
-        H.append('<tr class="%s"><td>%s</td><td><a href="ficheEtud?etudid=%s">%s</a></td><td class="%s">%s</td>'
-                 % (cls, nt.get_etud_rang(etudid),
-                    etudid, nt.get_nom_short(etudid),
-                    moycls, fmt_note(t[0]) ))
-        # ne propose que si sous la barre
-        if sem_must_valid[etudid]:
-            H.append('<td class="fvs_chk">valid\xE9</td>')
-        elif nt.get_etud_etat(etudid) == 'D':
-            H.append('<td class="fvs_chk">d\xE9mission</td>')
-        else:
-            # semestres pour compensation
-            sems_pour_comp = _lists_semestre_utilisables(self, formsemestre_id, etudid)
-            H.append('<td><select name="sem_decision_%s" style="width: 250px">'% etudid)
-            decision = form_sem_decision.get(etudid,None)
-            if decision == 'N':
-                selected = 'selected'
-            else:
-                selected = ''
-            H.append('<option value="N" %s>Non</option>' % selected)
-            if decision == 'O':
-                selected = 'selected'
-            else:
-                selected = ''
-            H.append('<option value="O" %s>Admis</option>' % selected)
-
-            for sem in sems_pour_comp:
-                if sem['formsemestre_id'] == decision:
-                    selected = 'selected'
-                else:
-                    selected = ''
-                H.append("""<option value="%s" %s>Compens\xE9 avec %s (%s - %s) [moy=%s, moy comp.=%s]</option>"""
-                         % (sem['formsemestre_id'], selected,
-                            sem['titre_num'], sem['date_debut'], sem['date_fin'],
-                            fmt_note(sem['moy_gen']), fmt_note(sem['moy_comp']) ))
-            H.append("""</select>""")
-            # check: s'il y a eu compensation, doit etre dans la liste des possibles
-            # sinon, bug ou modification des notes ou de l'archi de formation
-            if decision and decision != 'O' and decision != 'N' \
-                   and decision not in [ x['formsemestre_id']
-                                         for x in sems_pour_comp ]:
-                H.append('(compensation impossible d\xE9j\xE0 saisie !)')
-            H.append("""</td>""")
-
-        # UEs
-        iue = 0
-        for ue in ues:
-            iue += 1
-            if ue['type'] == UE_SPORT:
-                continue
-            if uevalid[ue['ue_id']].has_key(etudid): # dans le formulaire
-                if uevalid[ue['ue_id']][etudid]:
-                    sem_checked, sem_unchecked = "checked", ""
-                else:
-                    sem_checked, sem_unchecked = "", "checked"
-            elif ue_must_valid[ue['ue_id']][etudid]:
-                sem_checked, sem_unchecked = "checked", ""
-            else:
-                sem_checked, sem_unchecked = "", "checked"
-            # ne propose les UE que si le semestre est sous la barre
-            if ue_must_valid[ue['ue_id']][etudid]:
-                H.append('<td class="fvs_val">%s</td><td class="fvs_chk">valid.</td>'
-                         % (t[iue],))
-            elif nt.get_etud_etat(etudid) == 'D':
-                H.append('<td class="fvs_val"></td><td class="fvs_chk"></td>')
-            else:
-                H.append("""<td class="fvs_val_inf">%s</td><td class="fvs_chk">
-                <input type="radio" name="ue_%s_%s" value="1" class="radio_green" %s/>O 
-                <input type="radio" name="ue_%s_%s" value="0" class="radio_red" %s/>N
-                </td>            
-                """ % (t[iue],ue['ue_id'],etudid,sem_checked,
-                       ue['ue_id'],etudid,sem_unchecked))
-        #
-        H.append('</tr>')
-    # ligne titres en bas
-    H.append('<tr class="recap_row_tit"><td></td><td></td><td class="fvs_tit">Moy</td><td class="fvs_tit_chk">Semestre</td>')
-    for ue in ues:
-        if ue['type'] != UE_SPORT:
-            H.append('<td class="fvs_tit">%s</td><td class="fvs_tit_chk"></td>' % ue['acronyme'])
-    H.append('</tr></table>')
-    if valid_individuelle:
-        H.append('<input type="hidden" name="etudid" value="%s" />' % etudid)
-
-    #
-    H.append("""<input type="submit" name="submit" value="V\xE9rifier" /></form>
-
-    <p style="color:red"><em>En d\xE9veloppement: il est pr\xE9f\xE9rable d'attendre quelques jours avant
-        de valider ce formulaire</em></p>
-
-    """)
-    return header + '\n'.join(H) + footer
-
-
-#
-def _lists_semestre_utilisables(self, formsemestre_id, etudid):
-    """Liste des semestres utilisables pour compenser une decision
-    sur formsemestre_id.
-    On prend tous les semestres de la m\xEAme formation 
-    dans lesquels etudid a \xE9t\xE9 inscrit
-    et qui sont "adjacents" (semestre precedent ou suivant)
-    et tels que moyenne des moyennes generales > 10 (=NOTES_BARRE_GEN)
-    et toutes les UE > 8 (=NOTES_BARRE_UE)
-    Ajoute les champs moy_gen et moy_comp (moyenne des 2 semestres)
-    a chaque semestre selectionne
-    """
-    cursem = self.do_formsemestre_list(
-        args={ 'formsemestre_id' : formsemestre_id })[0]
-    if cursem['gestion_compensation'] != '1':
-        return [] # pas de compensation possible
-    cur_formation_id = cursem['formation_id']
-    insems = self.do_formsemestre_inscription_list( args={ 'etudid' : etudid } )
-    # Cherche les semestres avec lesquels on pourrait compenser
-    nt = self._getNotesCache().get_NotesTable(self, formsemestre_id) 
-    moy_gen = nt.moy_gen[etudid]
-    if type(moy_gen) != type(1.0):
-        return [] # pas de moyenne calculee, on ne peut pas compenser
-    # Pour pr\xE9tendre \xE0 compenser, il faut que toutes les UE soient > 8           
-    if not nt.etud_has_all_ue_over_threshold(etudid):
-        return [] # aucune compensation possible car une UE sous la barre
-    #
-    cnx = self.GetDBConnexion()
-    sems = []
-    for ins in insems:
-        sem = self.do_formsemestre_list(
-            args={ 'formsemestre_id' : ins['formsemestre_id'] })[0]
-        # semestres "adjacents" ?
-        adjacent = False
-        if sem['semestre_id'] != None and cursem['semestre_id'] != None:
-            d = sem['semestre_id'] - cursem['semestre_id']
-            if abs(d) == 1:
-                adjacent = True
-        
-        if sem['formsemestre_id'] != formsemestre_id \
-               and sem['formation_id'] == cur_formation_id \
-               and adjacent:
-            # semestre adjacents de la meme formation
-            # a-t-il ete valid\xE9 ?
-            sem_d, ue_ids, comp_semid = self._formsemestre_get_decision(
-                cnx, etudid, sem['formsemestre_id'] )
-            nto = self._getNotesCache().get_NotesTable(self, sem['formsemestre_id'])
-            # Sem valide avec toutes ses UE > 8 ?
-            if sem_d == 2 and nto.etud_has_all_ue_over_threshold(etudid):
-                # A-t-il deja \xE9t\xE9 utilis\xE9 ?
-                events = scolars.scolar_events_list(
-                    cnx, args={'etudid':etudid,
-                               'formsemestre_id': sem['formsemestre_id'],
-                               'event_type' : 'UTIL_COMPENSATION' })                
-                if not events or events[0]['comp_formsemestre_id'] == formsemestre_id:
-                    # Pas deja utilis\xE9 (ou utilis\xE9 pour ce semestre)
-                    # Calcule moyenne des moyennes g\xE9n\xE9rales
-                    other_moy = nto.moy_gen[etudid]
-                    if type(moy_gen) == type(1.0):
-                        moy_comp = (moy_gen+other_moy) / 2
-                        log('moy_comp=%s' % moy_comp)
-                        if moy_comp >= NOTES_BARRE_GEN:
-                            sem['moy_gen'] = other_moy
-                            sem['moy_comp'] = moy_comp
-                            sems.append(sem)
-    return sems
-
-
-#
-def _compute_barres( ues, nt, T ):
-    # D\xE9termine pour chaque etudiant s'il DOIT valider le semestre
-    # car > barre et la liste des UE qu'il DOIT valider car > barre,
-    # Elimine les demissionnaires
-    
-    sem_must_valid = {} # etudid : True|False
-    ue_must_valid = {}  # ue_id : { etudid : True|False }
-    for ue in ues:
-        ue_must_valid[ue['ue_id']] = {}
-    for t in T:
-        etudid = t[-1]            
-        # premiere passe sur les UE pour verif barres (sauf sport):
-        barres_ue_ok = True
-        iue = 0
-        for ue in ues:
-            iue += 1
-            if ue['type'] == UE_SPORT:
-                continue # pas de barre sur notes de sport
-            try:
-                if (float(t[iue]) < NOTES_BARRE_UE):
-                    barres_ue_ok = False
-            except:
-                barres_ue_ok = False # une UE sans note
-        # valide semestre
-        try:
-            if (float(t[0]) >= NOTES_BARRE_GEN) and barres_ue_ok:
-                sem_must_valid[etudid] = True
-            else:
-                sem_must_valid[etudid] = False
-        except:
-            sem_must_valid[etudid] = False # manque notes
-        # valide les UE
-        iue = 0
-        for ue in ues:
-            iue += 1
-            if ue['type'] == UE_SPORT:
-                ue_must_valid[ue['ue_id']][etudid] = False
-                continue
-            try:
-                if (float(t[iue]) < NOTES_BARRE_VALID_UE) and not sem_must_valid[etudid]:
-                    ue_must_valid[ue['ue_id']][etudid] = False
-                else:
-                    ue_must_valid[ue['ue_id']][etudid] = True                    
-            except:
-                ue_must_valid[ue['ue_id']][etudid] = False
-        # et s'il est d\xE9missionnaire, il n'a rien
-        if nt.get_etud_etat(etudid) == 'D':
-            sem_must_valid[etudid] = False
-            for ue in ues:
-                ue_must_valid[ue['ue_id']][etudid] = False
-    
-    return sem_must_valid, ue_must_valid
-
-#
-def _get_validation_form_state( REQUEST, ues, T, sem_must_valid, ue_must_valid ):
-    # recupere les etudiants du form
-    semvalid = {} # etudid: 0 ou 1
-    semcomp = {} # etudid : id semestre utilise pour compenser, or None
-    form_sem_decision = {} # etudid : 'O', 'N' ou formsemestre_id util. pour compenser
-    uevalid = {} # ue_id : { etudid : True|False }
-    uevalid_byetud = {} # etudid : [ ue_id, ... ]
-    for ue in ues:
-        uevalid[ue['ue_id']] = {}
-    #
-    for etudid in [t[-1] for t in T]:
-        semcomp[etudid] = None
-        if sem_must_valid[etudid]:
-            semvalid[etudid] = 1 # happily ignore form
-            form_sem_decision[etudid] = None
-        else:
-            v = REQUEST.form.get('sem_decision_%s'%etudid,None)
-            form_sem_decision[etudid] = v
-            if v != None and v != 'N':
-                semvalid[etudid] = 1
-                if v != 'O':
-                    semcomp[etudid] = v # semestre utilise pour compenser
-            else:
-                semvalid[etudid] = 0
-        # recupere chaque UE valid\xE9e
-        for ue in ues:
-            if ue['type'] == UE_SPORT:
-                uevalid[ue['ue_id']][etudid] = 1
-                continue
-            if ue_must_valid[ue['ue_id']][etudid]:
-                uevalid[ue['ue_id']][etudid] = 1 # happily ignore form
-            else:
-                v = REQUEST.form.get('ue_%s_%s'%(ue['ue_id'],etudid),None)
-                if v != None:
-                    uevalid[ue['ue_id']][etudid] = int(v)
-                else:
-                    uevalid[ue['ue_id']][etudid] = 0
-    # reconstruit la liste des ue valides pour chaque etud
-    uevalid_byetud = {}
-    for ue_id in uevalid.keys():
-        for etudid in uevalid[ue_id].keys():
-            if not uevalid_byetud.has_key(etudid):
-                uevalid_byetud[etudid] = []
-            if uevalid[ue_id][etudid]:
-                uevalid_byetud[etudid].append(ue_id)                    
-    #
-    return semvalid, semcomp, form_sem_decision, uevalid, uevalid_byetud
-
-
-#
-def _do_formsemestre_validation(
-    self, # ZNotes instance
-    formsemestre_id,
-    semvalid,
-    semcomp, # etudid : semestre utilise pour compenser
-    uevalid_byetud,
-    date_jury=None, REQUEST=None):
-    # effectue la validation des semestres et UE indiqu\xE9s
-    cnx = self.GetDBConnexion()
-    # semestres
-    for etudid in semvalid.keys():
-        scolars.scolar_validate_sem(
-            cnx, etudid,
-            formsemestre_id, valid=semvalid[etudid],
-            formsemestre_used_to_compensate=semcomp.get( etudid, None ),
-            event_date=date_jury,
-            REQUEST=REQUEST)
-    # UE
-    for etudid in uevalid_byetud.keys():
-        scolars.scolar_validate_ues(self, cnx, etudid, formsemestre_id, 
-                                    uevalid_byetud[etudid], event_date=date_jury,
-                                    suppress_previously_validated=True,
-                                    REQUEST=REQUEST)
-    # Inval cache bulletins
-    self._inval_cache(formsemestre_id=formsemestre_id) #> modif decision jury XXX bug: si valide et capitalise UE, peut affecter d'autres semestres (utilisant cette UE) XXX a revoir
-    #
-    return REQUEST.RESPONSE.redirect(
-        'formsemestre_validation_list?formsemestre_id=%s'%formsemestre_id)
-
-# ---
-def get_etud_moys_semestre(self, etudid, formsemestre_id):
-    """Notes moyennes du semestre:
-    { 'moy' : moyenne generale
-      'moy_ues' : { ue_id : (moy, coef) }
-      'comment' : explication si des UEs faites a des formsemestre differents
-                  (capitalisation ou calcul de la meilleure UE si rep\xE9t\xE9e)
-      }
-
-    Cherche dans le semestre, et tous les semestres identiques
-    (m\xEAme semestre_id dans la formation) pour g\xE9rer capitalisations et redoublements.
-    """
-    
-    nt = self._getNotesCache().get_NotesTable(self, formsemestre_id)
-    cursem = self.do_formsemestre_list({'formsemestre_id':formsemestre_id})[0]
-    # cherche les semestres identiques o\xF9 l'etudiant est inscrit
-    ins = self.do_formsemestre_inscription_list({'etudid':etudid})
-    sems = [] # semestres "identiques"
-    for i in ins:
-        sem = self.do_formsemestre_list({'formsemestre_id':i['formsemestre_id']})[0]
-        if sem['formation_id'] == cursem['formation_id'] \
-           and sem['semestre_id'] == cursem['semestre_id']:
-            sems.append(sem)
-    # cherche la liste des UEs
-    # (au cas improbable ou l'on aurait pas les m\xEAmes UE dans les differents
-    #  semestres identiques, merge les listes d'ue...)
-    # Dans chaque UE, place sa moyenne (moycoef) et son semestre (sem).
-    ues = nt.get_ues(etudid=etudid)
-    uedict = {}
-    for ue in ues:
-        ue['moycoef'] = nt.get_etud_moycoef_ue(etudid, ue['ue_id'])
-        ue['sem'] = cursem
-        uedict[ue['ue_id']] = ue
-
-    comment = [] # explications
-    for sem in sems:
-        nt1 = self._getNotesCache().get_NotesTable(self, sem['formsemestre_id'])
-        ues1 = nt1.get_ues(etudid=etudid)
-        for ue in ues1:
-            ue['moycoef'] = nt1.get_etud_moycoef_ue(etudid, ue['ue_id'])
-            ue['sem'] = sem
-            if not uedict.has_key(ue['ue_id']):
-                uedict[ue['ue_id']] = ue
-            else:
-                # remplace UE existante 
-                # Si UE "acquise" (au sens de l'article 19, donc moy > 10)
-                # et (pas cette UE dans sem. courant OU note meilleure)
-                moy, coef = ue['moycoef']
-                if coef > 0 and moy > NOTES_BARRE_VALID_UE:
-                    cur_ue = uedict[ue['ue_id']]
-                    cur_moy, cur_coef = cur_ue['moycoef']
-                    if (cur_coef == 0) or moy > cur_moy:
-                        uedict[ue['ue_id']] = ue
-                        comment.append('UE %s reprise de %s' %
-                                       ue['acronyme'], sem['date_debut'] )
-    # Recalcul moyenne g\xE9n\xE9rale avec UE reprises
-    summoy = 0.
-    sumcoef = 0.
-    for ue in uedict.values():
-        moy, coef = ue['moycoef']
-        if coef > 0:
-            summoy += moy
-            sumcoef += coef
-    if sumcoef > 0:
-        moy = summoy / sumcoef
-    else:
-        moy = 'NA'        
-    #
-    moy_ues = {}
-    for ue in uedict.values():
-        moy_ues[ue['ue_id']] = ue['moycoef']
-    return { 'moy' : moy,
-             'moy_ues' : moy_ues,
-             'comment' : comment }
-
-def get_etud_situation_semestre(self, etudid, formsemestre_id):
-    """Toutes les infos decrivant la situation de l'etudiant
-    au moment du jury.
-    """
-    nt = self._getNotesCache().get_NotesTable(self, formsemestre_id)
-    moys = get_etud_moys_semestre(self, etudid, formsemestre_id)
-
-    # XXX pour savoir si on peut valider: il faut regarder le sem. precedent (article 20)
-
-    info = { 'nom_abbrv' : nt.get_nom_short(etudid),
-             # rang semestre courant sans tenir compte des capitalisations d'UE
-             'rang' : nt.get_etud_rang(etudid),
-             'moys' : moys
-             }
-    

Modified: branches/ScoDoc7/sco_groups.py
===================================================================
--- branches/ScoDoc7/sco_groups.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_groups.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -155,10 +155,15 @@
     
     r = SimpleDictFetch(context, req, { 'group_id' : group_id, 'etat' : etat} )
 
-    r.sort(key=operator.itemgetter('nom'))
+    for etud in r:
+        scolars.format_etud_ident(etud)
+    
+    r.sort(key=operator.itemgetter('nom_disp')) # tri selon nom_usuel ou nom
+        
     if CONFIG.ALLOW_NULL_PRENOM:
         for x in r:
             x['prenom'] = x['prenom'] or ''
+    
     return r
 
 def get_group_infos(context, group_id, etat=None): # was _getlisteetud

Modified: branches/ScoDoc7/sco_inscr_passage.py
===================================================================
--- branches/ScoDoc7/sco_inscr_passage.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_inscr_passage.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -38,6 +38,7 @@
 from sets import Set
 from gen_tables import GenTable
 import sco_groups
+import scolars
 
 def list_authorized_etuds_by_sem(context, sem, delai=274):
     """Liste des etudiants autoris\xE9s \xE0 s'inscrire dans sem.
@@ -245,18 +246,18 @@
             if a_inscrire:
                 H.append('<h3>Etudiants \xE0 inscrire</h3><ol>')
                 for etud in set_to_sorted_etud_list(a_inscrire):
-                    H.append('<li>%s</li>' % context.nomprenom(etud))
+                    H.append('<li>%(nomprenom)s</li>' % etud)
                 H.append('</ol>')
             a_inscrire_en_double = inscrits_ailleurs.intersection(a_inscrire)
             if a_inscrire_en_double:
                 H.append('<h3>dont \xE9tudiants d\xE9j\xE0 inscrits:</h3><ul>')
                 for etud in set_to_sorted_etud_list(a_inscrire_en_double):
-                    H.append('<li class="inscrailleurs">%s</li>' % context.nomprenom(etud))
+                    H.append('<li class="inscrailleurs">%(nomprenom)s</li>' % etud)
                 H.append('</ul>')
             if a_desinscrire:
                 H.append('<h3>Etudiants \xE0 d\xE9sinscrire</h3><ol>')
                 for etudid in a_desinscrire:
-                    H.append('<li class="desinscription">%s</li>' % context.nomprenom(inscrits[etudid]))
+                    H.append('<li class="desinscription">%(nomprenom)s</li>' % inscrits[etudid])
                 H.append('</ol>')
             if not a_inscrire and not a_desinscrire:
                 H.append("""<h3>Il n'y a rien \xE0 modifier !</h3>""")
@@ -412,11 +413,13 @@
                         c = ' inscrailleurs'
                     else:
                         c = ''
+                scolars.format_etud_ident(etud)
                 if etud['etudid']:
                     elink = """<a class="discretelink %s" href="ficheEtud?etudid=%s">%s</a>""" % (
-                        c, etud['etudid'], context.nomprenom(etud))
+                        c, etud['etudid'], etud['nomprenom'])
                 else:
-                    elink = context.nomprenom(etud)
+                    # ce n'est pas un etudiant ScoDoc
+                    elink = etud['nomprenom']
                 if not etud.get('paiementinscription', True):
                     elink = '<span class="paspaye">' + elink + ' (non paiement)</span>'
                 H.append("""<div class="pas_etud%s">""" % c )

Modified: branches/ScoDoc7/sco_page_etud.py
===================================================================
--- branches/ScoDoc7/sco_page_etud.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_page_etud.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -338,7 +338,7 @@
     etud = context.getEtudInfo(filled=1, REQUEST=REQUEST)[0]
 
     menuEtud = [
-        { 'title' : '%(sexe)s %(prenom)s %(nom)s' % etud,
+        { 'title' : etud['nomprenom'],
           'url' : 'ficheEtud?etudid=%(etudid)s' % etud,
           'enabled' : True,
           'helpmsg' : 'Fiche \xE9tudiant'

Modified: branches/ScoDoc7/sco_photos.py
===================================================================
--- branches/ScoDoc7/sco_photos.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_photos.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -114,7 +114,7 @@
     """HTML img tag for the photo, in small size.
     """
     path = etud_photo_url(context, etud, REQUEST=REQUEST)
-    nom = etud.get('nomprenom', etud['nom'])
+    nom = etud.get('nomprenom', etud['nom_disp'])
     if title is None:
         title = nom
     if not etud_photo_is_local(context, etud):
@@ -129,7 +129,7 @@
     They are the original uploaded images, converted in jpeg.
     """
     path = has_photo(context, etud)
-    nom = etud.get('nomprenom', etud['nom'])
+    nom = etud.get('nomprenom', etud['nom_disp'])
     if not path:
         path = unknown_image_path()
     path = url_from_rel_path(path)
@@ -286,24 +286,25 @@
     """Copy the photo from portal (distant website) to local fs.
     Returns rel. path or None if copy failed, a a diagnotic message
     """
+    scolars.format_etud_ident(etud)
     etudid = etud['etudid']
     url = photo_portal_url(context, etud)
     if not url:
-        return None, '%s: pas de code NIP' % context.nomprenom(etud)
+        return None, '%(nomprenom)s: pas de code NIP' % etud
     f = None
     try:
         log('copy_portal_photo_to_fs: getting %s' % url)
         f = urllib2.urlopen(url) # in python 2.6, should use a timeout
     except:
         log('download failed: exception:\n%s' % traceback.format_exc())        
-        return None, '%s: erreur chargement de %s' % (context.nomprenom(etud), url)
+        return None, '%s: erreur chargement de %s' % (etud['nomprenom'], url)
     if not f:
         log('download failed')
-        return None, '%s: erreur chargement de %s' % (context.nomprenom(etud), url)
+        return None, '%s: erreur chargement de %s' % (etud['nomprenom'], url)
     data = f.read()
     status, diag = store_photo(context, etud, data, REQUEST=REQUEST)
     if status == 1:
         log('copy_portal_photo_to_fs: copied %s' % url)
-        return has_photo(context, etud), '%s: photo charg\xE9e' % context.nomprenom(etud)
+        return has_photo(context, etud), '%s: photo charg\xE9e' % etud['nomprenom']
     else:
-        return None, '%s: <b>%s</b>' % (context.nomprenom(etud), diag)
+        return None, '%s: <b>%s</b>' % (etud['nomprenom'], diag)

Modified: branches/ScoDoc7/sco_pvjury.py
===================================================================
--- branches/ScoDoc7/sco_pvjury.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_pvjury.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -5,7 +5,7 @@
 #
 # Gestion scolarite IUT
 #
-# Copyright (c) 2001 - 2006 Emmanuel Viennet.  All rights reserved.
+# Copyright (c) 2001 - 2013 Emmanuel Viennet.  All rights reserved.
 #
 # 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
@@ -28,6 +28,7 @@
 """Edition des PV de jury
 """
 
+import scolars
 import sco_parcours_dut
 import sco_codes_parcours
 import sco_excel
@@ -248,8 +249,9 @@
     
     lines = []
     for e in dpv['decisions']:
+        scolars.format_etud_ident(e['identite'])
         l = { 'etudid' : e['identite']['etudid'],
-              'nomprenom' : context.nomprenom(e['identite']),
+              'nomprenom' : e['identite']['nomprenom'],
               '_nomprenom_target' : '%s/ficheEtud?etudid=%s' % (context.ScoURL(),e['identite']['etudid']),
               '_nomprenom_td_attrs' : 'id="%s" class="etudinfo"' % e['identite']['etudid'],
               'parcours' : e['parcours'],

Modified: branches/ScoDoc7/sco_synchro_etuds.py
===================================================================
--- branches/ScoDoc7/sco_synchro_etuds.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/sco_synchro_etuds.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -152,11 +152,12 @@
             if a_desinscrire or a_desinscrire_without_key:
                 H.append('<h3>Etudiants \xE0 d\xE9sinscrire :</h3><ol>')
                 for key in a_desinscrire:
-                    etud = context.getEtudInfo(code_nip=key)[0]
-                    H.append('<li class="desinscription">%s</li>' % context.nomprenom(etud) )
+                    etud = context.getEtudInfo(filled=1, code_nip=key)[0]
+                    H.append('<li class="desinscription">%(nomprenom)s</li>' % etud )
                 for etudid in a_desinscrire_without_key:
                     etud = inscrits_without_key_all[etudid]
-                    H.append('<li class="desinscription">%s</li>' % context.nomprenom(etud) )
+                    scolars.format_etud_ident(etud)
+                    H.append('<li class="desinscription">%(nomprenom)s</li>' % etud )
                 H.append('</ol>')
 
             if not a_importer and not a_inscrire and not a_desinscrire:
@@ -238,7 +239,7 @@
         """<p>Actuellement <b>%d</b> inscrits dans ce semestre.</p>"""
         % (len(etuds_by_cat['etuds_ok']['etuds'])+len(etuds_by_cat['etuds_nonapogee']['etuds'])+len(etuds_by_cat['inscrits_without_key']['etuds'])),
         """<p>Code \xE9tape Apog\xE9e: %(etape_apo_str)s</p>
-        <form method="post">
+        <form method="post" action="formsemestre_synchro_etuds">
         """ % sem,
         """
         Ann\xE9e Apog\xE9e: <select id="anneeapogee" name="anneeapogee" onchange="document.location='formsemestre_synchro_etuds?formsemestre_id=%s&anneeapogee='+document.getElementById('anneeapogee').value">""" % (sem['formsemestre_id']),

Modified: branches/ScoDoc7/scolars.py
===================================================================
--- branches/ScoDoc7/scolars.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/scolars.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -54,11 +54,46 @@
                 'juillet', 'aout', 'septembre', 'octobre', 'novembre',
                 'd\xE9cembre' ]                
 
+def format_etud_ident(etud):
+    """Format identite de l'\xE9tudiant (modifi\xE9 en place)
+    nom, pr\xE9nom et formes associees
+    """
+    etud['nom'] = format_nom(etud['nom'])
+    if 'nom_usuel' in etud:
+        etud['nom_usuel'] =  format_nom(etud['nom_usuel'])
+    else:
+        etud['nom_usuel'] = ''
+    etud['prenom'] = format_prenom(etud['prenom'])
+    etud['sexe'] = format_sexe(etud['sexe'])
+    # Nom \xE0 afficher:
+    if etud['nom_usuel']:
+        etud['nom_disp'] = etud['nom_usuel']
+        if etud['nom']:
+            etud['nom_disp'] += ' (' + etud['nom'] + ')'
+    else:
+        etud['nom_disp'] = etud['nom']
+
+    etud['nomprenom'] = format_nomprenom(etud) # M. Pierre DUPONT
+    if etud['sexe'] == 'M.':
+        etud['ne'] = ''
+    else:
+        etud['ne'] = 'e'
+    if 'email' in etud and etud['email']:
+        etud['emaillink'] = '<a class="stdlink" href="mailto:%s">%s</a>'%(etud['email'],etud['email'])
+    else:
+        etud['emaillink'] = '<em>(pas d\'adresse e-mail)</em>'
+
+
+
 def force_uppercase(s):
     if s:
         s = s.upper()
     return s
 
+def format_nomprenom(etud):
+    "formatte sexe/nom/prenom pour affichages"
+    return ' '.join([ format_sexe(etud['sexe']), format_prenom(etud['prenom']), etud['nom_disp']])
+
 def format_prenom(s):
     "formatte prenom etudiant pour affichage"
     locale.setlocale(locale.LC_ALL, ('en_US', 'ISO8859-15') )
@@ -77,6 +112,8 @@
 #    return '-'.join( [ x.lower().capitalize() for x in frags ] )
 
 def format_nom(s):
+    if not s:
+        return ''
     return s.upper()
 
 def format_sexe(sexe):
@@ -147,8 +184,8 @@
 _identiteEditor = EditableTable(
     'identite',
     'etudid',
-    ('etudid','nom','prenom','sexe',
-     'date_naissance','lieu_naissance',
+    ('etudid', 'nom', 'nom_usuel', 'prenom', 'sexe',
+     'date_naissance', 'lieu_naissance',
      'nationalite', 
      'statut',
      'foto', 'photo_filename', 'code_ine', 'code_nip'),
@@ -175,6 +212,11 @@
             o['annee_naissance'] = o['date_naissance']
     return objs
 
+def identite_edit_nocheck(cnx, args):
+    """Modifie les champs mentionnes dans args, sans verification ni notification.
+    """
+    _identiteEditor.edit(cnx, args)
+
 def check_nom_prenom(cnx, nom='', prenom='', etudid=None):
     """Check if nom and prenom are valid.
     Also check for duplicates (homonyms), excluding etudid : 
@@ -247,7 +289,8 @@
     if notify_to:
         # etat AVANT edition pour envoyer diffs
         before = identite_list(cnx, {'etudid':args['etudid']})[0]    
-    _identiteEditor.edit(cnx, args)
+    
+    identite_edit_nocheck(cnx, args)
 
     # Notification du changement par e-mail:
     if notify_to:
@@ -256,9 +299,6 @@
         notify_etud_change(context, notify_to, etud, before, after,
                            'Modification identite %(nomprenom)s' % etud)
 
-def identite_edit_nocheck(cnx, args):
-    "Modifie les champs mentionnes dans args, sans verification ni notification"""
-    _identiteEditor.edit(cnx, args)
 
 def identite_create( cnx, args, context=None, REQUEST=None ):
     "check unique etudid, then create"

Modified: branches/ScoDoc7/tests/conn_info.py
===================================================================
--- branches/ScoDoc7/tests/conn_info.py	2013-10-05 13:27:25 UTC (rev 1272)
+++ branches/ScoDoc7/tests/conn_info.py	2013-10-06 15:18:20 UTC (rev 1273)
@@ -2,7 +2,9 @@
 """Put here the informations neeede to connect to your development test user
 """
 
-SCODOC='https://scodoc.example.com/'
+#SCODOC='https://scodoc.example.com/'
+SCODOC='https://scodoc.viennet.net/'
 USER = 'tester'
-PASSWD = 'XXXXXXXXXXXX'
+#PASSWD = 'XXXXXXXXXXXX'
+PASSWD = '67un^:653'
 


Plus d'informations sur la liste de diffusion Scodoc-devel