[Scodoc-devel] [SVN] Scolar : [1532] Feuille placement des etudiants en examen (contrib.

eviennet at lipn.univ-paris13.fr eviennet at lipn.univ-paris13.fr
Lun 18 Juil 09:20:40 CEST 2016


Une pièce jointe HTML a été nettoyée...
URL: <https://www-rt.iutv.univ-paris13.fr/pipermail/scodoc-devel/attachments/20160718/0baa3aa5/attachment-0001.html>
-------------- section suivante --------------
Modified: branches/ScoDoc7/ZNotes.py
===================================================================
--- branches/ScoDoc7/ZNotes.py	2016-07-17 14:03:17 UTC (rev 1531)
+++ branches/ScoDoc7/ZNotes.py	2016-07-18 07:20:38 UTC (rev 1532)
@@ -67,6 +67,7 @@
 import sco_recapcomplet
 import sco_liste_notes
 import sco_saisie_notes
+import sco_placement
 import sco_undo_notes
 import sco_formations
 import sco_report
@@ -2206,6 +2207,13 @@
     security.declareProtected(ScoView, 'formsemestre_check_absences_html')
     formsemestre_check_absences_html = sco_liste_notes.formsemestre_check_absences_html
 
+    # --- Placement des étudiants pour l'évaluation
+    security.declareProtected(ScoEnsView, 'placement_eval_selectetuds')
+    placement_eval_selectetuds = sco_placement.placement_eval_selectetuds
+
+    security.declareProtected(ScoEnsView, 'do_placement')
+    do_placement = sco_placement.do_placement
+    
     # --- Saisie des notes    
     security.declareProtected(ScoEnsView, 'notes_eval_selectetuds')
     notes_eval_selectetuds = sco_saisie_notes.notes_eval_selectetuds

Modified: branches/ScoDoc7/sco_excel.py
===================================================================
--- branches/ScoDoc7/sco_excel.py	2016-07-17 14:03:17 UTC (rev 1531)
+++ branches/ScoDoc7/sco_excel.py	2016-07-18 07:20:38 UTC (rev 1532)
@@ -129,11 +129,15 @@
 #         return doc.savetostr(self.get_biff_data())
 
 
-def Excel_MakeStyle( bold=False, color='black', bgcolor=None, halign=None, valign=None ):
+def Excel_MakeStyle( bold=False, italic=False, 
+                     color='black', bgcolor=None, 
+                     halign=None, valign=None ):
     style = XFStyle()
     font = Font()
     if bold:
         font.bold = bold
+    if italic:
+        font.italic = italic
     font.name = 'Arial'                    
     colour_index = COLOR_CODES.get(color, None)    
     if colour_index:

Modified: branches/ScoDoc7/sco_moduleimpl_status.py
===================================================================
--- branches/ScoDoc7/sco_moduleimpl_status.py	2016-07-17 14:03:17 UTC (rev 1531)
+++ branches/ScoDoc7/sco_moduleimpl_status.py	2016-07-18 07:20:38 UTC (rev 1532)
@@ -82,7 +82,11 @@
         { 'title' : 'Afficher les notes',
           'url' : 'evaluation_listenotes?evaluation_id=' + evaluation_id,
           'enabled' : nbnotes > 0
-          },            
+          },
+        { 'title' : 'Placement étudiants',
+          'url' : 'placement_eval_selectetuds?evaluation_id=' + evaluation_id,
+          'enabled' : nbnotes == 0 and context.can_edit_notes(REQUEST.AUTHENTICATED_USER, E['moduleimpl_id'])
+          },
         { 'title' : 'Absences ce jour',
           'url' : 'Absences/EtatAbsencesDate?date=%s&group_ids=%s'
           % (urllib.quote(E['jour'],safe=''), group_id),
@@ -164,7 +168,7 @@
         if context.can_change_ens(REQUEST, moduleimpl_id, raise_exc=False):
             H.append(' (<a class="stdlink" href="edit_moduleimpl_expr?moduleimpl_id=%s">changer</a>)' % moduleimpl_id)
         H.append('</td></tr>')
-    H.append('<tr><td colspan="2"><a class="stdlink" href="view_module_abs?moduleimpl_id=%s">Absences</a>' % moduleimpl_id)
+    H.append('<tr><td colspan="2"><a class="stdlink" href="view_module_abs?moduleimpl_id=%s">Absences</a> ' % moduleimpl_id)
     H.append('</table>')
     #
     nt = context._getNotesCache().get_NotesTable(context, formsemestre_id)

Added: branches/ScoDoc7/sco_placement.py
===================================================================
--- branches/ScoDoc7/sco_placement.py	                        (rev 0)
+++ branches/ScoDoc7/sco_placement.py	2016-07-18 07:20:38 UTC (rev 1532)
@@ -0,0 +1,691 @@
+# -*- mode: python -*-
+# -*- coding: utf-8 -*-
+
+##############################################################################
+#
+# Gestion scolarite IUT
+#
+# Copyright (c) 2001 - 2016 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
+#
+##############################################################################
+
+"""ScoDoc: génération feuille émargement et placement
+
+Contribution M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016
+
+"""
+
+from notesdb import *
+from sco_utils import *
+from notes_log import log
+import scolars
+import sco_formsemestre
+import sco_groups
+import sco_excel
+from sco_excel import *
+from gen_tables import GenTable
+import random
+
+
+def do_placement_selectetuds(context, REQUEST):
+    """
+    Choisi les etudiants et les infos sur la salle pour le placement des etudiants
+    """
+    evaluation_id = REQUEST.form['evaluation_id']
+    E = context.do_evaluation_list( {'evaluation_id' : evaluation_id})
+    if not E:
+        raise ScoValueError("invalid evaluation_id")
+    E = E[0]
+    M = context.do_moduleimpl_list( args={ 'moduleimpl_id' : E['moduleimpl_id'] } )[0]
+    formsemestre_id = M['formsemestre_id']
+    # groupes
+    groups = sco_groups.do_evaluation_listegroupes(context,evaluation_id, include_default=True)
+    grlabs = [ g['group_name'] or 'tous' for g in groups ]  # legendes des boutons
+    grnams  = [ g['group_id'] for g in groups ] # noms des checkbox
+    no_groups = (len(groups) == 1) and groups[0]['group_name'] is None
+    
+    # description de l'evaluation    
+    H = [ context.evaluation_create_form(
+        evaluation_id=evaluation_id, REQUEST=REQUEST, readonly=1),
+        '<h3>Placement et émargement des étudiants</h3>'
+        ]
+    #
+    descr = [
+        ('evaluation_id', { 'default' : evaluation_id, 'input_type' : 'hidden' }),
+        ('placement_method', {'input_type' : 'radio', 'default' : 'xls', 'allow_null' : False, 
+                              'allowed_values' : [ 'pdf', 'xls' ],
+                              'labels' : ['fichier pdf', 'fichier xls'],
+                              'title' : 'Format de fichier :' }),
+        ('teachers', { 'size' : 25, 'title' : 'Surveillants :'}),
+        ('building', { 'size' : 25, 'title' : 'Batiment :'}),
+        ('room', { 'size' : 10, 'title' : 'Salle :'}),
+        ('columns', {'input_type' : 'radio', 'default' : '5', 'allow_null' : False, 
+                     'allowed_values' : [ '3', '4', '5', '6', '7', '8' ],
+                     'labels' : ['3 colonnes', '4 colonnes', '5 colonnes', '6 colonnes', '7 colonnes', '8 colonnes'],
+                         'title' : 'Nombre de colonnes :' }),
+        ('numbering', {'input_type' : 'radio', 
+                       'default' : 'coordinate', 
+                       'allow_null' : False, 
+                       'allowed_values' : [ 'continuous', 'coordinate' ], 
+                       'labels' : ['continue', 'coordonnées'], 
+                       'title' : 'Numérotation :' })]
+    if no_groups:
+        submitbuttonattributes = []
+        descr += [ 
+            ('group_ids', { 'default' : [g['group_id'] for g in groups],  'input_type' : 'hidden', 'type':'list' }) ]
+    else:
+        descr += [ 
+            ('group_ids', { 'input_type' : 'checkbox',
+                            'title':'Choix groupe(s) d\'étudiants :',
+                            'allowed_values' : grnams, 'labels' : grlabs,
+                            'attributes' : ['onchange="gr_change(this);"']
+                            }) ]
+         
+        if not(REQUEST.form.has_key('group_ids') and REQUEST.form['group_ids']):
+            submitbuttonattributes = [ 'disabled="1"' ]
+        else:
+            submitbuttonattributes = [] # groupe(s) preselectionnés
+        H.append(
+          # JS pour desactiver le bouton OK si aucun groupe selectionné
+          """<script type="text/javascript">
+          function gr_change(e) {
+          var boxes = document.getElementsByName("group_ids:list");
+          var nbchecked = 0;
+          for (var i=0; i < boxes.length; i++) {
+              if (boxes[i].checked)
+                 nbchecked++;
+          }
+          if (nbchecked > 0) {
+              document.getElementsByName('gr_submit')[0].disabled=false;
+          } else {
+              document.getElementsByName('gr_submit')[0].disabled=true;
+          }
+          }
+          </script>
+          """
+          )
+
+    tf = TrivialFormulator( REQUEST.URL0, REQUEST.form, descr,
+                            cancelbutton = 'Annuler',
+                            submitbuttonattributes=submitbuttonattributes,
+                            submitlabel = 'OK', formid='gr' )
+    if  tf[0] == 0:
+        #H.append( """<div class="saisienote_etape1">
+        #<span class="titredivplacementetudiants">Choix du groupe et de la localisation</span>
+        #""")
+        H.append("""<div class="saisienote_etape1">""")
+        return '\n'.join(H) + '\n' + tf[1] + "\n</div>"
+    elif tf[0] == -1:
+        return REQUEST.RESPONSE.redirect( '%s/Notes/moduleimpl_status?moduleimpl_id=%s'
+                                          % (context.ScoURL(),E['moduleimpl_id']) )
+    else:
+        placement_method = tf[2]['placement_method']
+        teachers = tf[2]['teachers']
+        building = tf[2]['building']
+        room = tf[2]['room']
+        group_ids = tf[2]['group_ids']
+        columns = tf[2]['columns']
+        numbering = tf[2]['numbering']
+        if columns in ('3', '4', '5', '6', '7', '8'):
+            # return notes_evaluation_formnotes( REQUEST )
+            gs = [('group_ids%3Alist=' + urllib.quote_plus(x)) for x in group_ids ]
+            query = 'evaluation_id=%s&placement_method=%s&teachers=%s&building=%s&room=%s&columns=%s&numbering=%s&' % (evaluation_id,placement_method,teachers,building,room,columns,numbering) + '&'.join(gs)
+            return REQUEST.RESPONSE.redirect( REQUEST.URL1 + '/do_placement?' + query )
+        else:
+            raise ValueError, "invalid placement_method (%s)" % tf[2]['placement_method'] 
+
+
+def do_placement(context, REQUEST ):
+    """
+    Choisi le placement
+    """
+    authuser = REQUEST.AUTHENTICATED_USER
+    authusername = str(authuser)
+    try:
+        evaluation_id = REQUEST.form['evaluation_id']
+    except:        
+        raise ScoValueError("Formulaire incomplet ! Vous avez sans doute attendu trop longtemps, veuillez vous reconnecter. Si le problème persiste, contacter l'administrateur. Merci.")
+    E = context.do_evaluation_list( {'evaluation_id' : evaluation_id})[0]
+    jour_iso = DateDMYtoISO(E['jour'])
+    
+    # Check access
+    # (admin, respformation, and responsable_id)
+    if not context.can_edit_notes( authuser, E['moduleimpl_id'] ):
+        return '<h2>Génération du placement impossible pour %s</h2>' % authusername\
+               + """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
+               avez l'autorisation d'effectuer cette opération)</p>
+               <p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
+               """ % E['moduleimpl_id']
+    cnx = context.GetDBConnexion()
+    # Infos transmises
+    placement_method = REQUEST.form['placement_method']
+    teachers = REQUEST.form['teachers']
+    building = REQUEST.form['building']
+    room = REQUEST.form['room']
+    columns = REQUEST.form['columns']
+    numbering = REQUEST.form['numbering']
+      
+    # Construit liste des etudiants        
+    group_ids = REQUEST.form.get('group_ids', [] )
+    groups = sco_groups.listgroups(context, group_ids)
+    gr_title_filename = sco_groups.listgroups_filename(groups) 
+    gr_title = sco_groups.listgroups_abbrev(groups)
+
+    if None in [ g['group_name'] for g in groups ]: # tous les etudiants
+        getallstudents = True
+        gr_title = 'tous'
+        gr_title_filename = 'tous'
+    else:
+        getallstudents = False
+    etudids = sco_groups.do_evaluation_listeetuds_groups(
+        context, evaluation_id, groups, getallstudents=getallstudents, include_dems=True)
+    if not etudids:
+        return '<p>Aucun groupe sélectionné !</p>'
+    
+    M = context.do_moduleimpl_list( args={ 'moduleimpl_id' : E['moduleimpl_id'] } )[0]
+    Mod = context.do_module_list( args={ 'module_id' : M['module_id'] } )[0]
+    sem = sco_formsemestre.get_formsemestre(context, M['formsemestre_id'])
+    evalname = '%s-%s' % (Mod['code'],DateDMYtoISO(E['jour']))
+    if E['description']:
+        evaltitre = E['description']
+    else:
+        evaltitre = 'évaluation du %s' % E['jour']
+    
+    desceval = [] # une liste de liste de chaines: description de l'evaluation
+    desceval.append( ['%s' % sem['titreannee']] )
+    desceval.append( ['Module : %s - %s' % (Mod['code'],Mod['abbrev'])] )
+    desceval.append( ['Surveillants : %s' % teachers] )
+    desceval.append( ['Batiment : %s - Salle : %s' % (building,room)] )   
+    desceval.append( ['Controle : %s (coef. %g)' % (evaltitre, E['coefficient'])] )
+    
+    listetud = [] # liste de couples (nom,prenom)
+    for etudid in etudids:
+        # infos identite etudiant (xxx sous-optimal: 1/select par etudiant)
+        ident = scolars.etudident_list(cnx, { 'etudid' : etudid })[0] # XXX utiliser ZScolar (parent)
+        # infos inscription
+        inscr = context.do_formsemestre_inscription_list(
+            {'etudid':etudid, 'formsemestre_id' : M['formsemestre_id']})[0]
+        if inscr['etat'] <> 'D':
+            nom = strupper(ident['nom'])
+            prenom = strcapitalize(strlower(ident['prenom']))
+            listetud.append( (nom, prenom) )
+    random.shuffle(listetud)
+    
+    sem_preferences = context.get_preferences()
+    space = sem_preferences.get('feuille_placement_emargement')
+    maxlines = sem_preferences.get('feuille_placement_positions')
+      
+    if placement_method == 'xls':
+        filename = 'placement_%s_%s.xls' % (evalname, gr_title_filename)
+        xls = Excel_feuille_placement( E, desceval, listetud, columns, space, maxlines, building, room, numbering )
+        return sco_excel.sendExcelFile(REQUEST, xls, filename)
+    else:
+        nbcolumns = int(columns)
+
+        pdf_title = '%s<br/>' % sem['titreannee']
+        pdf_title += 'Module : %s - %s<br/>' % (Mod['code'],Mod['abbrev'])
+        pdf_title += 'Surveillants : %s<br/>' % teachers
+        pdf_title += 'Batiment : %s - Salle : %s<br/>' % (building,room)
+        pdf_title += 'Controle : %s (coef. %g)<br/>' % (evaltitre, E['coefficient'])
+        pdf_title += 'Date : %s - Horaire : %s à %s' % (E['jour'],E['heure_debut'],E['heure_fin'])
+        
+        filename = 'placement_%s_%s.pdf' % (evalname, gr_title_filename)
+        titles={ 'nom' : 'Nom', 'prenom' : 'Prenom', 'colonne' : 'Colonne', 'ligne' : 'Ligne', 'place' : 'Place' }
+        if numbering == 'coordinate':
+                columns_ids = [ 'nom', 'prenom', 'colonne', 'ligne' ]
+        else:
+                columns_ids = [ 'nom', 'prenom', 'place' ]
+
+        # etudiants
+        line = 1
+        col = 1
+        orderetud = [] 
+        for etudid in listetud:
+            if numbering == 'coordinate':
+                orderetud.append((etudid[0],etudid[1],col,line))    
+            else:
+                orderetud.append((etudid[0],etudid[1],col+(line-1)*nbcolumns))
+
+            if col == nbcolumns:
+                col = 0
+                line += 1
+            col += 1
+
+        rows = []
+        orderetud.sort()
+        for etudid in orderetud:
+            if numbering == 'coordinate':
+                rows.append({
+                    'nom' : etudid[0],
+                    'prenom' : etudid[1],
+                    'colonne' : etudid[2],
+                    'ligne' : etudid[3]
+                    })
+            else:
+                rows.append({
+                    'nom' : etudid[0],
+                    'prenom' : etudid[1],
+                    'place' : etudid[2]
+                    })
+
+        tab = GenTable( titles=titles, columns_ids=columns_ids,
+                                        rows=rows,
+                                        filename=filename,
+                        origin = 'Généré par %s le ' % VERSION.SCONAME + timedate_human_repr() + '',
+                        pdf_title = pdf_title,
+                        # pdf_shorttitle = '',
+                        preferences=context.get_preferences(M['formsemestre_id']),
+                    #html_generate_cells=False # la derniere ligne (moyennes) est incomplete
+                        )
+        t = tab.make_page(context, format='pdf', with_html_headers=False, REQUEST=REQUEST)
+        return t
+
+def placement_eval_selectetuds(context, evaluation_id, REQUEST=None):
+    """Dialogue placement etudiants: choix methode et localisation
+    """
+    evals = context.do_evaluation_list( {'evaluation_id' : evaluation_id})
+    if not evals:
+        raise ScoValueError('invalid evaluation_id')
+    theeval = evals[0]
+
+    if theeval['description']:
+        page_title = 'Placement "%s"' % theeval['description']
+    else:
+        page_title = 'Placement des étudiants'
+    H = [ context.sco_header(REQUEST, page_title=page_title) ]
+    
+    formid = 'placementfile'
+    if not REQUEST.form.get('%s-submitted'%formid,False):
+        # not submitted, choix groupe
+        r = do_placement_selectetuds(context, REQUEST)
+        if r:
+            H.append(r)            
+
+    H.append("""<h3>Explications</h3>
+<ul>
+<li>Choisir le format du fichier résultat :</li>
+<ul>
+<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;</li>
+<li>le format xls produit un classeur avec deux onglets</li>
+<ul>
+<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et peut servir de feuille d'émargement;</li>
+<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
+</ul>
+</ul> 
+<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer le nombre de colonnes;</li> 
+<li>deux types de placements sont possibles :</li>
+<ul>
+<li>continue suppose que les tables ont toutes un numéro unique;</li>
+<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
+</ul>
+</ul>
+""")
+    H.append( context.sco_footer(REQUEST) )
+    return '\n'.join(H)
+
+
+
+    
+def Excel_feuille_placement( E, description, listetud, columns, space, maxlines, building, room, numbering ):
+    """Genere feuille excel pour placement des etudiants.
+    E: evaluation (dict)
+    lines: liste de tuples
+               (etudid, nom, prenom, etat, groupe, val, explanation)
+    """
+    nbcolumns = int(columns)
+    
+    wb = Workbook()
+    
+    SheetName0 = 'Emargement'
+    ws0 = wb.add_sheet(SheetName0.decode(SCO_ENCODING))
+    # ajuste largeurs colonnes (unite inconnue, empirique)
+    width=4500
+    if nbcolumns>5:
+        width=22500/nbcolumns
+
+    for col in range(nbcolumns):
+        ws0.col(col+1).width = width 
+        ws0.col(0).width=750
+    
+    SheetName1 = 'Positions'
+    ws1 = wb.add_sheet(SheetName1.decode(SCO_ENCODING))
+    if numbering == 'coordinate':
+        ws1.col(0).width=4000
+        ws1.col(1).width=4500
+        ws1.col(2).width=1500
+        ws1.col(3).width=1500
+
+        ws1.col(4).width=500
+
+        ws1.col(5).width=4000
+        ws1.col(6).width=4500
+        ws1.col(7).width=1500
+        ws1.col(8).width=1500
+    else:
+        ws1.col(0).width=4000
+        ws1.col(1).width=4500
+        ws1.col(2).width=3000
+
+        ws1.col(3).width=500
+
+        ws1.col(4).width=4000
+        ws1.col(5).width=4500
+        ws1.col(6).width=3000
+    
+    # styles
+    font0 = Font()
+    font0.name = 'Arial'
+    font0.bold = True
+    font0.height = 12*0x14
+    
+    font1b = Font()
+    font1b.name = 'Arial'
+    font1b.bold = True
+    font1b.height = 9*0x14
+
+    font1i = Font()
+    font1i.name = 'Arial'
+    font1i.height = 10*0x14
+    font1i.italic = True
+
+    font1o = Font()
+    font1o.name = 'Arial'
+    font1o.height = 10*0x14
+    font1o.outline = True
+    
+    font2bi = Font()
+    font2bi.name = 'Arial'
+    font2bi.height = 8*0x14
+    font2bi.bold = True
+    font2bi.italic = True
+    
+    font2 = Font()
+    font2.name = 'Arial'
+    font2.height = 10*0x14
+    
+    style_titres = XFStyle()
+    style_titres.font = font0
+    
+    style1t = XFStyle()
+    style1t.font = font1b
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_CENTER
+    alignment.vert = Alignment.VERT_CENTER
+    style1t.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.DOUBLE
+    borders.top = Borders.DOUBLE
+    borders.bottom = Borders.NO_LINE
+    borders.right = Borders.DOUBLE
+    style1t.borders = borders
+    
+    style1m = XFStyle()
+    style1m.font = font1b
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_CENTER
+    alignment.vert = Alignment.VERT_CENTER
+    style1m.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.DOUBLE
+    borders.top = Borders.NO_LINE
+    borders.bottom = Borders.THIN
+    borders.right = Borders.DOUBLE
+    style1m.borders = borders
+    
+    style1bm = XFStyle()
+    borders = Borders()
+    borders.left = Borders.DOUBLE
+    borders.top = Borders.NO_LINE
+    borders.bottom = Borders.NO_LINE
+    borders.right = Borders.DOUBLE
+    style1bm.borders = borders
+    
+    style1bb = XFStyle()
+    style1bb.font = font1o
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_RIGHT
+    alignment.vert = Alignment.VERT_BOTTOM
+    style1bb.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.DOUBLE
+    borders.top = Borders.NO_LINE
+    borders.bottom = Borders.DOUBLE
+    borders.right = Borders.DOUBLE
+    style1bb.borders = borders
+    
+    style2b = XFStyle()
+    style2b.font = font1i
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_CENTER
+    alignment.vert = Alignment.VERT_CENTER
+    style2b.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.THIN
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.THIN
+    style2b.borders = borders
+    
+    style2bi = XFStyle()
+    style2bi.font = font2bi
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_CENTER
+    alignment.vert = Alignment.VERT_CENTER
+    style2bi.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.THIN
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.THIN
+    style2bi.borders = borders
+    pattern = Pattern()
+    pattern.pattern = Pattern.SOLID_PATTERN
+    pattern._pattern_back_colour = 'gray'
+    style2bi.pattern = pattern
+    
+    style2l = XFStyle()
+    style2l.font = font2
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_LEFT
+    alignment.vert = Alignment.VERT_CENTER
+    style2l.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.THIN
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.NO_LINE
+    style2l.borders = borders
+    
+    style2m1 = XFStyle()
+    style2m1.font = font2
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_LEFT
+    alignment.vert = Alignment.VERT_CENTER
+    style2m1.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.NO_LINE
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.NO_LINE
+    style2m1.borders = borders
+    
+    style2m2 = XFStyle()
+    style2l.font = font2
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_RIGHT
+    alignment.vert = Alignment.VERT_CENTER
+    style2m2.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.NO_LINE
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.NO_LINE
+    style2m2.borders = borders
+
+    style2r = XFStyle()
+    style2l.font = font2
+    alignment = Alignment()
+    alignment.horz = Alignment.HORZ_RIGHT
+    alignment.vert = Alignment.VERT_CENTER
+    style2r.alignment = alignment
+    borders = Borders()
+    borders.left = Borders.NO_LINE
+    borders.top = Borders.THIN
+    borders.bottom = Borders.THIN
+    borders.right = Borders.THIN
+    style2r.borders = borders
+    
+    # ligne de titres
+    li = 0
+    line = 0
+    dt = time.strftime( '%d/%m/%Y a %Hh%M' )
+    ws0.write(li,0, u'Feuille placement etudiants éditée le %s' % dt, style_titres)
+    ws1.write(li,0, u'Feuille placement etudiants éditée le %s' % dt, style_titres)
+    for desceval in description:
+        if line%2 == 0:
+          li += 2
+        else:
+          li += 1
+        line += 1
+        ws0.write(li,0, desceval[0].decode(SCO_ENCODING), style_titres)
+        ws1.write(li,0, desceval[0].decode(SCO_ENCODING), style_titres)
+    li += 1
+    ws0.write(li,0, u'Date : %s - Horaire : %s à %s' % (E['jour'],E['heure_debut'],E['heure_fin']), style_titres)
+    ws1.write(li,0, u'Date : %s - Horaire : %s à %s' % (E['jour'],E['heure_debut'],E['heure_fin']), style_titres)
+    li+=1
+    
+    # entetes colonnes - feuille0
+    for col in range(nbcolumns):
+        ws0.write(li,col+1, u'colonne %s' % (col+1), style2b)
+    # entetes colonnes - feuille1
+    if numbering == 'coordinate':
+        ws1.write(li,0, u"Nom", style2bi)
+        ws1.write(li,1, u"Prénom", style2bi)
+        ws1.write(li,2, u"Colonne", style2bi)
+        ws1.write(li,3, u"Ligne", style2bi)
+
+        ws1.write(li,5, u"Nom", style2bi)
+        ws1.write(li,6, u"Prénom", style2bi)
+        ws1.write(li,7, u"Colonne", style2bi)
+        ws1.write(li,8, u"Ligne", style2bi)
+    else:
+        ws1.write(li,0, u"Nom", style2bi)
+        ws1.write(li,1, u"Prénom", style2bi)
+        ws1.write(li,2, u"Place", style2bi)
+
+        ws1.write(li,4, u"Nom", style2bi)
+        ws1.write(li,5, u"Prénom", style2bi)
+        ws1.write(li,6, u"Place", style2bi)
+        
+    # etudiants
+    line = 1
+    col = 1
+    linetud = []
+    orderetud = []
+    placementetud = []
+    for etudid in listetud:
+        linetud.append(etudid)
+        if numbering == 'coordinate':
+            orderetud.append((etudid[0],etudid[1],col,line))
+        else:
+            orderetud.append((etudid[0],etudid[1],col+(line-1)*nbcolumns))
+
+        if col == nbcolumns:
+            placementetud.append(linetud)
+            linetud = []
+            col = 0
+            line += 1
+        col += 1
+    if len(linetud) > 0:
+        placementetud.append(linetud)
+    
+    # etudiants - feuille0
+    line = 0
+    li0 = li
+    for linetud in placementetud:
+        li0 += 1
+        line += 1
+        ws0.write(li0,0,line,style2b)
+        col = 1
+        for etudid in linetud:
+            ws0.write(li0,col, (etudid[0]).decode(SCO_ENCODING), style1t )
+            ws0.write(li0+1,col, (etudid[1]).decode(SCO_ENCODING), style1m )
+            ws0.row(li0+2).height = space
+            if numbering == 'coordinate':
+                ws0.write(li0+2,col, ' ', style1bb )
+            else:
+                ws0.write(li0+2,col, u'place %s' % (col+(line-1)*nbcolumns), style1bb )
+            #ws0.write(li+3,col, ' ', style1bm )
+            #ws0.write(li+4,col, ' ', style1bb )
+
+            if col == nbcolumns:
+                col = 0
+                li0 += 2
+            col += 1
+            
+    # etudiants - feuille1
+    if numbering == 'coordinate':
+        coloffset = 5
+    else:
+        coloffset = 4
+    line = 0
+    li1 = li
+    nbcol = 0
+    col = 0
+    orderetud.sort()
+    for etudid in orderetud:
+        li1 += 1
+        line += 1
+        ws1.write(li1,col, (etudid[0]).decode(SCO_ENCODING), style2l )
+        ws1.write(li1,col+1, (etudid[1]).decode(SCO_ENCODING), style2m1 )
+        if numbering == 'coordinate':
+            ws1.write(li1,col+2, etudid[2], style2m2 )
+            ws1.write(li1,col+3, etudid[3], style2r )
+        else:
+            ws1.write(li1,col+2, etudid[2], style2r )
+          
+        if line == maxlines:
+            line = 0
+            li1 = li
+            nbcol = nbcol+1
+            col = col+coloffset
+            if nbcol == 2:
+                li = li+maxlines+2
+                li1 = li
+                nbcol = 0
+                col = 0
+                if numbering == 'coordinate':
+                    ws1.write(li,0, u"Nom", style2bi)
+                    ws1.write(li,1, u"Prénom", style2bi)
+                    ws1.write(li,2, u"Colonne", style2bi)
+                    ws1.write(li,3, u"Ligne", style2bi)
+
+                    ws1.write(li,5, u"Nom", style2bi)
+                    ws1.write(li,6, u"Prénom", style2bi)
+                    ws1.write(li,7, u"Colonne", style2bi)
+                    ws1.write(li,8, u"Ligne", style2bi)
+                else:
+                    ws1.write(li,0, u"Nom", style2bi)
+                    ws1.write(li,1, u"Prénom", style2bi)
+                    ws1.write(li,2, u"Place", style2bi)
+
+                    ws1.write(li,4, u"Nom", style2bi)
+                    ws1.write(li,5, u"Prénom", style2bi)
+                    ws1.write(li,6, u"Place", style2bi)                    
+    return wb.savetostr()

Modified: branches/ScoDoc7/sco_preferences.py
===================================================================
--- branches/ScoDoc7/sco_preferences.py	2016-07-17 14:03:17 UTC (rev 1531)
+++ branches/ScoDoc7/sco_preferences.py	2016-07-18 07:20:38 UTC (rev 1532)
@@ -1057,6 +1057,24 @@
         'convert_numbers' : True,
         'category' : 'feuilles'
         }),
+    ( 'feuille_placement_emargement',
+      {	'initvalue' : '625',
+        'title' : "Feuille d'émargement des contrôles - Signature étudiant",
+        'explanation' : "Hauteur de l'espace pour signer",
+        'size' : 4,
+        'type' : 'int',
+        'convert_numbers' : True,
+        'category' : 'feuilles'
+        }),
+    ( 'feuille_placement_positions',
+      {	'initvalue' : '45',
+        'title' : "Feuille des places lors des contrôles",
+        'explanation' : "Nombre maximum de lignes par colonne",
+        'size' : 4,
+        'type' : 'int',
+        'convert_numbers' : True,
+        'category' : 'feuilles'
+        }),
     # Feuille prepa jury
     ( 'prepa_jury_nip',
       { 'initvalue' : 0,


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