forked from ScoDoc/ScoDoc
refacto
This commit is contained in:
parent
342bff3b56
commit
c536336017
@ -33,7 +33,6 @@ import time
|
||||
from flask import flash, abort
|
||||
|
||||
from app.but.cursus_but import EtudCursusBUT
|
||||
from app.but.prepajury_cells import Cell
|
||||
from app.but.prepajury_desc import ParcoursDesc
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_but import ResultatsSemestreBUT
|
||||
@ -46,7 +45,7 @@ from app.models import (
|
||||
ApcCompetence,
|
||||
)
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_excel import ScoExcelBook, ScoExcelSheet
|
||||
from app.but.prepajury_xl import ScoExcelBook
|
||||
|
||||
|
||||
class _Bilan:
|
||||
@ -220,8 +219,8 @@ def feuille_preparation_jury_but(formsemestre_id):
|
||||
abort(404)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
# res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
|
||||
breakpoint()
|
||||
"""Feuille excel pour préparation des jurys adaptée pour le BUT."""
|
||||
breakpoint()
|
||||
compilation = _Compilation(formsemestre)
|
||||
compilation.computes_decision()
|
||||
filename = scu.sanitize_filename(
|
||||
|
@ -1,3 +1,6 @@
|
||||
import openpyxl.cell
|
||||
from openpyxl.cell import Cell
|
||||
|
||||
from app.but.prepajury_xl import (
|
||||
ScoExcelSheet,
|
||||
)
|
||||
@ -11,14 +14,29 @@ from app.but.prepajury_xl_format import (
|
||||
)
|
||||
|
||||
base_signature = (
|
||||
FMT.FONT_NAME.write(SCO_FONTNAME.FONTNAME_CALIBRI)
|
||||
+ FMT.FONT_SIZE.write(SCO_FONTSIZE.FONTSIZE_10)
|
||||
+ FMT.ALIGNMENT_HALIGN.write(SCO_HALIGN.HALIGN_CENTER)
|
||||
+ FMT.ALIGNEMENT_VALIGN.write(SCO_VALIGN.VALIGN_CENTER)
|
||||
FMT.FONT_NAME.set(SCO_FONTNAME.FONTNAME_CALIBRI)
|
||||
+ FMT.FONT_SIZE.set(SCO_FONTSIZE.FONTSIZE_13)
|
||||
+ FMT.ALIGNMENT_HALIGN.set(SCO_HALIGN.HALIGN_CENTER)
|
||||
+ FMT.ALIGNMENT_VALIGN.set(SCO_VALIGN.VALIGN_CENTER)
|
||||
)
|
||||
|
||||
|
||||
class Cell:
|
||||
def set_cell(
|
||||
ws: Cell,
|
||||
row: int,
|
||||
column: int,
|
||||
text: str = "",
|
||||
from_signature: int = 0,
|
||||
composition: list = [],
|
||||
):
|
||||
cell = ws.cell(row, column)
|
||||
cell.value = text
|
||||
sign = FMT.compose(composition, from_signature)
|
||||
style: Sco_Style = FMT.style(sign)
|
||||
style.apply(cell)
|
||||
|
||||
|
||||
class Sco_Cell:
|
||||
def __init__(
|
||||
self, text: str = None, signature: int = base_signature, comment: str = None
|
||||
):
|
||||
@ -26,11 +44,16 @@ class Cell:
|
||||
self.signature = signature or base_signature
|
||||
self.comment = comment
|
||||
|
||||
def format(self, FMT: FMT, value=None):
|
||||
self.signature = FMT.composante.write(self.signature, value)
|
||||
def format(self, value=None):
|
||||
self.signature = FMT.ALL.composante.write(self.signature, value)
|
||||
|
||||
def copy(self, cell: openpyxl.cell.Cell):
|
||||
cell.value = self.text
|
||||
style: Sco_Style = FMT.style(self.signature)
|
||||
style.apply(cell)
|
||||
|
||||
def make_cell(self, worksheet: ScoExcelSheet):
|
||||
cell = worksheet.make_cell(self.text or "")
|
||||
style: Sco_Style = FMT.ALL.get_style(self.signature)
|
||||
style: Sco_Style = FMT.style(self.signature)
|
||||
style.apply(cell)
|
||||
return cell
|
||||
|
@ -1,10 +1,18 @@
|
||||
from openpyxl.styles.colors import BLACK
|
||||
import openpyxl
|
||||
from openpyxl.worksheet.worksheet import Worksheet
|
||||
|
||||
from app.but.prepajury_cells import Cell, base_style
|
||||
from app.but.prepajury_xl import Sco_Style
|
||||
from app.but.prepajury_xl_format import SCO_COLORS
|
||||
from app.but.prepajury_cells import Sco_Cell, base_signature, set_cell
|
||||
from app.but.prepajury_xl_format import (
|
||||
SCO_COLORS,
|
||||
FMT,
|
||||
SCO_FONTSIZE,
|
||||
SCO_VALIGN,
|
||||
SCO_HALIGN,
|
||||
SCO_BORDERTHICKNESS,
|
||||
Sco_Style,
|
||||
)
|
||||
from app.models import ApcCompetence, ApcParcours
|
||||
from app.scodoc.sco_excel import ScoExcelBook, ScoExcelSheet
|
||||
from app.but.prepajury_xl import ScoExcelBook, ScoExcelSheet
|
||||
|
||||
|
||||
def parite(semestre_idx):
|
||||
@ -33,16 +41,21 @@ header_colors = {
|
||||
|
||||
class _Header:
|
||||
def __init__(self):
|
||||
self.lines: list[list[Cell]] = []
|
||||
self.lines: list[list[Sco_Cell]] = []
|
||||
self.presets = [None, None, None, None]
|
||||
self.styles = [None, None, None, None]
|
||||
self.signatures = [
|
||||
base_signature,
|
||||
base_signature,
|
||||
base_signature,
|
||||
base_signature,
|
||||
]
|
||||
|
||||
def setLabelOnce(self, rang: int, label: str):
|
||||
self.presets[rang] = label
|
||||
|
||||
def setStyles(self, rangs: list[int], style: Sco_Style):
|
||||
def setSignatures(self, rangs: list[int], signature: int):
|
||||
for rang in rangs:
|
||||
self.styles[rang] = style
|
||||
self.signatures[rang] = signature
|
||||
|
||||
def add_column(
|
||||
self,
|
||||
@ -54,9 +67,9 @@ class _Header:
|
||||
):
|
||||
if labels is None:
|
||||
labels = [label0, label1, label2, label3]
|
||||
cells: list(Cell) = [
|
||||
Cell(preset or label, style=style)
|
||||
for label, preset, style in zip(labels, self.presets, self.styles)
|
||||
cells: list(Sco_Cell) = [
|
||||
Sco_Cell(preset or label, signature=signature)
|
||||
for label, preset, signature in zip(labels, self.presets, self.signatures)
|
||||
]
|
||||
self.presets = [None, None, None, None]
|
||||
self.lines.append(cells)
|
||||
@ -65,19 +78,13 @@ class _Header:
|
||||
self.lines.extend(header.lines)
|
||||
|
||||
def write(self, worksheet: ScoExcelSheet):
|
||||
row1 = []
|
||||
row2 = []
|
||||
row3 = []
|
||||
row4 = []
|
||||
column = 1
|
||||
for items in self.lines:
|
||||
row1.append(items[0].make_cell(worksheet))
|
||||
row2.append(items[1].make_cell(worksheet))
|
||||
row3.append(items[2].make_cell(worksheet))
|
||||
row4.append(items[3].make_cell(worksheet))
|
||||
worksheet.append_row(row1)
|
||||
worksheet.append_row(row2)
|
||||
worksheet.append_row(row3)
|
||||
worksheet.append_row(row4)
|
||||
items[0].copy(worksheet.ws.cell(1, column))
|
||||
items[1].copy(worksheet.ws.cell(2, column))
|
||||
items[2].copy(worksheet.ws.cell(3, column))
|
||||
items[3].copy(worksheet.ws.cell(4, column))
|
||||
column += 1
|
||||
|
||||
|
||||
class NiveauDesc:
|
||||
@ -91,17 +98,17 @@ class NiveauDesc:
|
||||
self.ue[parite(scodocUe.semestre_idx)] = scodocUe
|
||||
|
||||
def generate_header(self, header):
|
||||
rcue_style = Sco_Style(
|
||||
fromStyle=base_style,
|
||||
bgcolor=header_colors[self.fromScodoc.annee]["RCUE"],
|
||||
rcue_signature = FMT.FILL_BGCOLOR.write(
|
||||
value=header_colors[self.fromScodoc.annee]["RCUE"].value,
|
||||
signature=base_signature,
|
||||
)
|
||||
ue_style = Sco_Style(
|
||||
fromStyle=base_style,
|
||||
bgcolor=header_colors[self.fromScodoc.annee]["UE"],
|
||||
ue_signature = FMT.FILL_BGCOLOR.write(
|
||||
value=header_colors[self.fromScodoc.annee]["UE"].value,
|
||||
signature=base_signature,
|
||||
)
|
||||
header.setLabelOnce(1, self.fromScodoc.competence.titre)
|
||||
header.setStyles([1], rcue_style)
|
||||
header.setStyles([2, 3], ue_style)
|
||||
header.setSignatures([1], rcue_signature)
|
||||
header.setSignatures([2, 3], ue_signature)
|
||||
for ue in self.ue:
|
||||
if ue is None:
|
||||
header.setLabelOnce(2, "XXX")
|
||||
@ -111,7 +118,7 @@ class NiveauDesc:
|
||||
header.setLabelOnce(2, ue.acronyme)
|
||||
header.add_column(label3="Note")
|
||||
header.add_column(label3="Res.")
|
||||
header.setStyles([1, 2, 3], rcue_style)
|
||||
header.setSignatures([1, 2, 3], rcue_signature)
|
||||
header.add_column(label2="RCUE", label3="Note")
|
||||
header.add_column(label3="Res.")
|
||||
return header
|
||||
@ -160,24 +167,67 @@ class AnneeDesc:
|
||||
for _ in range(3):
|
||||
header.add_column()
|
||||
|
||||
def generate_header(self, header):
|
||||
but_style = Sco_Style(
|
||||
fromStyle=base_style, bgcolor=header_colors[self.codeAnnee]["BUT"]
|
||||
def generate_header(self, ws: Worksheet, column: int, codeAnnee: str):
|
||||
start = column
|
||||
but_signature = FMT.FILL_BGCOLOR.write(
|
||||
signature=base_signature, value=header_colors[self.codeAnnee]["BUT"].value
|
||||
)
|
||||
header.setStyles([0], but_style)
|
||||
for niveau in self.niveaux.values():
|
||||
niveau.generate_header(header)
|
||||
for i in range(len(self.niveaux), 6):
|
||||
self.generate_blank_niveau(header)
|
||||
header.add_column(label1="Année", label2="Nb", label3="RCUE")
|
||||
header.add_column(label3="Rés.")
|
||||
but_style = FMT.style(but_signature)
|
||||
set_cell(
|
||||
ws,
|
||||
1,
|
||||
column,
|
||||
text=codeAnnee,
|
||||
from_signature=but_signature,
|
||||
composition=[
|
||||
(FMT.BORDER_LEFT_COLOR, SCO_COLORS.BLACK.value),
|
||||
(FMT.BORDER_LEFT_STYLE, SCO_BORDERTHICKNESS.BORDER_MEDIUM.value),
|
||||
],
|
||||
)
|
||||
ws.cell(1, column).value = codeAnnee
|
||||
but_style.apply(ws.cell(1, column))
|
||||
# for niveau in self.niveaux.values():
|
||||
# column = niveau.generate_header(ws, column)
|
||||
# for i in range(len(self.niveaux), 6):
|
||||
# column = self.generate_blank_niveau(ws, column)
|
||||
ws.cell(2, column).value = "Année"
|
||||
# cell_format(ws.cell(2, column), but_signature, [(FMT.FONT_BOLD, True)])
|
||||
ws.cell(3, column).value = "Nb"
|
||||
ws.cell(4, column).value = "RCUE"
|
||||
column += 1
|
||||
ws.cell(2, column).value = "Année"
|
||||
ws.cell(4, column).value = "Moy."
|
||||
column += 1
|
||||
if self.codeAnnee == "BUT2":
|
||||
header.add_column(label1="DUT", label3="Rés.")
|
||||
ws.cell(3, column).value = "DUT"
|
||||
ws.cell(3, column).value = "Rés."
|
||||
if self.codeAnnee == "BUT3":
|
||||
header.add_column(label1="BUT", label3="Rés.")
|
||||
ws.cell(3, column).value = "BUT"
|
||||
ws.cell(3, column).value = "Rés."
|
||||
# ws.merge_cells(start_column=start, end_column=column, start_row=1, end_row=1)
|
||||
return column
|
||||
|
||||
|
||||
class ParcoursDesc:
|
||||
signature_header = FMT.compose(
|
||||
[
|
||||
(FMT.FILL_BGCOLOR, SCO_COLORS.LIGHT_YELLOW.value),
|
||||
(FMT.FONT_BOLD, True),
|
||||
(FMT.FONT_SIZE, SCO_FONTSIZE.FONTSIZE_13.value),
|
||||
(FMT.ALIGNMENT_VALIGN, SCO_VALIGN.VALIGN_CENTER.value),
|
||||
(FMT.ALIGNMENT_HALIGN, SCO_HALIGN.HALIGN_CENTER.value),
|
||||
(FMT.BORDER_LEFT_STYLE, SCO_BORDERTHICKNESS.BORDER_THIN.value),
|
||||
(FMT.BORDER_RIGHT_STYLE, SCO_BORDERTHICKNESS.BORDER_THIN.value),
|
||||
(FMT.BORDER_TOP_STYLE, SCO_BORDERTHICKNESS.BORDER_THIN.value),
|
||||
(FMT.BORDER_BOTTOM_STYLE, SCO_BORDERTHICKNESS.BORDER_THIN.value),
|
||||
(FMT.BORDER_LEFT_COLOR, SCO_COLORS.BLACK.value),
|
||||
(FMT.BORDER_RIGHT_COLOR, SCO_COLORS.BLACK.value),
|
||||
(FMT.BORDER_TOP_COLOR, SCO_COLORS.BLACK.value),
|
||||
(FMT.BORDER_RIGHT_COLOR, SCO_COLORS.BLACK.value),
|
||||
],
|
||||
base_signature,
|
||||
)
|
||||
|
||||
def __init__(self, formation, scodocParcour: ApcParcours = None):
|
||||
self.fromScodoc: ApcParcours = scodocParcour # None pour le tronc commun 'TC'
|
||||
self.etudiants = []
|
||||
@ -221,16 +271,60 @@ class ParcoursDesc:
|
||||
cells3.append(worksheet.make_cell(val3))
|
||||
cells4.append(worksheet.make_cell(val4))
|
||||
|
||||
def handle_description(self, header, description, level):
|
||||
def handle_description(
|
||||
self, ws: Worksheet, description: tuple, row: int, column: int
|
||||
) -> int:
|
||||
title, content_list = description
|
||||
header.setLabelOnce(level, title)
|
||||
style = FMT.style(self.signature_header)
|
||||
ws.cell(row, column).value = title
|
||||
style.apply(ws.cell(row, column))
|
||||
if content_list is None:
|
||||
header.add_column()
|
||||
ws.merge_cells(
|
||||
start_row=row, end_row=4, start_column=column, end_column=column
|
||||
)
|
||||
column += 1
|
||||
else:
|
||||
first = column
|
||||
for content in content_list:
|
||||
self.handle_description(header, content, level + 1)
|
||||
column = self.handle_description(ws, content, row + 1, column)
|
||||
if column - first > 1:
|
||||
ws.merge_cells(
|
||||
start_row=row,
|
||||
end_row=row,
|
||||
start_column=first,
|
||||
end_column=column - 1,
|
||||
)
|
||||
# left_medium = FMT.compose(
|
||||
# self.signature_header,
|
||||
# [
|
||||
# (FMT.BORDER_LEFT_STYLE, SCO_BORDERTHICKNESS.BORDER_MEDIUM.value),
|
||||
# (FMT.BORDER_LEFT_COLOR, SCO_COLORS.BLACK.value),
|
||||
# ],
|
||||
# )
|
||||
# right_medium = FMT.compose(
|
||||
# self.signature_header,
|
||||
# [
|
||||
# (FMT.BORDER_RIGHT_STYLE, SCO_BORDERTHICKNESS.BORDER_MEDIUM.value),
|
||||
# (FMT.BORDER_RIGHT_COLOR, SCO_COLORS.BLACK.value),
|
||||
# ],
|
||||
# )
|
||||
# top_medium = FMT.compose(
|
||||
# self.signature_header,
|
||||
# [
|
||||
# (FMT.BORDER_TOP_STYLE, SCO_BORDERTHICKNESS.BORDER_MEDIUM.value),
|
||||
# (FMT.BORDER_TOP_COLOR, SCO_COLORS.BLACK.value),
|
||||
# ],
|
||||
# )
|
||||
# FMT.get_style(FMT.ALL, left_medium).apply(ws.cell(1, 1))
|
||||
# FMT.get_style(FMT.ALL, top_medium).apply(ws.cell(1, 1))
|
||||
# FMT.get_style(FMT.ALL, right_medium).apply(ws.cell(1, 1))
|
||||
# FMT.get_style(FMT.ALL, left_medium).apply(ws.cell(2, 1))
|
||||
# FMT.get_style(FMT.ALL, right_medium).apply(ws.cell(2, column - 1))
|
||||
# FMT.get_style(FMT.ALL, right_medium).apply(ws.cell(3, column - 1))
|
||||
# FMT.get_style(FMT.ALL, right_medium).apply(ws.cell(4, column - 1))
|
||||
return column
|
||||
|
||||
def generate_etudiant_header(self, header):
|
||||
def generate_etudiant_header(self, ws: Worksheet) -> int:
|
||||
titles = (
|
||||
"ETUDIANT",
|
||||
[
|
||||
@ -250,14 +344,13 @@ class ParcoursDesc:
|
||||
),
|
||||
],
|
||||
)
|
||||
self.handle_description(header, titles, 0)
|
||||
column = self.handle_description(ws, titles, 1, 1)
|
||||
return column
|
||||
|
||||
def generate_header(self, header) -> _Header:
|
||||
self.generate_etudiant_header(header)
|
||||
def generate_header(self, ws: Worksheet):
|
||||
column: int = self.generate_etudiant_header(ws)
|
||||
for codeAnnee in listeAnnees:
|
||||
header.setLabelOnce(0, codeAnnee)
|
||||
self.annees[codeAnnee].generate_header(header)
|
||||
return header
|
||||
column = self.annees[codeAnnee].generate_header(ws, column, codeAnnee)
|
||||
|
||||
def generate(self, workbook: ScoExcelBook):
|
||||
header = _Header()
|
||||
@ -266,4 +359,4 @@ class ParcoursDesc:
|
||||
else:
|
||||
sheet_name = "TC"
|
||||
worksheet: ScoExcelSheet = workbook.create_sheet(sheet_name)
|
||||
self.generate_header(header).write(worksheet)
|
||||
self.generate_header(worksheet.ws)
|
||||
|
@ -87,7 +87,7 @@ class ScoExcelBook:
|
||||
|
||||
def __init__(self):
|
||||
self.sheets = [] # list of sheets
|
||||
self.wb = Workbook(write_only=True)
|
||||
self.wb = Workbook()
|
||||
|
||||
def create_sheet(self, sheet_name="feuille", default_style=None):
|
||||
"""Crée une nouvelle feuille dans ce classeur
|
||||
@ -208,21 +208,9 @@ class ScoExcelSheet:
|
||||
# création de la cellule
|
||||
cell = WriteOnlyCell(self.ws, value)
|
||||
|
||||
# recopie des styles
|
||||
if style is None:
|
||||
style = self.default_style
|
||||
if "font" in style:
|
||||
cell.font = style["font"]
|
||||
if "alignment" in style:
|
||||
cell.alignment = style["alignment"].get_openxl()
|
||||
if "border" in style:
|
||||
cell.border = style["border"].get_openxl()
|
||||
if "number_format" in style:
|
||||
cell.number_format = style["number_format"]
|
||||
if "fill" in style:
|
||||
cell.fill = style["fill"]
|
||||
if "alignment" in style:
|
||||
cell.alignment = style["alignment"].get_openxl()
|
||||
if style is not None:
|
||||
style.apply(cell)
|
||||
|
||||
if not comment is None:
|
||||
cell.comment = Comment(comment, "scodoc")
|
||||
lines = comment.splitlines()
|
||||
|
@ -2,9 +2,11 @@ import abc
|
||||
from enum import Enum
|
||||
|
||||
import openpyxl.styles
|
||||
from openpyxl.styles import Side, Border, Font, PatternFill
|
||||
from openpyxl.styles import Side, Border, Font, PatternFill, Alignment
|
||||
from openpyxl.styles.numbers import FORMAT_GENERAL, FORMAT_NUMBER_00
|
||||
|
||||
# Formatting Enums
|
||||
|
||||
|
||||
class SCO_COLORS(Enum):
|
||||
def __new__(cls, value, argb):
|
||||
@ -13,6 +15,7 @@ class SCO_COLORS(Enum):
|
||||
obj.argb = argb
|
||||
return obj
|
||||
|
||||
NONE = (0, None)
|
||||
BLACK = (1, "FF000000")
|
||||
WHITE = (2, "FFFFFFFF")
|
||||
RED = (3, "FFFF0000")
|
||||
@ -39,11 +42,11 @@ class SCO_BORDERTHICKNESS(Enum):
|
||||
obj.width = width
|
||||
return obj
|
||||
|
||||
BORDER_NONE = (1, None)
|
||||
BORDER_HAIR = (2, "hair")
|
||||
BORDER_THIN = (3, "thin")
|
||||
BORDER_MEDIUM = (4, "medium")
|
||||
BORDER_THICK = (5, "thick")
|
||||
BORDER_NONE = (0, None)
|
||||
BORDER_HAIR = (1, "hair")
|
||||
BORDER_THIN = (2, "thin")
|
||||
BORDER_MEDIUM = (3, "medium")
|
||||
BORDER_THICK = (4, "thick")
|
||||
|
||||
|
||||
class SCO_FONTNAME(Enum):
|
||||
@ -53,10 +56,10 @@ class SCO_FONTNAME(Enum):
|
||||
obj.fontname = fontname
|
||||
return obj
|
||||
|
||||
FONTNAME_CALIBRI = (0, "Calibri")
|
||||
FONTNAME_ARIAL = (1, "Arial")
|
||||
FONTNAME_COURIER = (2, "Courier New")
|
||||
FONTNAME_CALIBRI = (3, "Calibri")
|
||||
FONTNAME_TIMES = (4, "Times New Roman")
|
||||
FONTNAME_TIMES = (3, "Times New Roman")
|
||||
|
||||
|
||||
class SCO_FONTSIZE(Enum):
|
||||
@ -66,11 +69,11 @@ class SCO_FONTSIZE(Enum):
|
||||
obj.fontsize = fontsize
|
||||
return obj
|
||||
|
||||
FONTSIZE_9 = (1, 9)
|
||||
FONTSIZE_10 = (1, 10)
|
||||
FONTSIZE_11 = (1, 11)
|
||||
FONTSIZE_13 = (1, 13)
|
||||
FONTSIZE_16 = (1, 16)
|
||||
FONTSIZE_10 = (0, 10.0)
|
||||
FONTSIZE_9 = (1, 9.0)
|
||||
FONTSIZE_11 = (2, 11.0)
|
||||
FONTSIZE_13 = (3, 13.0)
|
||||
FONTSIZE_16 = (4, 16.0)
|
||||
|
||||
|
||||
class SCO_NUMBER_FORMAT(Enum):
|
||||
@ -91,9 +94,9 @@ class SCO_HALIGN(Enum):
|
||||
obj.position = position
|
||||
return obj
|
||||
|
||||
HALIGN_LEFT = (0, "left")
|
||||
HALIGN_CENTER = (1, "center")
|
||||
HALIGN_RIGHT = (2, "right")
|
||||
HALIGN_LEFT = (3, "left")
|
||||
|
||||
|
||||
class SCO_VALIGN(Enum):
|
||||
@ -103,18 +106,18 @@ class SCO_VALIGN(Enum):
|
||||
obj.position = position
|
||||
return obj
|
||||
|
||||
VALIGN_BOTTOM = (0, "bottom")
|
||||
VALIGN_TOP = (1, "top")
|
||||
VALIGN_BOTTOM = (2, "bottom")
|
||||
VALIGN_CENTER = (3, "center")
|
||||
VALIGN_CENTER = (2, "center")
|
||||
|
||||
|
||||
# Composante (bitfield) atomique. Based on Enums
|
||||
free = 0
|
||||
|
||||
|
||||
class Composante:
|
||||
def __init__(self, base=None, width: int = 1):
|
||||
global free
|
||||
self.cache = {}
|
||||
if base is None:
|
||||
self.base = free
|
||||
free += width
|
||||
@ -131,13 +134,7 @@ class Composante:
|
||||
return signature & ~self.mask
|
||||
|
||||
def write(self, index, signature=0) -> int:
|
||||
return self.clear(signature) + index << self.base
|
||||
|
||||
def lookup_or_cache(self, signature: int):
|
||||
value = self.read(signature)
|
||||
if not value in self.cache:
|
||||
self.cache[signature] = self.build(value)
|
||||
return self.cache[value]
|
||||
return self.clear(signature) + (index << self.base)
|
||||
|
||||
@abc.abstractmethod
|
||||
def build(self, value: int):
|
||||
@ -148,8 +145,12 @@ class Composante_boolean(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=1)
|
||||
|
||||
def set(self, data: bool, signature=0) -> int:
|
||||
return self.write(1 if data else 0, signature)
|
||||
|
||||
def build(self, signature):
|
||||
value = self.read(signature)
|
||||
assert value < (1 << self.width)
|
||||
return value == 1
|
||||
|
||||
|
||||
@ -157,44 +158,76 @@ class Composante_number_format(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=2)
|
||||
|
||||
def set(self, data: SCO_NUMBER_FORMAT, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_NUMBER_FORMAT(value) or None
|
||||
assert value < (1 << self.width)
|
||||
return SCO_NUMBER_FORMAT(value)
|
||||
|
||||
|
||||
class Composante_Colors(Composante):
|
||||
def __init__(self):
|
||||
def __init__(self, default: SCO_COLORS = SCO_COLORS.BLACK):
|
||||
super().__init__(width=5)
|
||||
self.default: SCO_COLORS = default
|
||||
|
||||
def set(self, data: SCO_COLORS, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_COLORS(value) or SCO_COLORS.BLACK
|
||||
assert value < (1 << self.width)
|
||||
if value == 0:
|
||||
return None
|
||||
try:
|
||||
return SCO_COLORS(value)
|
||||
except:
|
||||
return self.default
|
||||
|
||||
|
||||
class Composante_borderThickness(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=2)
|
||||
|
||||
def set(self, data: SCO_BORDERTHICKNESS, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_BORDERTHICKNESS(value) or None
|
||||
assert value < (1 << self.width)
|
||||
try:
|
||||
return SCO_BORDERTHICKNESS(value)
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
class Composante_fontname(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=3)
|
||||
|
||||
def set(self, data: SCO_FONTNAME, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_FONTNAME(value) or None
|
||||
assert value < (1 << self.width)
|
||||
try:
|
||||
return SCO_FONTNAME(value)
|
||||
except:
|
||||
return SCO_FONTNAME.FONTNAME_CALIBRI
|
||||
|
||||
|
||||
class Composante_fontsize(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=3)
|
||||
|
||||
def set(self, data: SCO_FONTSIZE, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
assert value < (1 << self.width)
|
||||
return SCO_FONTSIZE(value) or None
|
||||
|
||||
|
||||
@ -202,18 +235,46 @@ class Composante_halign(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=2)
|
||||
|
||||
def set(self, data: SCO_HALIGN, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_HALIGN(value) or None
|
||||
assert value < (1 << self.width)
|
||||
try:
|
||||
return SCO_HALIGN(value)
|
||||
except:
|
||||
return SCO_HALIGN.HALIGN_LEFT
|
||||
|
||||
|
||||
class Composante_valign(Composante):
|
||||
def __init__(self):
|
||||
super().__init__(width=2)
|
||||
|
||||
def set(self, data: SCO_VALIGN, signature=0) -> int:
|
||||
return self.write(data.value, signature)
|
||||
|
||||
def build(self, signature: int):
|
||||
value = self.read(signature)
|
||||
return SCO_VALIGN(signature) or None
|
||||
assert value < (1 << self.width)
|
||||
try:
|
||||
return SCO_VALIGN(value)
|
||||
except:
|
||||
return SCO_VALIGN.VALIGN_CENTER
|
||||
|
||||
|
||||
# Formatting objects
|
||||
|
||||
|
||||
class Sco_Fill:
|
||||
def __init__(self, color: SCO_COLORS):
|
||||
self.color = color
|
||||
|
||||
def to_openpyxl(self):
|
||||
return PatternFill(
|
||||
fill_type="solid",
|
||||
fgColor=None if self.color is None else self.color.argb,
|
||||
)
|
||||
|
||||
|
||||
class Sco_BorderSide:
|
||||
@ -225,9 +286,11 @@ class Sco_BorderSide:
|
||||
self.thickness = thickness
|
||||
self.color: SCO_COLORS = color
|
||||
|
||||
def get_openxl(self):
|
||||
side: Side = Side(border_style=self.thickness.width, color=self.color.argb)
|
||||
return side
|
||||
def to_openpyxl(self):
|
||||
return Side(
|
||||
border_style=self.thickness.width,
|
||||
color=None if self.color is None else self.color.argb,
|
||||
)
|
||||
|
||||
|
||||
class Sco_Borders:
|
||||
@ -243,12 +306,12 @@ class Sco_Borders:
|
||||
self.top = top
|
||||
self.bottom = bottom
|
||||
|
||||
def get_openxl(self):
|
||||
border: Border = Border(
|
||||
left=self.left.get_openxl(),
|
||||
right=self.right.get_openxl(),
|
||||
top=self.top.get_openxl(),
|
||||
bottom=self.bottom.get_openxl(),
|
||||
def to_openpyxl(self):
|
||||
return Border(
|
||||
left=self.left.to_openpyxl(),
|
||||
right=self.right.to_openpyxl(),
|
||||
top=self.top.to_openpyxl(),
|
||||
bottom=self.bottom.to_openpyxl(),
|
||||
)
|
||||
|
||||
|
||||
@ -261,18 +324,18 @@ class Sco_Alignment:
|
||||
self.halign = halign
|
||||
self.valign = valign
|
||||
|
||||
def get_openxl(self):
|
||||
al = openpyxl.styles.Alignment()
|
||||
al.horizontal = self.halign.position
|
||||
al.vertical = self.valign.position
|
||||
return al
|
||||
def to_openpyxl(self):
|
||||
return Alignment(
|
||||
horizontal=self.halign.position,
|
||||
vertical=self.valign.position,
|
||||
)
|
||||
|
||||
|
||||
class Sco_Font:
|
||||
def __init__(
|
||||
self,
|
||||
name: str = None,
|
||||
fontsize: int = None,
|
||||
name: SCO_FONTNAME = SCO_FONTNAME(0),
|
||||
fontsize: SCO_FONTSIZE = SCO_FONTSIZE(0),
|
||||
bold: bool = None,
|
||||
italic: bool = None,
|
||||
outline: bool = None,
|
||||
@ -285,40 +348,54 @@ class Sco_Font:
|
||||
self.color = color
|
||||
self.fontsize = fontsize
|
||||
|
||||
def to_openpyxl(self):
|
||||
return Font(
|
||||
name=self.name.fontname,
|
||||
size=self.fontsize.fontsize,
|
||||
bold=self.bold,
|
||||
italic=self.italic,
|
||||
outline=self.outline,
|
||||
color=None if self.color is None else self.color.argb,
|
||||
)
|
||||
|
||||
|
||||
class Sco_Style:
|
||||
from openpyxl.cell import WriteOnlyCell, Cell
|
||||
from openpyxl.cell import Cell
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
font: Sco_Font = None,
|
||||
bgcolor: SCO_COLORS = None,
|
||||
fill: Sco_Fill = None,
|
||||
alignment: Sco_Alignment = None,
|
||||
borders: Sco_Borders = None,
|
||||
number_format: SCO_NUMBER_FORMAT = None,
|
||||
):
|
||||
self.font = font
|
||||
self.bgcolor = bgcolor
|
||||
self.fill = fill
|
||||
self.alignment = alignment
|
||||
self.borders = borders
|
||||
self.number_format = number_format
|
||||
|
||||
def apply(self, cell: Cell):
|
||||
if self.font:
|
||||
cell.font = self.font.get_openxl()
|
||||
if self.bgcolor:
|
||||
cell.fill = PatternFill(fill_type="solid", fgColor=self.bgcolor.argb)
|
||||
cell.font = self.font.to_openpyxl()
|
||||
if self.fill and self.fill.color:
|
||||
cell.fill = self.fill.to_openpyxl()
|
||||
if self.alignment:
|
||||
cell.alignment = self.alignment.get_openxl()
|
||||
cell.alignment = self.alignment.to_openpyxl()
|
||||
if self.borders:
|
||||
cell.border = self.borders.get_openxl()
|
||||
cell.border = self.borders.to_openpyxl()
|
||||
if self.number_format:
|
||||
cell.number_format = self.number_format.get_openxl()
|
||||
cell.number_format = self.number_format.format
|
||||
|
||||
|
||||
# Composantes groupant d'autres composantes et dotées d'un mécanisme de cache
|
||||
|
||||
|
||||
class Composante_group(Composante):
|
||||
def __init__(self, composantes: list[Composante]):
|
||||
self.composantes = composantes
|
||||
self.cache = {}
|
||||
mini = min([comp.base for comp in composantes])
|
||||
maxi = max([comp.end for comp in composantes])
|
||||
width = sum([comp.width for comp in composantes])
|
||||
@ -326,6 +403,22 @@ class Composante_group(Composante):
|
||||
raise Exception("Composante group non complete ou non connexe")
|
||||
super().__init__(base=mini, width=width)
|
||||
|
||||
def lookup_or_cache(self, signature: int):
|
||||
value = self.read(signature)
|
||||
assert value < (1 << self.width)
|
||||
if not value in self.cache:
|
||||
self.cache[value] = self.build(signature)
|
||||
return self.cache[value]
|
||||
|
||||
|
||||
class Composante_fill(Composante_group):
|
||||
def __init__(self, color: Composante_Colors):
|
||||
super().__init__([color])
|
||||
self.color = color
|
||||
|
||||
def build(self, signature: int):
|
||||
return Sco_Fill(color=self.color.build(signature))
|
||||
|
||||
|
||||
class Composante_font(Composante_group):
|
||||
def __init__(
|
||||
@ -345,14 +438,14 @@ class Composante_font(Composante_group):
|
||||
self.italic = italic
|
||||
self.outline = outline
|
||||
|
||||
def build(self, signature):
|
||||
def build(self, signature: int):
|
||||
return Sco_Font(
|
||||
name=self.name.lookup_or_cache(signature),
|
||||
fontsize=self.fontsize.lookup_or_cache(signature),
|
||||
color=self.color.lookup_or_cache(signature),
|
||||
bold=self.bold.lookup_or_cache(signature),
|
||||
italic=self.italic.lookup_or_cache(signature),
|
||||
outline=self.outline.lookup_or_cache(signature),
|
||||
name=self.name.build(signature),
|
||||
fontsize=self.fontsize.build(signature),
|
||||
color=self.color.build(signature),
|
||||
bold=self.bold.build(signature),
|
||||
italic=self.italic.build(signature),
|
||||
outline=self.outline.build(signature),
|
||||
)
|
||||
|
||||
|
||||
@ -364,8 +457,8 @@ class Composante_border(Composante_group):
|
||||
|
||||
def build(self, signature: int):
|
||||
return Sco_BorderSide(
|
||||
thickness=self.thick.lookup_or_cache(signature),
|
||||
color=self.color.lookup_or_cache(signature),
|
||||
thickness=self.thick.build(signature),
|
||||
color=self.color.build(signature),
|
||||
)
|
||||
|
||||
|
||||
@ -400,36 +493,34 @@ class Composante_alignment(Composante_group):
|
||||
|
||||
def build(self, signature: int):
|
||||
return Sco_Alignment(
|
||||
halign=self.halign.lookup_or_cache(signature),
|
||||
valign=self.valign.lookup_or_cache(signature),
|
||||
halign=self.halign.build(signature),
|
||||
valign=self.valign.build(signature),
|
||||
)
|
||||
|
||||
# ALL = Composante_all(FONT, BGCOLOR, BORDERS, ALIGNMENT, NUMBER_FORMAT)
|
||||
|
||||
|
||||
class Composante_all(Composante_group):
|
||||
def __init__(
|
||||
self,
|
||||
font: Composante_font,
|
||||
bgcolor: Composante_Colors,
|
||||
fill: Composante_fill,
|
||||
borders: Composante_borders,
|
||||
alignment: Composante_alignment,
|
||||
number_format: Composante_number_format,
|
||||
):
|
||||
super().__init__([font, bgcolor, borders, alignment, number_format])
|
||||
super().__init__([font, fill, borders, alignment, number_format])
|
||||
self.font = font
|
||||
self.bgcolor = bgcolor
|
||||
self.fill = fill
|
||||
self.borders = borders
|
||||
self.alignment = alignment
|
||||
self.number_format = number_format
|
||||
|
||||
def build(self, signature: int):
|
||||
return Sco_Style(
|
||||
bgcolor=self.bgcolor.lookup_or_cache(signature),
|
||||
fill=self.fill.lookup_or_cache(signature),
|
||||
font=self.font.lookup_or_cache(signature),
|
||||
borders=self.borders.lookup_or_cache(signature),
|
||||
alignment=self.alignment.lookup_or_cache(signature),
|
||||
number_format=self.number_format.lookup_or_cache(signature),
|
||||
number_format=self.number_format.build(signature),
|
||||
)
|
||||
|
||||
def get_style(self, signature: int):
|
||||
@ -443,9 +534,22 @@ class FMT(Enum):
|
||||
def write(self, value, signature=0) -> int:
|
||||
return self.composante.write(value, signature)
|
||||
|
||||
def set(self, data, signature: int = 0) -> int:
|
||||
return self.composante.set(data, signature)
|
||||
|
||||
def get_style(self, signature: int):
|
||||
return self.composante.lookup_or_cache(signature)
|
||||
|
||||
@classmethod
|
||||
def compose(cls, composition: list, signature: int = 0) -> int:
|
||||
for field, value in composition:
|
||||
signature = field.write(value, signature)
|
||||
return signature
|
||||
|
||||
@classmethod
|
||||
def style(cls, signature: int):
|
||||
return FMT.ALL.get_style(signature)
|
||||
|
||||
FONT_NAME = Composante_fontname()
|
||||
FONT_SIZE = Composante_fontsize()
|
||||
FONT_COLOR = Composante_Colors()
|
||||
@ -460,17 +564,18 @@ class FMT(Enum):
|
||||
BORDER_TOP_COLOR = Composante_Colors()
|
||||
BORDER_BOTTOM_STYLE = Composante_borderThickness()
|
||||
BORDER_BOTTOM_COLOR = Composante_Colors()
|
||||
BGCOLOR = Composante_Colors()
|
||||
FILL_BGCOLOR = Composante_Colors(None)
|
||||
ALIGNMENT_HALIGN = Composante_halign()
|
||||
ALIGNEMENT_VALIGN = Composante_valign()
|
||||
ALIGNMENT_VALIGN = Composante_valign()
|
||||
NUMBER_FORMAT = Composante_number_format()
|
||||
FONT = Composante_font(
|
||||
FONT_NAME, FONT_SIZE, FONT_COLOR, FONT_BOLD, FONT_ITALIC, FONT_OUTLINE
|
||||
)
|
||||
FILL = Composante_fill(FILL_BGCOLOR)
|
||||
BORDER_LEFT = Composante_border(BORDER_LEFT_STYLE, BORDER_LEFT_COLOR)
|
||||
BORDER_RIGHT = Composante_border(BORDER_RIGHT_STYLE, BORDER_RIGHT_COLOR)
|
||||
BORDER_TOP = Composante_border(BORDER_TOP_STYLE, BORDER_TOP_COLOR)
|
||||
BORDER_BOTTOM = Composante_border(BORDER_BOTTOM_STYLE, BORDER_BOTTOM_COLOR)
|
||||
BORDERS = Composante_borders(BORDER_LEFT, BORDER_RIGHT, BORDER_TOP, BORDER_BOTTOM)
|
||||
ALIGNMENT = Composante_alignment(ALIGNMENT_HALIGN, ALIGNMENT_HALIGN)
|
||||
ALL = Composante_all(FONT, BGCOLOR, BORDERS, ALIGNMENT, NUMBER_FORMAT)
|
||||
ALIGNMENT = Composante_alignment(ALIGNMENT_HALIGN, ALIGNMENT_VALIGN)
|
||||
ALL = Composante_all(FONT, FILL, BORDERS, ALIGNMENT, NUMBER_FORMAT)
|
||||
|
Loading…
x
Reference in New Issue
Block a user