# -*- mode: python -*-
# -*- coding: utf-8 -*-

##############################################################################
#
# Gestion scolarite IUT
#
# Copyright (c) 1999 - 2021 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@viennet.net
#
##############################################################################

"""ScoDoc: génération feuille émargement et placement

Contribution M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016

"""
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import random

import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import log
from app.scodoc import html_sco_header
from app.scodoc import sco_edit_module
from app.scodoc import sco_evaluations
from app.scodoc import sco_excel
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_permissions_check
from app.scodoc import sco_preferences
from app.scodoc import sco_saisie_notes
from app.scodoc import sco_etud
from app.scodoc import VERSION
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_excel import *
from app.scodoc.TrivialFormulator import TrivialFormulator


def do_placement_selectetuds(context, REQUEST):
    """
    Choisi les étudiants et les infos sur la salle pour leur placement.
    """
    evaluation_id = REQUEST.form["evaluation_id"]
    E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})
    if not E:
        raise ScoValueError("invalid evaluation_id")
    E = E[0]
    # M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
    # 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 = [
        sco_evaluations.evaluation_describe(
            context, evaluation_id=evaluation_id, REQUEST=REQUEST
        ),
        "<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"]  # pylint: disable=invalid-sequence-index
                        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 ("group_ids" in REQUEST.form 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"
            % (scu.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"):
            gs = [
                ("group_ids%3Alist=" + six.moves.urllib.parse.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(scu.NotesURL() + "/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 = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]

    # Check access
    # (admin, respformation, and responsable_id)
    if not sco_permissions_check.can_edit_notes(context, 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 = ndb.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_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 = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
    Mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
    sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
    evalname = "%s-%s" % (Mod["code"], ndb.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 = sco_etud.etudident_list(cnx, {"etudid": etudid})[
            0
        ]  # XXX utiliser ZScolar (parent)
        # infos inscription
        inscr = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
            context, {"etudid": etudid, "formsemestre_id": M["formsemestre_id"]}
        )[0]
        if inscr["etat"] != "D":
            nom = scu.strupper(ident["nom"])
            prenom = scu.strcapitalize(scu.strlower(ident["prenom"]))
            listetud.append((nom, prenom))
    random.shuffle(listetud)

    sem_preferences = sco_preferences.SemPreferences(context)
    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
            + scu.timedate_human_repr()
            + "",
            pdf_title=pdf_title,
            # pdf_shorttitle = '',
            preferences=sco_preferences.SemPreferences(context, 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 = sco_evaluations.do_evaluation_list(
        context, {"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 = [html_sco_header.sco_header(context, 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(html_sco_header.sco_footer(context, 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(scu.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(scu.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(scu.SCO_ENCODING), style_titres)
        ws1.write(li, 0, desceval[0].decode(scu.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(scu.SCO_ENCODING), style1t)
            ws0.write(li0 + 1, col, (etudid[1]).decode(scu.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(scu.SCO_ENCODING), style2l)
        ws1.write(li1, col + 1, (etudid[1]).decode(scu.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()