diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py
index 1b0ea311c..46679b118 100644
--- a/app/scodoc/sco_excel.py
+++ b/app/scodoc/sco_excel.py
@@ -1,675 +1,654 @@
-# -*- 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
-#
-##############################################################################
-
-
-""" Excel file handling
-"""
-import time, datetime
-
-# #sco8 #py3 XXX TODO A revoir utiliser d'autres modules
-# from pyExcelerator import *
-
-import app.scodoc.sco_utils as scu
-from app.scodoc import notesdb
-from app.scodoc.notes_log import log
-from app.scodoc.scolog import logdb
-from app.scodoc.sco_exceptions import ScoValueError
-from app.scodoc import sco_preferences
-import six
-
-
-# colors, voir exemple format.py
-COLOR_CODES = {
- "black": 0,
- "red": 0x0A,
- "mauve": 0x19,
- "marron": 0x3C,
- "blue": 0x4,
- "orange": 0x34,
- "lightyellow": 0x2B,
-}
-
-
-def sendExcelFile(REQUEST, data, filename):
- """publication fichier.
- (on ne doit rien avoir émis avant, car ici sont générés les entetes)
- """
- filename = (
- scu.unescape_html(scu.suppress_accents(filename))
- .replace("&", "")
- .replace(" ", "_")
- )
- REQUEST.RESPONSE.setHeader("content-type", scu.XLS_MIMETYPE)
- REQUEST.RESPONSE.setHeader(
- "content-disposition", 'attachment; filename="%s"' % filename
- )
- return data
-
-
-## (stolen from xlrd)
-# Convert an Excel number (presumed to represent a date, a datetime or a time) into
-# a Python datetime.datetime
-# @param xldate The Excel number
-# @param datemode 0: 1900-based, 1: 1904-based.
-# @return a datetime.datetime object, to the nearest_second.
-#
Special case: if 0.0 <= xldate < 1.0, it is assumed to represent a time;
-# a datetime.time object will be returned.
-#
Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number"
-# is zero.
-
-_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01
-
-
-def xldate_as_datetime(xldate, datemode=0):
- if datemode not in (0, 1):
- raise ValueError("invalid mode %s" % datemode)
- if xldate == 0.00:
- return datetime.time(0, 0, 0)
- if xldate < 0.00:
- raise ValueError("invalid date code %s" % xldate)
- xldays = int(xldate)
- frac = xldate - xldays
- seconds = int(round(frac * 86400.0))
- assert 0 <= seconds <= 86400
- if seconds == 86400:
- seconds = 0
- xldays += 1
- if xldays >= _XLDAYS_TOO_LARGE[datemode]:
- raise ValueError("date too large %s" % xldate)
-
- if xldays == 0:
- # second = seconds % 60; minutes = seconds // 60
- minutes, second = divmod(seconds, 60)
- # minute = minutes % 60; hour = minutes // 60
- hour, minute = divmod(minutes, 60)
- return datetime.time(hour, minute, second)
-
- if xldays < 61 and datemode == 0:
- raise ValueError("ambiguous date %s" % xldate)
-
- return datetime.datetime.fromordinal(
- xldays + 693594 + 1462 * datemode
- ) + datetime.timedelta(seconds=seconds)
-
-
-# Sous-classes pour ajouter methode savetostr()
-# (generation de fichiers en memoire)
-# XXX ne marche pas car accès a methodes privees (__xxx)
-# -> on utilise version modifiee par nous meme de pyExcelerator
-#
-# class XlsDocWithSave(CompoundDoc.XlsDoc):
-# def savetostr(self, stream):
-# #added by Emmanuel: save method, but returns a string
-# # 1. Align stream on 0x1000 boundary (and therefore on sector boundary)
-# padding = '\x00' * (0x1000 - (len(stream) % 0x1000))
-# self.book_stream_len = len(stream) + len(padding)
-
-# self.__build_directory()
-# self.__build_sat()
-# self.__build_header()
-
-# return self.header+self.packed_MSAT_1st+stream+padding+self.packed_MSAT_2nd+self.packed_SAT+self.dir_stream
-
-# class WorkbookWithSave(Workbook):
-# def savetostr(self):
-# doc = XlsDocWithSave()
-# return doc.savetostr(self.get_biff_data())
-
-
-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:
- font.colour_index = colour_index
- if bgcolor:
- style.pattern = Pattern()
- style.pattern.pattern = Pattern.SOLID_PATTERN
- style.pattern.pattern_fore_colour = COLOR_CODES.get(bgcolor, None)
- al = None
- if halign:
- al = Alignment()
- al.horz = {
- "left": Alignment.HORZ_LEFT,
- "right": Alignment.HORZ_RIGHT,
- "center": Alignment.HORZ_CENTER,
- }[halign]
- if valign:
- if not al:
- al = Alignment()
- al.vert = {
- "top": Alignment.VERT_TOP,
- "bottom": VERT_BOTTOM,
- "center": VERT_CENTER,
- }[valign]
- if al:
- style.alignment = al
- style.font = font
- return style
-
-
-class ScoExcelSheet(object):
- def __init__(self, sheet_name="feuille", default_style=None):
- self.sheet_name = sheet_name
- self.cells = [] # list of list
- self.cells_styles_lico = {} # { (li,co) : style }
- self.cells_styles_li = {} # { li : style }
- self.cells_styles_co = {} # { co : style }
- if not default_style:
- default_style = Excel_MakeStyle()
- self.default_style = default_style
-
- def set_style(self, style=None, li=None, co=None):
- if li != None and co != None:
- self.cells_styles_lico[(li, co)] = style
- elif li != None:
- self.cells_styles_li[li] = style
- elif co != None:
- self.cells_styles_co[co] = style
-
- def append(self, l):
- """Append a line of cells"""
- self.cells.append(l)
-
- def get_cell_style(self, li, co):
- """Get style for specified cell"""
- return (
- self.cells_styles_lico.get((li, co), None)
- or self.cells_styles_li.get(li, None)
- or self.cells_styles_co.get(co, None)
- or self.default_style
- )
-
- def gen_workbook(self, wb=None):
- """Generates and returns a workbook from stored data.
- If wb, add a sheet (tab) to the existing workbook (in this case, returns None).
- """
- if wb == None:
- wb = Workbook() # Création du fichier
- sauvegarde = True
- else:
- sauvegarde = False
- ws0 = wb.add_sheet(self.sheet_name.decode(scu.SCO_ENCODING))
- li = 0
- for l in self.cells:
- co = 0
- for c in l:
- # safety net: allow only str, int and float
- # #py3 #sco8 A revoir lors de la ré-écriture de ce module
- # XXX if type(c) not in (IntType, FloatType):
- # c = str(c).decode(scu.SCO_ENCODING)
-
- ws0.write(li, co, c, self.get_cell_style(li, co))
- co += 1
- li += 1
- if sauvegarde == True:
- return wb.savetostr()
- else:
- return None
-
-
-def Excel_SimpleTable(titles=[], lines=[[]], SheetName="feuille", titlesStyles=[]):
- """Export simple type 'CSV': 1ere ligne en gras, le reste tel quel"""
- # XXX devrait maintenant utiliser ScoExcelSheet
- wb = Workbook()
- ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
- if not titlesStyles:
- style = Excel_MakeStyle(bold=True)
- titlesStyles = [style] * len(titles)
- # ligne de titres
- col = 0
- for it in titles:
- ws0.write(0, col, it.decode(scu.SCO_ENCODING), titlesStyles[col])
- col += 1
- # suite
- default_style = Excel_MakeStyle()
- text_style = Excel_MakeStyle()
- text_style.num_format_str = "@"
- li = 1
- for l in lines:
- col = 0
- for it in l:
- cell_style = default_style
- # safety net: allow only str, int and float
- if isinstance(it, LongType): # XXX
- it = int(it) # assume all ScoDoc longs fits in int !
- elif type(it) not in (IntType, FloatType): # XXX A REVOIR
- it = str(it).decode(scu.SCO_ENCODING)
- cell_style = text_style
- ws0.write(li, col, it, cell_style)
- col += 1
- li += 1
- #
- return wb.savetostr()
-
-
-def Excel_feuille_saisie(E, titreannee, description, lines):
- """Genere feuille excel pour saisie des notes.
- E: evaluation (dict)
- lines: liste de tuples
- (etudid, nom, prenom, etat, groupe, val, explanation)
- """
- SheetName = "Saisie notes"
- wb = Workbook()
- ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
- # ajuste largeurs colonnes (unite inconnue, empirique)
- ws0.col(0).width = 400 # codes
- ws0.col(1).width = 6000 # noms
- ws0.col(2).width = 4000 # prenoms
- ws0.col(3).width = 6000 # groupes
- ws0.col(4).width = 3000 # notes
- ws0.col(5).width = 13000 # remarques
- # styles
- style_titres = XFStyle()
- font0 = Font()
- font0.bold = True
- font0.name = "Arial"
- font0.bold = True
- font0.height = 14 * 0x14
- style_titres.font = font0
-
- style_expl = XFStyle()
- font_expl = Font()
- font_expl.name = "Arial"
- font_expl.italic = True
- font0.height = 12 * 0x14
- font_expl.colour_index = 0x0A # rouge, voir exemple format.py
- style_expl.font = font_expl
-
- topborders = Borders()
- topborders.top = 1
- topleftborders = Borders()
- topleftborders.top = 1
- topleftborders.left = 1
- rightborder = Borders()
- rightborder.right = 1
-
- style_ro = XFStyle() # cells read-only
- font_ro = Font()
- font_ro.name = "Arial"
- font_ro.colour_index = COLOR_CODES["mauve"]
- style_ro.font = font_ro
- style_ro.borders = rightborder
-
- style_dem = XFStyle() # cells read-only
- font_dem = Font()
- font_dem.name = "Arial"
- font_dem.colour_index = COLOR_CODES["marron"]
- style_dem.font = font_dem
- style_dem.borders = topborders
-
- style = XFStyle()
- font1 = Font()
- font1.name = "Arial"
- font1.height = 12 * 0x14
- style.font = font1
-
- style_nom = XFStyle() # style pour nom, prenom, groupe
- style_nom.font = font1
- style_nom.borders = topborders
-
- style_notes = XFStyle()
- font2 = Font()
- font2.name = "Arial"
- font2.bold = True
- style_notes.font = font2
- style_notes.num_format_str = "general"
- style_notes.pattern = Pattern() # fond jaune
- style_notes.pattern.pattern = Pattern.SOLID_PATTERN
- style_notes.pattern.pattern_fore_colour = COLOR_CODES["lightyellow"]
- style_notes.borders = topborders
-
- style_comment = XFStyle()
- font_comment = Font()
- font_comment.name = "Arial"
- font_comment.height = 9 * 0x14
- font_comment.colour_index = COLOR_CODES["blue"]
- style_comment.font = font_comment
- style_comment.borders = topborders
-
- # ligne de titres
- li = 0
- ws0.write(
- li, 0, u"Feuille saisie note (à enregistrer au format excel)", style_titres
- )
- li += 1
- ws0.write(li, 0, u"Saisir les notes dans la colonne E (cases jaunes)", style_expl)
- li += 1
- ws0.write(li, 0, u"Ne pas modifier les cases en mauve !", style_expl)
- li += 1
- # Nom du semestre
- ws0.write(
- li, 0, scu.unescape_html(titreannee).decode(scu.SCO_ENCODING), style_titres
- )
- li += 1
- # description evaluation
- ws0.write(
- li, 0, scu.unescape_html(description).decode(scu.SCO_ENCODING), style_titres
- )
- li += 1
- ws0.write(
- li, 0, u"Evaluation du %s (coef. %g)" % (E["jour"], E["coefficient"]), style
- )
- li += 1
- li += 1 # ligne blanche
- # code et titres colonnes
- ws0.write(li, 0, u"!%s" % E["evaluation_id"], style_ro)
- ws0.write(li, 1, u"Nom", style_titres)
- ws0.write(li, 2, u"Prénom", style_titres)
- ws0.write(li, 3, u"Groupe", style_titres)
- ws0.write(li, 4, u"Note sur %g" % E["note_max"], style_titres)
- ws0.write(li, 5, u"Remarque", style_titres)
- # etudiants
- for line in lines:
- li += 1
- st = style_nom
- ws0.write(li, 0, ("!" + line[0]).decode(scu.SCO_ENCODING), style_ro) # code
- if line[3] != "I":
- st = style_dem
- if line[3] == "D": # demissionnaire
- s = "DEM"
- else:
- s = line[3] # etat autre
- else:
- s = line[4] # groupes TD/TP/...
- ws0.write(li, 1, line[1].decode(scu.SCO_ENCODING), st)
- ws0.write(li, 2, line[2].decode(scu.SCO_ENCODING), st)
- ws0.write(li, 3, s.decode(scu.SCO_ENCODING), st)
- try:
- val = float(line[5])
- except:
- val = line[5].decode(scu.SCO_ENCODING)
- ws0.write(li, 4, val, style_notes) # note
- ws0.write(li, 5, line[6].decode(scu.SCO_ENCODING), style_comment) # comment
- # explication en bas
- li += 2
- ws0.write(li, 1, u"Code notes", style_titres)
- ws0.write(li + 1, 1, u"ABS", style_expl)
- ws0.write(li + 1, 2, u"absent (0)", style_expl)
- ws0.write(li + 2, 1, u"EXC", style_expl)
- ws0.write(li + 2, 2, u"pas prise en compte", style_expl)
- ws0.write(li + 3, 1, u"ATT", style_expl)
- ws0.write(li + 3, 2, u"en attente", style_expl)
- ws0.write(li + 4, 1, u"SUPR", style_expl)
- ws0.write(li + 4, 2, u"pour supprimer note déjà entrée", style_expl)
- ws0.write(li + 5, 1, u"", style_expl)
- ws0.write(li + 5, 2, u"cellule vide -> note non modifiée", style_expl)
- return wb.savetostr()
-
-
-def Excel_to_list(data, convert_to_string=str): # we may need 'encoding' argument ?
- """returns list of list
- convert_to_string is a conversion function applied to all non-string values (ie numbers)
- """
- try:
- P = parse_xls("", scu.SCO_ENCODING, doc=data)
- except:
- log("Excel_to_list: failure to import document")
- open("/tmp/last_scodoc_import_failure.xls", "w").write(data)
- raise ScoValueError(
- "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !"
- )
-
- diag = [] # liste de chaines pour former message d'erreur
- # n'utilise que la première feuille
- if len(P) < 1:
- diag.append("Aucune feuille trouvée dans le classeur !")
- return diag, None
- if len(P) > 1:
- diag.append("Attention: n'utilise que la première feuille du classeur !")
- # fill matrix
- sheet_name, values = P[0]
- sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace")
- if not values:
- diag.append("Aucune valeur trouvée dans le classeur !")
- return diag, None
- indexes = list(values.keys())
- # search numbers of rows and cols
- rows = [x[0] for x in indexes]
- cols = [x[1] for x in indexes]
- nbcols = max(cols) + 1
- nbrows = max(rows) + 1
- M = []
- for _ in range(nbrows):
- M.append([""] * nbcols)
-
- for row_idx, col_idx in indexes:
- v = values[(row_idx, col_idx)]
- if isinstance(v, six.text_type):
- v = v.encode(scu.SCO_ENCODING, "backslashreplace")
- elif convert_to_string:
- v = convert_to_string(v)
- M[row_idx][col_idx] = v
- diag.append('Feuille "%s", %d lignes' % (sheet_name, len(M)))
- # diag.append(str(M))
- #
- return diag, M
-
-
-#
-def Excel_feuille_listeappel(
- context,
- sem,
- groupname,
- lines,
- partitions=[], # partitions a montrer (colonnes)
- with_codes=False, # indique codes etuds
- with_paiement=False, # indique si etudiant a paye inscription
- server_name=None,
-):
- "generation feuille appel"
- formsemestre_id = sem["formsemestre_id"]
- SheetName = "Liste " + groupname
- wb = Workbook()
- ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
-
- font1 = Font()
- font1.name = "Arial"
- font1.height = 10 * 0x14
-
- font1i = Font()
- font1i.name = "Arial"
- font1i.height = 10 * 0x14
- font1i.italic = True
-
- style1i = XFStyle()
- style1i.font = font1i
-
- style1b = XFStyle()
- style1b.font = font1
- borders = Borders()
- borders.left = 1
- borders.top = 1
- borders.bottom = 1
- style1b.borders = borders
-
- style2 = XFStyle()
- font2 = Font()
- font2.name = "Arial"
- font2.height = 14 * 0x14
- style2.font = font2
-
- style2b = XFStyle()
- style2b.font = font1i
- borders = Borders()
- borders.left = 1
- borders.top = 1
- borders.bottom = 1
- borders.right = 1
- style2b.borders = borders
-
- style2tb = XFStyle()
- borders = Borders()
- borders.top = 1
- borders.bottom = 1
- style2tb.borders = borders
- style2tb.font = Font()
- style2tb.font.height = 16 * 0x14 # -> ligne hautes
-
- style2t3 = XFStyle()
- borders = Borders()
- borders.top = 1
- borders.bottom = 1
- borders.left = 1
- style2t3.borders = borders
-
- style2t3bold = XFStyle()
- borders = Borders()
- borders.top = 1
- borders.bottom = 1
- borders.left = 1
- style2t3bold.borders = borders
- fontb = Font()
- fontb.bold = True
- style2t3bold.font = fontb
-
- style3 = XFStyle()
- font3 = Font()
- font3.name = "Arial"
- font3.bold = True
- font3.height = 14 * 0x14
- style3.font = font3
-
- NbWeeks = 4 # nombre de colonnes pour remplir absences
-
- # ligne 1
- li = 0
- ws0.write(
- li,
- 1,
- (
- "%s %s (%s - %s)"
- % (
- sco_preferences.get_preference("DeptName", formsemestre_id),
- notesdb.unquote(sem["titre_num"]),
- sem["date_debut"],
- sem["date_fin"],
- )
- ).decode(scu.SCO_ENCODING),
- style2,
- )
- # ligne 2
- li += 1
- ws0.write(li, 1, u"Discipline :", style2)
- # ligne 3
- li += 1
- ws0.write(li, 1, u"Enseignant :", style2)
- ws0.write(li, 5, ("Groupe %s" % groupname).decode(scu.SCO_ENCODING), style3)
- # Avertissement pour ne pas confondre avec listes notes
- ws0.write(
- li + 1, 2, u"Ne pas utiliser cette feuille pour saisir les notes !", style1i
- )
- #
- li += 2
- li += 1
- ws0.write(li, 1, u"Nom", style3)
- co = 2
- for partition in partitions:
- if partition["partition_name"]:
- ws0.write(
- li, co, partition["partition_name"].decode(scu.SCO_ENCODING), style3
- )
- co += 1
- if with_codes:
- coc = co
- ws0.write(li, coc, u"etudid", style3)
- ws0.write(li, coc + 1, u"code_nip", style3)
- ws0.write(li, coc + 2, u"code_ine", style3)
- co += 3
-
- for i in range(NbWeeks):
- ws0.write(li, co + i, "", style2b)
- n = 0
- for t in lines:
- n += 1
- li += 1
- ws0.write(li, 0, n, style1b)
- nomprenom = (
- t["civilite_str"]
- + " "
- + t["nom"]
- + " "
- + scu.strcapitalize(scu.strlower(t["prenom"]))
- )
- style_nom = style2t3
- if with_paiement:
- paie = t.get("paiementinscription", None)
- if paie is None:
- nomprenom += " (inscription ?)"
- style_nom = style2t3bold
- elif not paie:
- nomprenom += " (non paiement)"
- style_nom = style2t3bold
- ws0.write(li, 1, nomprenom.decode(scu.SCO_ENCODING), style_nom)
- co = 2
- for partition in partitions:
- if partition["partition_name"]:
- ws0.write(
- li,
- co,
- t.get(partition["partition_id"], "").decode(scu.SCO_ENCODING),
- style2t3,
- )
- co += 1
- if with_codes:
- ws0.write(li, coc, t["etudid"].decode(scu.SCO_ENCODING), style2t3)
- if t["code_nip"]:
- code_nip = t["code_nip"].decode(scu.SCO_ENCODING)
- else:
- code_nip = u""
- ws0.write(li, coc + 1, code_nip, style2t3)
- if t["code_ine"]:
- code_ine = t["code_ine"].decode(scu.SCO_ENCODING)
- else:
- code_ine = u""
- ws0.write(li, coc + 2, code_ine, style2t3)
- if t["etath"]:
- etath = t["etath"].decode(scu.SCO_ENCODING)
- else:
- etath = u""
- ws0.write(li, co, etath, style2b) # etat
- for i in range(1, NbWeeks):
- ws0.write(li, co + i, u"", style2b) # cellules vides
- ws0.row(li).height = 850 # sans effet ?
- #
- li += 2
- dt = time.strftime("%d/%m/%Y à %Hh%M")
- if server_name:
- dt += " sur " + server_name
- ws0.write(li, 1, ("Liste éditée le " + dt).decode(scu.SCO_ENCODING), style1i)
- #
- ws0.col(0).width = 850
- ws0.col(1).width = 9000
-
- return wb.savetostr()
+# -*- 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
+#
+##############################################################################
+
+
+""" Excel file handling
+"""
+import time, datetime
+
+# #sco8 #py3 XXX TODO A revoir utiliser d'autres modules
+# from pyExcelerator import *
+
+import app.scodoc.sco_utils as scu
+from app.scodoc import notesdb
+from app.scodoc.notes_log import log
+from app.scodoc.scolog import logdb
+from app.scodoc.sco_exceptions import ScoValueError
+from app.scodoc import sco_preferences
+import six
+from openpyxl import Workbook
+from openpyxl.styles import Font, PatternFill, Border, Side, Alignment, Protection
+from openpyxl.cell import WriteOnlyCell
+from tempfile import NamedTemporaryFile
+
+# colors, voir exemple format.py
+COLOR_CODES = {
+ "black": 0,
+ "red": 0x0A,
+ "mauve": 0x19,
+ "marron": 0x3C,
+ "blue": 0x4,
+ "orange": 0x34,
+ "lightyellow": 0x2B,
+}
+
+
+def send_excel_file(REQUEST, data, filename):
+ """publication fichier.
+ (on ne doit rien avoir émis avant, car ici sont générés les entetes)
+ """
+ filename = (
+ scu.unescape_html(scu.suppress_accents(filename))
+ .replace("&", "")
+ .replace(" ", "_")
+ )
+ REQUEST.RESPONSE.setHeader("content-type", scu.XLSX_MIMETYPE)
+ REQUEST.RESPONSE.setHeader(
+ "content-disposition", 'attachment; filename="%s"' % filename
+ )
+ return data
+
+
+## (stolen from xlrd)
+# Convert an Excel number (presumed to represent a date, a datetime or a time) into
+# a Python datetime.datetime
+# @param xldate The Excel number
+# @param datemode 0: 1900-based, 1: 1904-based.
+# @return a datetime.datetime object, to the nearest_second.
+#
Special case: if 0.0 <= xldate < 1.0, it is assumed to represent a time;
+# a datetime.time object will be returned.
+#
Note: 1904-01-01 is not regarded as a valid date in the datemode 1 system; its "serial number"
+# is zero.
+
+_XLDAYS_TOO_LARGE = (2958466, 2958466 - 1462) # This is equivalent to 10000-01-01
+
+
+def xldate_as_datetime(xldate, datemode=0):
+ if datemode not in (0, 1):
+ raise ValueError("invalid mode %s" % datemode)
+ if xldate == 0.00:
+ return datetime.time(0, 0, 0)
+ if xldate < 0.00:
+ raise ValueError("invalid date code %s" % xldate)
+ xldays = int(xldate)
+ frac = xldate - xldays
+ seconds = int(round(frac * 86400.0))
+ assert 0 <= seconds <= 86400
+ if seconds == 86400:
+ seconds = 0
+ xldays += 1
+ if xldays >= _XLDAYS_TOO_LARGE[datemode]:
+ raise ValueError("date too large %s" % xldate)
+
+ if xldays == 0:
+ # second = seconds % 60; minutes = seconds // 60
+ minutes, second = divmod(seconds, 60)
+ # minute = minutes % 60; hour = minutes // 60
+ hour, minute = divmod(minutes, 60)
+ return datetime.time(hour, minute, second)
+
+ if xldays < 61 and datemode == 0:
+ raise ValueError("ambiguous date %s" % xldate)
+
+ return datetime.datetime.fromordinal(
+ xldays + 693594 + 1462 * datemode
+ ) + datetime.timedelta(seconds=seconds)
+
+
+# Sous-classes pour ajouter methode savetostr()
+# (generation de fichiers en memoire)
+# XXX ne marche pas car accès a methodes privees (__xxx)
+# -> on utilise version modifiee par nous meme de pyExcelerator
+#
+# class XlsDocWithSave(CompoundDoc.XlsDoc):
+# def savetostr(self, stream):
+# #added by Emmanuel: save method, but returns a string
+# # 1. Align stream on 0x1000 boundary (and therefore on sector boundary)
+# padding = '\x00' * (0x1000 - (len(stream) % 0x1000))
+# self.book_stream_len = len(stream) + len(padding)
+
+# self.__build_directory()
+# self.__build_sat()
+# self.__build_header()
+
+# return self.header+self.packed_MSAT_1st+stream+padding+self.packed_MSAT_2nd+self.packed_SAT+self.dir_stream
+
+# class WorkbookWithSave(Workbook):
+# def savetostr(self):
+# doc = XlsDocWithSave()
+# return doc.savetostr(self.get_biff_data())
+
+
+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:
+ font.colour_index = colour_index
+ if bgcolor:
+ style.pattern = Pattern()
+ style.pattern.pattern = Pattern.SOLID_PATTERN
+ style.pattern.pattern_fore_colour = COLOR_CODES.get(bgcolor, None)
+ al = None
+ if halign:
+ al = Alignment()
+ al.horz = {
+ "left": Alignment.HORZ_LEFT,
+ "right": Alignment.HORZ_RIGHT,
+ "center": Alignment.HORZ_CENTER,
+ }[halign]
+ if valign:
+ if not al:
+ al = Alignment()
+ al.vert = {
+ "top": Alignment.VERT_TOP,
+ "bottom": VERT_BOTTOM,
+ "center": VERT_CENTER,
+ }[valign]
+ if al:
+ style.alignment = al
+ style.font = font
+ return style
+
+
+class ScoExcelSheet(object):
+ def __init__(self, sheet_name="feuille", default_style=None):
+ self.sheet_name = sheet_name
+ self.cells = [] # list of list
+ self.cells_styles_lico = {} # { (li,co) : style }
+ self.cells_styles_li = {} # { li : style }
+ self.cells_styles_co = {} # { co : style }
+ if not default_style:
+ default_style = Excel_MakeStyle()
+ self.default_style = default_style
+
+ def set_style(self, style=None, li=None, co=None):
+ if li != None and co != None:
+ self.cells_styles_lico[(li, co)] = style
+ elif li != None:
+ self.cells_styles_li[li] = style
+ elif co != None:
+ self.cells_styles_co[co] = style
+
+ def append(self, l):
+ """Append a line of cells"""
+ self.cells.append(l)
+
+ def get_cell_style(self, li, co):
+ """Get style for specified cell"""
+ return (
+ self.cells_styles_lico.get((li, co), None)
+ or self.cells_styles_li.get(li, None)
+ or self.cells_styles_co.get(co, None)
+ or self.default_style
+ )
+
+ def gen_workbook(self, wb=None):
+ """Generates and returns a workbook from stored data.
+ If wb, add a sheet (tab) to the existing workbook (in this case, returns None).
+ """
+ if wb == None:
+ wb = Workbook() # Création du fichier
+ sauvegarde = True
+ else:
+ sauvegarde = False
+ ws0 = wb.add_sheet(self.sheet_name)
+ li = 0
+ for l in self.cells:
+ co = 0
+ for c in l:
+ # safety net: allow only str, int and float
+ # #py3 #sco8 A revoir lors de la ré-écriture de ce module
+ # XXX if type(c) not in (IntType, FloatType):
+ # c = str(c).decode(scu.SCO_ENCODING)
+
+ ws0.write(li, co, c, self.get_cell_style(li, co))
+ co += 1
+ li += 1
+ if sauvegarde == True:
+ return wb.savetostr()
+ else:
+ return None
+
+
+def Excel_SimpleTable(titles=[], lines=[[]], SheetName="feuille", titlesStyles=[]):
+ """Export simple type 'CSV': 1ere ligne en gras, le reste tel quel"""
+ # XXX devrait maintenant utiliser ScoExcelSheet
+ wb = Workbook()
+ ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
+ if not titlesStyles:
+ style = Excel_MakeStyle(bold=True)
+ titlesStyles = [style] * len(titles)
+ # ligne de titres
+ col = 0
+ for it in titles:
+ ws0.write(0, col, it.decode(scu.SCO_ENCODING), titlesStyles[col])
+ col += 1
+ # suite
+ default_style = Excel_MakeStyle()
+ text_style = Excel_MakeStyle()
+ text_style.num_format_str = "@"
+ li = 1
+ for l in lines:
+ col = 0
+ for it in l:
+ cell_style = default_style
+ # safety net: allow only str, int and float
+ if isinstance(it, LongType): # XXX
+ it = int(it) # assume all ScoDoc longs fits in int !
+ elif type(it) not in (IntType, FloatType): # XXX A REVOIR
+ it = str(it).decode(scu.SCO_ENCODING)
+ cell_style = text_style
+ ws0.write(li, col, it, cell_style)
+ col += 1
+ li += 1
+ #
+ return wb.savetostr()
+
+
+def Excel_feuille_saisie(E, titreannee, description, lines):
+ """Genere feuille excel pour saisie des notes.
+ E: evaluation (dict)
+ lines: liste de tuples
+ (etudid, nom, prenom, etat, groupe, val, explanation)
+ """
+ SheetName = "Saisie notes"
+ wb = Workbook()
+ ws0 = wb.add_sheet(SheetName.decode(scu.SCO_ENCODING))
+ # ajuste largeurs colonnes (unite inconnue, empirique)
+ ws0.col(0).width = 400 # codes
+ ws0.col(1).width = 6000 # noms
+ ws0.col(2).width = 4000 # prenoms
+ ws0.col(3).width = 6000 # groupes
+ ws0.col(4).width = 3000 # notes
+ ws0.col(5).width = 13000 # remarques
+ # styles
+ style_titres = XFStyle()
+ font0 = Font()
+ font0.bold = True
+ font0.name = "Arial"
+ font0.bold = True
+ font0.height = 14 * 0x14
+ style_titres.font = font0
+
+ style_expl = XFStyle()
+ font_expl = Font()
+ font_expl.name = "Arial"
+ font_expl.italic = True
+ font0.height = 12 * 0x14
+ font_expl.colour_index = 0x0A # rouge, voir exemple format.py
+ style_expl.font = font_expl
+
+ topborders = Borders()
+ topborders.top = 1
+ topleftborders = Borders()
+ topleftborders.top = 1
+ topleftborders.left = 1
+ rightborder = Borders()
+ rightborder.right = 1
+
+ style_ro = XFStyle() # cells read-only
+ font_ro = Font()
+ font_ro.name = "Arial"
+ font_ro.colour_index = COLOR_CODES["mauve"]
+ style_ro.font = font_ro
+ style_ro.borders = rightborder
+
+ style_dem = XFStyle() # cells read-only
+ font_dem = Font()
+ font_dem.name = "Arial"
+ font_dem.colour_index = COLOR_CODES["marron"]
+ style_dem.font = font_dem
+ style_dem.borders = topborders
+
+ style = XFStyle()
+ font1 = Font()
+ font1.name = "Arial"
+ font1.height = 12 * 0x14
+ style.font = font1
+
+ style_nom = XFStyle() # style pour nom, prenom, groupe
+ style_nom.font = font1
+ style_nom.borders = topborders
+
+ style_notes = XFStyle()
+ font2 = Font()
+ font2.name = "Arial"
+ font2.bold = True
+ style_notes.font = font2
+ style_notes.num_format_str = "general"
+ style_notes.pattern = Pattern() # fond jaune
+ style_notes.pattern.pattern = Pattern.SOLID_PATTERN
+ style_notes.pattern.pattern_fore_colour = COLOR_CODES["lightyellow"]
+ style_notes.borders = topborders
+
+ style_comment = XFStyle()
+ font_comment = Font()
+ font_comment.name = "Arial"
+ font_comment.height = 9 * 0x14
+ font_comment.colour_index = COLOR_CODES["blue"]
+ style_comment.font = font_comment
+ style_comment.borders = topborders
+
+ # ligne de titres
+ li = 0
+ ws0.write(
+ li, 0, u"Feuille saisie note (à enregistrer au format excel)", style_titres
+ )
+ li += 1
+ ws0.write(li, 0, u"Saisir les notes dans la colonne E (cases jaunes)", style_expl)
+ li += 1
+ ws0.write(li, 0, u"Ne pas modifier les cases en mauve !", style_expl)
+ li += 1
+ # Nom du semestre
+ ws0.write(
+ li, 0, scu.unescape_html(titreannee).decode(scu.SCO_ENCODING), style_titres
+ )
+ li += 1
+ # description evaluation
+ ws0.write(
+ li, 0, scu.unescape_html(description).decode(scu.SCO_ENCODING), style_titres
+ )
+ li += 1
+ ws0.write(
+ li, 0, u"Evaluation du %s (coef. %g)" % (E["jour"], E["coefficient"]), style
+ )
+ li += 1
+ li += 1 # ligne blanche
+ # code et titres colonnes
+ ws0.write(li, 0, u"!%s" % E["evaluation_id"], style_ro)
+ ws0.write(li, 1, u"Nom", style_titres)
+ ws0.write(li, 2, u"Prénom", style_titres)
+ ws0.write(li, 3, u"Groupe", style_titres)
+ ws0.write(li, 4, u"Note sur %g" % E["note_max"], style_titres)
+ ws0.write(li, 5, u"Remarque", style_titres)
+ # etudiants
+ for line in lines:
+ li += 1
+ st = style_nom
+ ws0.write(li, 0, ("!" + line[0]).decode(scu.SCO_ENCODING), style_ro) # code
+ if line[3] != "I":
+ st = style_dem
+ if line[3] == "D": # demissionnaire
+ s = "DEM"
+ else:
+ s = line[3] # etat autre
+ else:
+ s = line[4] # groupes TD/TP/...
+ ws0.write(li, 1, line[1].decode(scu.SCO_ENCODING), st)
+ ws0.write(li, 2, line[2].decode(scu.SCO_ENCODING), st)
+ ws0.write(li, 3, s.decode(scu.SCO_ENCODING), st)
+ try:
+ val = float(line[5])
+ except:
+ val = line[5].decode(scu.SCO_ENCODING)
+ ws0.write(li, 4, val, style_notes) # note
+ ws0.write(li, 5, line[6].decode(scu.SCO_ENCODING), style_comment) # comment
+ # explication en bas
+ li += 2
+ ws0.write(li, 1, u"Code notes", style_titres)
+ ws0.write(li + 1, 1, u"ABS", style_expl)
+ ws0.write(li + 1, 2, u"absent (0)", style_expl)
+ ws0.write(li + 2, 1, u"EXC", style_expl)
+ ws0.write(li + 2, 2, u"pas prise en compte", style_expl)
+ ws0.write(li + 3, 1, u"ATT", style_expl)
+ ws0.write(li + 3, 2, u"en attente", style_expl)
+ ws0.write(li + 4, 1, u"SUPR", style_expl)
+ ws0.write(li + 4, 2, u"pour supprimer note déjà entrée", style_expl)
+ ws0.write(li + 5, 1, u"", style_expl)
+ ws0.write(li + 5, 2, u"cellule vide -> note non modifiée", style_expl)
+ return wb.savetostr()
+
+
+def Excel_to_list(data, convert_to_string=str): # we may need 'encoding' argument ?
+ """returns list of list
+ convert_to_string is a conversion function applied to all non-string values (ie numbers)
+ """
+ try:
+ P = parse_xls("", scu.SCO_ENCODING, doc=data)
+ except:
+ log("Excel_to_list: failure to import document")
+ open("/tmp/last_scodoc_import_failure.xls", "w").write(data)
+ raise ScoValueError(
+ "Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !"
+ )
+
+ diag = [] # liste de chaines pour former message d'erreur
+ # n'utilise que la première feuille
+ if len(P) < 1:
+ diag.append("Aucune feuille trouvée dans le classeur !")
+ return diag, None
+ if len(P) > 1:
+ diag.append("Attention: n'utilise que la première feuille du classeur !")
+ # fill matrix
+ sheet_name, values = P[0]
+ sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace")
+ if not values:
+ diag.append("Aucune valeur trouvée dans le classeur !")
+ return diag, None
+ indexes = list(values.keys())
+ # search numbers of rows and cols
+ rows = [x[0] for x in indexes]
+ cols = [x[1] for x in indexes]
+ nbcols = max(cols) + 1
+ nbrows = max(rows) + 1
+ M = []
+ for _ in range(nbrows):
+ M.append([""] * nbcols)
+
+ for row_idx, col_idx in indexes:
+ v = values[(row_idx, col_idx)]
+ if isinstance(v, six.text_type):
+ v = v.encode(scu.SCO_ENCODING, "backslashreplace")
+ elif convert_to_string:
+ v = convert_to_string(v)
+ M[row_idx][col_idx] = v
+ diag.append('Feuille "%s", %d lignes' % (sheet_name, len(M)))
+ # diag.append(str(M))
+ #
+ return diag, M
+
+# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
+# font, border, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
+
+
+def __make_cell(ws, value: any = u"", style=None):
+ """Contruit/retourne une cellule en spécifiant contenu et style.
+
+ ws -- La feuille où sera intégrée la cellule
+ value -- le contenu de la cellule (texte)
+ style -- le style de la cellule
+ """
+ cell = WriteOnlyCell(ws, value)
+ if "font" in style:
+ cell.font = style["font"]
+ if "border" in style:
+ cell.border = style["border"]
+ return cell
+
+
+def excel_feuille_listeappel(sem, groupname, lines, partitions=None, with_codes=False, with_paiement=False,
+ server_name=None):
+ """generation feuille appel"""
+ if partitions is None:
+ partitions = []
+ formsemestre_id = sem["formsemestre_id"]
+ sheet_name = "Liste " + groupname
+ wb = Workbook(write_only=True)
+ ws = wb.create_sheet(title=sheet_name)
+ ws.column_dimensions["A"].width = 3
+ ws.column_dimensions["B"].width = 35
+ ws.column_dimensions["C"].width = 12
+
+ font1 = Font(name="Arial", size=11)
+ font1i = Font(name="Arial", size=10, italic=True)
+ font1b = Font(name="Arial", size=11, bold=True)
+
+ side_thin = Side(border_style="thin", color="FF000000")
+
+ border_tbl = Border(top=side_thin, bottom=side_thin, left=side_thin)
+ border_tblr = Border(
+ top=side_thin, bottom=side_thin, left=side_thin, right=side_thin
+ )
+
+ style1i = {
+ "font": font1i,
+ }
+
+ style1b = {
+ "font": font1,
+ "border": border_tbl,
+ }
+
+ style2 = {
+ "font": Font(name="Arial", size=14),
+ }
+
+ style2b = {
+ "font": font1i,
+ "border": border_tblr,
+ }
+
+ style2t3 = {
+ "border": border_tblr,
+ }
+
+ style2t3bold = {
+ "font": font1b,
+ "border": border_tblr,
+ }
+
+ style3 = {
+ "font": Font(name="Arial", bold=True, size=14),
+ }
+
+ nb_weeks = 4 # nombre de colonnes pour remplir absences
+
+ # ligne 1
+ title = "%s %s (%s - %s)" % (
+ sco_preferences.get_preference("DeptName", formsemestre_id),
+ notesdb.unquote(sem["titre_num"]),
+ sem["date_debut"],
+ sem["date_fin"],
+ )
+
+ cell_2 = __make_cell(ws, title, style2)
+ ws.append([None, cell_2])
+
+ # ligne 2
+ cell_2 = __make_cell(ws, u"Discipline :", style2)
+ ws.append([None, cell_2])
+
+ # ligne 3
+ cell_2 = __make_cell(ws, u"Enseignant :", style2)
+ cell_6 = __make_cell(ws, ("Groupe %s" % groupname), style3)
+ ws.append([None, cell_2, None, None, None, None, cell_6])
+
+ # ligne 4: Avertissement pour ne pas confondre avec listes notes
+ cell_2 = __make_cell(ws, u"Ne pas utiliser cette feuille pour saisir les notes !", style1i)
+ ws.append([None, None, cell_2])
+
+ ws.append([None])
+ ws.append([None])
+
+ # ligne 7: Entête (contruction dans une liste cells)
+ cells = [None] # passe la première colonne
+ cell_2 = __make_cell(ws, u"Nom", style3)
+ cells.append(cell_2)
+ for partition in partitions:
+ cells.append(__make_cell(ws, partition["partition_name"], style3))
+ if with_codes:
+ cells.append(__make_cell(ws, u"etudid", style3))
+ cells.append(__make_cell(ws, u"code_nip", style3))
+ cells.append(__make_cell(ws, u"code_ine", style3))
+ for i in range(nb_weeks):
+ cells.append(__make_cell(ws, "", style2b))
+ ws.append(cells)
+
+ n = 0
+ # pour chaque étudiant
+ for t in lines:
+ n += 1
+ nomprenom = (
+ t["civilite_str"]
+ + " "
+ + t["nom"]
+ + " "
+ + scu.strcapitalize(scu.strlower(t["prenom"]))
+ )
+ style_nom = style2t3
+ if with_paiement:
+ paie = t.get("paiementinscription", None)
+ if paie is None:
+ nomprenom += " (inscription ?)"
+ style_nom = style2t3bold
+ elif not paie:
+ nomprenom += " (non paiement)"
+ style_nom = style2t3bold
+ cell_1 = __make_cell(ws, n, style1b)
+ cell_2 = __make_cell(ws, nomprenom, style_nom)
+ cells = [cell_1, cell_2]
+
+ for partition in partitions:
+ if partition["partition_name"]:
+ cells.append(
+ __make_cell(ws, t.get(partition["partition_id"], u""), style2t3)
+ )
+ if with_codes:
+ cells.append(__make_cell(ws, t["etudid"], style2t3))
+ code_nip = t.get("code_nip", u"")
+ cells.append(__make_cell(ws, code_nip, style2t3))
+ code_ine = t.get("code_ine", u"")
+ cells.append(__make_cell(ws, code_ine, style2t3))
+ cells.append(__make_cell(ws, t.get("etath", ""), style2b))
+ for i in range(1, nb_weeks):
+ cells.append(__make_cell(ws, style=style2t3))
+ # ws0.row(li).height = 850 # sans effet ?
+ # (openpyxl: en mode optimisé, les hauteurs de lignes doivent être spécifiées avant toutes les cellules)
+ ws.append(cells)
+
+ ws.append([None])
+
+ # bas de page (date, serveur)
+ dt = time.strftime("%d/%m/%Y à %Hh%M")
+ if server_name:
+ dt += " sur " + server_name
+ cell_2 = __make_cell(ws, ("Liste éditée le " + dt), style1i)
+ ws.append([None, cell_2])
+
+ # construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
+ with NamedTemporaryFile() as tmp:
+ wb.save(tmp.name)
+ tmp.seek(0)
+ return tmp.read()
diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py
index 033cb0e41..708038b4d 100644
--- a/app/scodoc/sco_groups_view.py
+++ b/app/scodoc/sco_groups_view.py
@@ -1,1002 +1,996 @@
-# -*- 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
-#
-##############################################################################
-
-"""Affichage étudiants d'un ou plusieurs groupes
- sous forme: de liste html (table exportable), de trombinoscope (exportable en pdf)
-"""
-
-# Re-ecriture en 2014 (re-organisation de l'interface, modernisation du code)
-import datetime
-import cgi
-import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
-import time
-import collections
-import operator
-
-from flask import url_for, g
-
-import app.scodoc.sco_utils as scu
-from app.scodoc import html_sco_header
-from app.scodoc import sco_abs
-from app.scodoc import sco_excel
-from app.scodoc import sco_formsemestre
-from app.scodoc import sco_groups
-from app.scodoc import sco_moduleimpl
-from app.scodoc import sco_parcours_dut
-from app.scodoc import sco_portal_apogee
-from app.scodoc import sco_preferences
-from app.scodoc import sco_etud
-from app.scodoc.gen_tables import GenTable
-from app.scodoc.sco_exceptions import ScoValueError
-from app.scodoc.sco_permissions import Permission
-from six.moves import range
-
-JAVASCRIPTS = html_sco_header.BOOTSTRAP_MULTISELECT_JS + [
- "js/etud_info.js",
- "js/groups_view.js",
-]
-
-CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
-
-
-def groups_view(
- context,
- group_ids=[],
- format="html",
- REQUEST=None,
- # Options pour listes:
- with_codes=0,
- etat=None,
- with_paiement=0, # si vrai, ajoute colonnes infos paiement droits et finalisation inscription (lent car interrogation portail)
- with_archives=0, # ajoute colonne avec noms fichiers archivés
- with_annotations=0,
- formsemestre_id=None, # utilise si aucun groupe selectionné
-):
- """Affichage des étudiants des groupes indiqués
- group_ids: liste de group_id
- format: csv, json, xml, xls, allxls, xlsappel, moodlecsv, pdf
- """
- # Informations sur les groupes à afficher:
- groups_infos = DisplayedGroupsInfos(
- context,
- group_ids,
- formsemestre_id=formsemestre_id,
- etat=etat,
- REQUEST=REQUEST,
- select_all_when_unspecified=True,
- )
- # Formats spéciaux: download direct
- if format != "html":
- return groups_table(
- context=context,
- groups_infos=groups_infos,
- format=format,
- REQUEST=REQUEST,
- with_codes=with_codes,
- etat=etat,
- with_paiement=with_paiement,
- with_archives=with_archives,
- with_annotations=with_annotations,
- )
-
- H = [
- html_sco_header.sco_header(
- javascripts=JAVASCRIPTS,
- cssstyles=CSSSTYLES,
- init_qtip=True,
- )
- ]
- # Menu choix groupe
- H.append("""
hello
', - "hello
', + "