apres refactor merge cells

This commit is contained in:
Jean-Marie Place 2023-05-20 11:29:19 +02:00
parent 7427db0ba6
commit 66480f0d11
4 changed files with 318 additions and 322 deletions

View File

@ -1,84 +0,0 @@
import openpyxl.cell
from openpyxl.cell import Cell
from openpyxl.worksheet.worksheet import Worksheet
from app.but.prepajury_xl import (
ScoExcelSheet,
)
from app.but.prepajury_xl_format import (
SCO_HALIGN,
SCO_VALIGN,
SCO_FONTNAME,
SCO_FONTSIZE,
FMT,
Sco_Style,
)
base_signature = (
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)
)
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 Merge_Engine:
def __init__(self, ws: Worksheet, start_row: int = 1, start_column: int = 1):
self.start_row: int = start_row
self.start_column: int = start_column
self.ws: Worksheet = ws
def close(self, end_row=None, end_column=None):
if end_row is None:
end_row = self.start_row
if end_column is None:
end_column = self.start_column
if (end_row - self.start_row > 0) or (end_column - self.start_column > 0):
self.ws.merge_cells(
start_row=self.start_row,
start_column=self.start_column,
end_row=end_row,
end_column=end_column,
)
if end_row is not None:
end_row = end_row + 1
if end_column is not None:
end_column = end_column + 1
class Sco_Cell:
def __init__(
self, text: str = None, signature: int = base_signature, comment: str = None
):
self.text = text
self.signature = signature or base_signature
self.comment = comment
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.style(self.signature)
style.apply(cell)
return cell

View File

@ -1,7 +1,5 @@
import openpyxl
from openpyxl.worksheet.worksheet import Worksheet
from app.but.prepajury_cells import Sco_Cell, base_signature, set_cell, Merge_Engine
from app.but.prepajury_xl_format import (
SCO_COLORS,
FMT,
@ -10,9 +8,17 @@ from app.but.prepajury_xl_format import (
SCO_HALIGN,
SCO_BORDERTHICKNESS,
Sco_Style,
HAIR_BLACK,
SCO_NUMBER_FORMAT,
)
from app.models import ApcCompetence, ApcParcours
from app.but.prepajury_xl import ScoExcelBook, ScoExcelSheet
from app.but.prepajury_xl import (
ScoExcelBook,
ScoExcelSheet,
base_signature,
Frame_Engine,
Merge_Engine,
)
def parite(semestre_idx):
@ -39,54 +45,6 @@ header_colors = {
}
class _Header:
def __init__(self):
self.lines: list[list[Sco_Cell]] = []
self.presets = [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 setSignatures(self, rangs: list[int], signature: int):
for rang in rangs:
self.signatures[rang] = signature
def add_column(
self,
label0=None,
label1=None,
label2=None,
label3=None,
labels: list[str] = None,
):
if labels is None:
labels = [label0, label1, label2, label3]
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)
def extend(self, header):
self.lines.extend(header.lines)
def write(self, worksheet: ScoExcelSheet):
column = 1
for items in self.lines:
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:
def __init__(self, scodocNiveau):
self.fromScodoc = scodocNiveau
@ -167,14 +125,12 @@ class AnneeDesc:
for _ in range(3):
header.add_column()
def generate_header(self, ws: Worksheet, column: int):
def generate_header(self, ws: ScoExcelSheet, column: int):
start = column
but_signature = FMT.FILL_BGCOLOR.write(
signature=base_signature, value=header_colors[self.codeAnnee]["BUT"].value
)
but_style = FMT.style(but_signature)
set_cell(
ws,
ws.set_cell(
1,
column,
text=self.codeAnnee,
@ -184,25 +140,24 @@ class AnneeDesc:
(FMT.BORDER_LEFT_STYLE, SCO_BORDERTHICKNESS.BORDER_MEDIUM.value),
],
)
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"
ws.set_cell(2, column, "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"
ws.set_cell(3, column, "Nb")
ws.set_cell(4, column, "RCUE")
column += 1
ws.cell(2, column).value = "Année"
ws.cell(4, column).value = "Moy."
ws.set_cell(2, column, "Année")
ws.set_cell(4, column, "Moy.")
column += 1
if self.codeAnnee == "BUT2":
ws.cell(3, column).value = "DUT"
ws.cell(3, column).value = "Rés."
ws.set_cell(3, column, "DUT")
ws.set_cell(3, column, "Rés.")
if self.codeAnnee == "BUT3":
ws.cell(3, column).value = "BUT"
ws.cell(3, column).value = "Rés."
ws.set_cell(3, column, "BUT")
ws.set_cell(3, column, "Rés.")
# ws.merge_cells(start_column=start, end_column=column, start_row=1, end_row=1)
return column
@ -211,18 +166,15 @@ 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),
(FMT.NUMBER_FORMAT, SCO_NUMBER_FORMAT.NUMBER_GENERAL.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_RIGHT, HAIR_BLACK),
# (FMT.BORDER_LEFT, HAIR_BLACK),
# (FMT.BORDER_TOP, HAIR_BLACK),
# (FMT.BORDER_BOTTOM, HAIR_BLACK),
],
base_signature,
)
@ -271,54 +223,27 @@ class ParcoursDesc:
cells4.append(worksheet.make_cell(val4))
def handle_description(
self, ws: Worksheet, description: tuple, row: int, column: int
self, ws: ScoExcelSheet, description: tuple, row: int, column: int
) -> int:
title, content_list = description
style = FMT.style(self.signature_header)
ws.cell(row, column).value = title
style.apply(ws.cell(row, column))
merge_h = Merge_Engine(ws=ws, start_row=row, start_column=column)
merge_v = Merge_Engine(ws=ws, start_row=row, start_column=column)
ws.set_cell(
row=row,
column=column,
text=title,
from_signature=self.signature_header,
)
merge_h = ws.get_merge_engine(start_row=row, start_column=column)
merge_v = ws.get_merge_engine(start_row=row, start_column=column)
if content_list is None:
merge_v.close(end_row=4)
column += 1
else:
first = column
for content in content_list:
column = self.handle_description(ws, content, row + 1, column)
merge_h.close(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))
# merge_h.close(end_column=column)
return column
def generate_etudiant_header(self, ws: Worksheet) -> int:
def generate_etudiant_header(self, ws: ScoExcelSheet) -> int:
titles = (
"ETUDIANT",
[
@ -338,19 +263,23 @@ class ParcoursDesc:
),
],
)
frame = ws.get_frame_engine(
1, 1, SCO_BORDERTHICKNESS.BORDER_THICK, SCO_COLORS.BLUE
)
column = self.handle_description(ws, titles, 1, 1)
frame.close(4, column - 1)
return column
def generate_header(self, ws: Worksheet):
def generate_header(self, ws: ScoExcelSheet):
column: int = self.generate_etudiant_header(ws)
for codeAnnee in listeAnnees:
column = self.annees[codeAnnee].generate_header(ws, column)
def generate(self, workbook: ScoExcelBook):
header = _Header()
if self.fromScodoc:
sheet_name = self.fromScodoc.code
else:
sheet_name = "TC"
worksheet: ScoExcelSheet = workbook.create_sheet(sheet_name)
self.generate_header(worksheet.ws)
self.generate_header(worksheet)

View File

@ -27,10 +27,23 @@
from __future__ import annotations
from collections import defaultdict
from openpyxl.cell import WriteOnlyCell
from openpyxl.worksheet.worksheet import Worksheet
from app.but.prepajury_xl_format import Sco_Style
from app.but.prepajury_xl_format import (
Sco_Style,
FMT,
SCO_FONTNAME,
SCO_FONTSIZE,
SCO_HALIGN,
SCO_VALIGN,
SCO_NUMBER_FORMAT,
SCO_BORDERTHICKNESS,
SCO_COLORS,
fmt_atomics,
)
""" Excel file handling
"""
@ -51,6 +64,93 @@ from app.scodoc.sco_exceptions import ScoValueError
# font, border, number_format, fill,...
# (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
base_signature = (
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)
+ FMT.NUMBER_FORMAT.set(SCO_NUMBER_FORMAT.NUMBER_GENERAL)
)
class Sco_Cell:
def __init__(self, text: str = "", signature: int = 0):
self.text = text
self.signature = signature
def alter(self, signature: int):
for fmt in fmt_atomics:
value: int = fmt.composante.read(signature)
if value > 0:
self.signature = fmt.write(value, self.signature)
def build(self, ws: Worksheet, row: int, column: int):
cell = ws.cell(row, column)
cell.value = self.text
FMT.ALL.apply(cell=cell, signature=self.signature)
class Frame_Engine:
def __init__(
self,
ws: ScoExcelSheet,
start_row: int = 1,
start_column: int = 1,
thickness: SCO_BORDERTHICKNESS = SCO_BORDERTHICKNESS.NONE,
color: SCO_COLORS = SCO_COLORS.BLACK,
):
self.start_row: int = start_row
self.start_column: int = start_column
self.ws: ScoExcelSheet = ws
self.border_style = FMT.BORDER_LEFT.make_zero_based_constant([thickness, color])
def close(self, end_row: int, end_column: int):
left_signature: int = FMT.BORDER_LEFT.write(self.border_style)
right_signature: int = FMT.BORDER_RIGHT.write(self.border_style)
top_signature: int = FMT.BORDER_TOP.write(self.border_style)
bottom_signature: int = FMT.BORDER_BOTTOM.write(self.border_style)
for row in range(self.start_row, end_row + 1):
self.ws.cells[row][self.start_column].alter(left_signature)
self.ws.cells[row][end_column].alter(right_signature)
for column in range(self.start_column, end_column + 1):
self.ws.cells[self.start_row][column].alter(top_signature)
self.ws.cells[end_row][column].alter(bottom_signature)
class Merge_Engine:
def __init__(self, start_row: int = 1, start_column: int = 1):
self.start_row: int = start_row
self.start_column: int = start_column
self.end_row: int = None
self.end_column: int = None
self.closed: bool = False
def close(self, end_row=None, end_column=None):
if end_row is None:
self.end_row = self.start_row + 1
else:
self.end_row = end_row + 1
if end_column is None:
self.end_column = self.start_column + 1
else:
self.end_column = end_column + 1
self.closed = True
def write(self, ws: Worksheet):
if self.closed:
if (self.end_row - self.start_row > 0) and (
self.end_column - self.start_column > 0
):
ws.merge_cells(
start_row=self.start_row,
start_column=self.start_column,
end_row=self.end_row - 1,
end_column=self.end_column - 1,
)
def __repr__(self):
return f"( {self.start_row}:{self.start_column}-{self.end_row}:{self.end_column})[{self.closed}]"
def xldate_as_datetime(xldate, datemode=0):
"""Conversion d'une date Excel en datetime python
@ -90,14 +190,14 @@ class ScoExcelBook:
self.sheets = [] # list of sheets
self.wb = Workbook()
def create_sheet(self, sheet_name="feuille", default_style=None):
def create_sheet(self, sheet_name="feuille", default_signature: int = 0):
"""Crée une nouvelle feuille dans ce classeur
sheet_name -- le nom de la feuille
default_style -- le style par défaut
"""
sheet_name = adjust_sheetname(sheet_name)
ws = self.wb.create_sheet(sheet_name)
sheet = ScoExcelSheet(sheet_name, default_style, ws)
sheet = ScoExcelSheet(sheet_name, default_signature=default_signature, ws=ws)
self.sheets.append(sheet)
return sheet
@ -128,7 +228,12 @@ class ScoExcelSheet:
* pour finir appel de la méthode de génération
"""
def __init__(self, sheet_name="feuille", default_style=None, wb: Workbook = None):
def __init__(
self,
sheet_name: str = "feuille",
default_signature: int = 0,
ws: Worksheet = None,
):
"""Création de la feuille. sheet_name
-- le nom de la feuille default_style
-- le style par défaut des cellules ws
@ -138,19 +243,42 @@ class ScoExcelSheet:
# Le nom de la feuille ne peut faire plus de 31 caractères.
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
self.sheet_name = adjust_sheetname(sheet_name)
self.default_style = default_style or Sco_Style()
if wb is None:
self.default_signature = default_signature
self.merges: list[Merge_Engine] = []
if ws is None:
self.wb = Workbook()
self.ws = self.wb.active
self.ws.title = self.sheet_name
else:
self.wb = None
self.ws = wb
self.ws = ws
# internal data
self.rows = [] # list of list of cells
self.cells = defaultdict(lambda: defaultdict(Sco_Cell))
self.column_dimensions = {}
self.row_dimensions = {}
def get_frame_engine(
self,
start_row: int,
start_column: int,
thickness: SCO_BORDERTHICKNESS = SCO_BORDERTHICKNESS.NONE,
color: SCO_COLORS = SCO_COLORS.NONE,
):
return Frame_Engine(
ws=self,
start_row=start_row,
start_column=start_column,
thickness=thickness,
color=color,
)
def get_merge_engine(self, start_row: int, start_column: int):
merge_engine: Merge_Engine = Merge_Engine(
start_row=start_row, start_column=start_column
)
self.merges.append(merge_engine)
return merge_engine
@staticmethod
def i2col(idx):
if idx < 26: # one letter key
@ -160,6 +288,18 @@ class ScoExcelSheet:
second = (idx % 26) + 65
return "" + chr(first) + chr(second)
def set_cell(
self,
row: int,
column: int,
text: str = "",
from_signature: int = 0,
composition: list = [],
):
cell: Sco_Cell = self.cells[row][column]
cell.text = text
self.signature = FMT.compose(composition, from_signature)
def set_column_dimension_width(self, cle=None, value=21):
"""Détermine la largeur d'une colonne. cle -- identifie la colonne ("A" "B", ... ou 0, 1, 2, ...) si None,
value donne la liste des largeurs de colonnes depuis A, B, C, ... value -- la dimension (unité : 7 pixels
@ -192,83 +332,62 @@ class ScoExcelSheet:
"""
self.ws.row_dimensions[cle].hidden = value
def make_cell(self, value: any = None, style: Sco_Style = None, comment=None):
"""Construit une cellule.
value -- contenu de la cellule (texte, numérique, booléen ou date)
style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié
"""
# adaptation des valeurs si nécessaire
if value is None:
value = ""
elif value is True:
value = 1
elif value is False:
value = 0
elif isinstance(value, datetime.datetime):
value = value.replace(
tzinfo=None
) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones)
# création de la cellule
cell = WriteOnlyCell(self.ws, value)
if style is not None:
style.apply(cell)
if not comment is None:
cell.comment = Comment(comment, "scodoc")
lines = comment.splitlines()
cell.comment.width = 7 * max([len(line) for line in lines]) if lines else 7
cell.comment.height = 20 * len(lines) if lines else 20
# test datatype to overwrite datetime format
if isinstance(value, datetime.date):
cell.data_type = "d"
cell.number_format = FORMAT_DATE_DDMMYY
elif isinstance(value, int) or isinstance(value, float):
cell.data_type = "n"
else:
cell.data_type = "s"
return cell
def make_row(self, values: list, style=None, comments=None) -> list:
"build a row"
# TODO make possible differents styles in a row
if comments is None:
comments = [None] * len(values)
return [
self.make_cell(value, style, comment)
for value, comment in zip(values, comments)
]
def append_single_cell_row(self, value: any, style=None):
"""construit une ligne composée d'une seule cellule et l'ajoute à la feuille.
mêmes paramètres que make_cell:
value -- contenu de la cellule (texte ou numérique)
style -- style par défaut de la feuille si non spécifié
"""
self.append_row([self.make_cell(value, style)])
def append_blank_row(self):
"""construit une ligne vide et l'ajoute à la feuille."""
self.append_row([None])
def append_row(self, row):
"""ajoute une ligne déjà construite à la feuille."""
self.rows.append(row)
# def make_cell(self, value: any = None, style: Sco_Style = None, comment=None):
# """Construit une cellule.
# value -- contenu de la cellule (texte, numérique, booléen ou date)
# style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié
# """
# # adaptation des valeurs si nécessaire
# if value is None:
# value = ""
# elif value is True:
# value = 1
# elif value is False:
# value = 0
# elif isinstance(value, datetime.datetime):
# value = value.replace(
# tzinfo=None
# ) # make date naive (cf https://openpyxl.readthedocs.io/en/latest/datetime.html#timezones)
#
# # création de la cellule
# cell = WriteOnlyCell(self.ws, value)
#
# if style is not None:
# style.apply(cell)
#
# if not comment is None:
# cell.comment = Comment(comment, "scodoc")
# lines = comment.splitlines()
# cell.comment.width = 7 * max([len(line) for line in lines]) if lines else 7
# cell.comment.height = 20 * len(lines) if lines else 20
#
# # test datatype to overwrite datetime format
# if isinstance(value, datetime.date):
# cell.data_type = "d"
# cell.number_format = FORMAT_DATE_DDMMYY
# elif isinstance(value, int) or isinstance(value, float):
# cell.data_type = "n"
# else:
# cell.data_type = "s"
#
# return cell
def prepare(self):
"""génére un flux décrivant la feuille.
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
ou pour la génération d'un classeur multi-feuilles
"""
for row in self.column_dimensions.keys():
self.ws.column_dimensions[row] = self.column_dimensions[row]
for row in self.row_dimensions.keys():
self.ws.row_dimensions[row] = self.row_dimensions[row]
for row in self.rows:
self.ws.append(row)
# for row in self.column_dimensions.keys():
# self.ws.column_dimensions[row] = self.column_dimensions[row]
# for row in self.row_dimensions.keys():
# self.ws.row_dimensions[row] = self.row_dimensions[row]
# for row in self.rows:
# self.ws.append(row)
for row in self.cells:
for column in self.cells[row]:
self.cells[row][column].build(self.ws, row, column)
for merge_engine in self.merges:
merge_engine.write(self.ws)
def generate(self):
"""génération d'un classeur mono-feuille"""

View File

@ -2,6 +2,7 @@ import abc
from enum import Enum
import openpyxl.styles
from openpyxl.cell import Cell
from openpyxl.styles import Side, Border, Font, PatternFill, Alignment
from openpyxl.styles.numbers import FORMAT_GENERAL, FORMAT_NUMBER_00
@ -42,7 +43,7 @@ class SCO_BORDERTHICKNESS(Enum):
obj.width = width
return obj
BORDER_NONE = (0, None)
NONE = (0, None)
BORDER_HAIR = (1, "hair")
BORDER_THIN = (2, "thin")
BORDER_MEDIUM = (3, "medium")
@ -56,10 +57,11 @@ class SCO_FONTNAME(Enum):
obj.fontname = fontname
return obj
FONTNAME_CALIBRI = (0, "Calibri")
FONTNAME_ARIAL = (1, "Arial")
FONTNAME_COURIER = (2, "Courier New")
FONTNAME_TIMES = (3, "Times New Roman")
NONE = (0, None)
FONTNAME_CALIBRI = (1, "Calibri")
FONTNAME_ARIAL = (2, "Arial")
FONTNAME_COURIER = (3, "Courier New")
FONTNAME_TIMES = (4, "Times New Roman")
class SCO_FONTSIZE(Enum):
@ -69,11 +71,11 @@ class SCO_FONTSIZE(Enum):
obj.fontsize = fontsize
return obj
FONTSIZE_10 = (0, 10.0)
NONE = (0, None)
FONTSIZE_9 = (1, 9.0)
FONTSIZE_10 = (2, 10.0)
FONTSIZE_11 = (2, 11.0)
FONTSIZE_13 = (3, 13.0)
FONTSIZE_16 = (4, 16.0)
FONTSIZE_13 = (4, 13.0)
class SCO_NUMBER_FORMAT(Enum):
@ -83,6 +85,7 @@ class SCO_NUMBER_FORMAT(Enum):
obj.format = format
return obj
NONE = (0, None)
NUMBER_GENERAL = (0, FORMAT_GENERAL)
NUMBER_00 = (1, FORMAT_NUMBER_00)
@ -94,9 +97,10 @@ class SCO_HALIGN(Enum):
obj.position = position
return obj
HALIGN_LEFT = (0, "left")
HALIGN_CENTER = (1, "center")
HALIGN_RIGHT = (2, "right")
NONE = (0, None)
HALIGN_LEFT = (1, "left")
HALIGN_CENTER = (2, "center")
HALIGN_RIGHT = (3, "right")
class SCO_VALIGN(Enum):
@ -151,7 +155,7 @@ class Composante_boolean(Composante):
def set(self, data: bool, signature=0) -> int:
return self.write(1 if data else 0, signature)
def build(self, signature):
def build(self, signature) -> bool:
value = self.read(signature)
assert value < (1 << self.width)
return value == 1
@ -167,7 +171,7 @@ class Composante_number_format(Composante):
def set(self, data: SCO_NUMBER_FORMAT, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_NUMBER_FORMAT:
value = self.read(signature)
assert value < (1 << self.width)
return SCO_NUMBER_FORMAT(value)
@ -184,7 +188,7 @@ class Composante_Colors(Composante):
def set(self, data: SCO_COLORS, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_COLORS:
value = self.read(signature)
assert value < (1 << self.width)
if value == 0:
@ -205,7 +209,7 @@ class Composante_borderThickness(Composante):
def set(self, data: SCO_BORDERTHICKNESS, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_BORDERTHICKNESS:
value = self.read(signature)
assert value < (1 << self.width)
try:
@ -224,7 +228,7 @@ class Composante_fontname(Composante):
def set(self, data: SCO_FONTNAME, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_FONTNAME:
value = self.read(signature)
assert value < (1 << self.width)
try:
@ -243,14 +247,14 @@ class Composante_fontsize(Composante):
def set(self, data: SCO_FONTSIZE, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_FONTSIZE:
value = self.read(signature)
assert value < (1 << self.width)
return SCO_FONTSIZE(value) or None
class Composante_halign(Composante):
WIDTH: int = 2
WIDTH: int = 3
def __init__(self):
assert (1 << self.WIDTH) > SCO_HALIGN.__len__()
@ -259,7 +263,7 @@ class Composante_halign(Composante):
def set(self, data: SCO_HALIGN, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_HALIGN:
value = self.read(signature)
assert value < (1 << self.width)
try:
@ -269,7 +273,7 @@ class Composante_halign(Composante):
class Composante_valign(Composante):
WIDTH: int = 2
WIDTH: int = 3
def __init__(self):
assert (1 << self.WIDTH) > SCO_VALIGN.__len__()
@ -278,7 +282,7 @@ class Composante_valign(Composante):
def set(self, data: SCO_VALIGN, signature=0) -> int:
return self.write(data.value, signature)
def build(self, signature: int):
def build(self, signature: int) -> SCO_VALIGN:
value = self.read(signature)
assert value < (1 << self.width)
try:
@ -312,7 +316,7 @@ class Sco_BorderSide:
def to_openpyxl(self):
return Side(
border_style=self.thickness.width,
border_style=None if self.thickness is None else self.thickness.width,
color=None if self.color is None else self.color.argb,
)
@ -350,8 +354,8 @@ class Sco_Alignment:
def to_openpyxl(self):
return Alignment(
horizontal=self.halign.position,
vertical=self.valign.position,
horizontal=None if self.halign is None else self.halign.position,
vertical=None if self.valign is None else self.valign.position,
)
@ -374,8 +378,8 @@ class Sco_Font:
def to_openpyxl(self):
return Font(
name=self.name.fontname,
size=self.fontsize.fontsize,
name=None if self.name is None else self.name.fontname,
size=None if self.fontsize is None else self.fontsize.fontsize,
bold=self.bold,
italic=self.italic,
outline=self.outline,
@ -392,13 +396,13 @@ class Sco_Style:
fill: Sco_Fill = None,
alignment: Sco_Alignment = None,
borders: Sco_Borders = None,
number_format: SCO_NUMBER_FORMAT = None,
number_format: SCO_NUMBER_FORMAT = FORMAT_GENERAL,
):
self.font = font
self.fill = fill
self.alignment = alignment
self.borders = borders
self.number_format = number_format
self.font = font or None
self.fill = fill or None
self.alignment = alignment or None
self.borders = borders or None
self.number_format = number_format or SCO_NUMBER_FORMAT.NUMBER_GENERAL
def apply(self, cell: Cell):
if self.font:
@ -409,8 +413,12 @@ class Sco_Style:
cell.alignment = self.alignment.to_openpyxl()
if self.borders:
cell.border = self.borders.to_openpyxl()
if self.number_format:
cell.number_format = self.number_format.format
cell.number_format = (
FORMAT_GENERAL
if self.number_format is None
or self.number_format == SCO_NUMBER_FORMAT.NONE
else self.number_format.format
)
# Composantes groupant d'autres composantes et dotées d'un mécanisme de cache
@ -448,7 +456,7 @@ class Composante_fill(Composante_group):
super().__init__([color])
self.color = color
def build(self, signature: int):
def build(self, signature: int) -> Sco_Fill:
return Sco_Fill(color=self.color.build(signature))
@ -470,7 +478,7 @@ class Composante_font(Composante_group):
self.italic = italic
self.outline = outline
def build(self, signature: int):
def build(self, signature: int) -> Sco_Font:
return Sco_Font(
name=self.name.build(signature),
fontsize=self.fontsize.build(signature),
@ -487,7 +495,7 @@ class Composante_border(Composante_group):
self.thick = thick
self.color = color
def build(self, signature: int):
def build(self, signature: int) -> Sco_BorderSide:
return Sco_BorderSide(
thickness=self.thick.build(signature),
color=self.color.build(signature),
@ -508,7 +516,7 @@ class Composante_borders(Composante_group):
self.top = top
self.bottom = bottom
def build(self, signature: int):
def build(self, signature: int) -> Sco_Borders:
return Sco_Borders(
left=self.left.lookup_or_cache(signature),
right=self.right.lookup_or_cache(signature),
@ -523,7 +531,7 @@ class Composante_alignment(Composante_group):
self.halign = halign
self.valign = valign
def build(self, signature: int):
def build(self, signature: int) -> Sco_Alignment:
return Sco_Alignment(
halign=self.halign.build(signature),
valign=self.valign.build(signature),
@ -547,7 +555,7 @@ class Composante_all(Composante_group):
self.alignment = alignment
self.number_format = number_format
def build(self, signature: int):
def build(self, signature: int) -> Sco_Style:
return Sco_Style(
fill=self.fill.lookup_or_cache(signature),
font=self.font.lookup_or_cache(signature),
@ -576,14 +584,17 @@ class FMT(Enum):
def make_zero_based_constant(self, enums: list[Enum]) -> int:
return self.composante.make_zero_based_constant(enums=enums)
def apply(self, cell: Cell, signature: int):
self.composante.build(signature).apply(cell)
@classmethod
def compose(cls, composition: list, signature: int = 0) -> int:
def compose(cls, composition: list[("FMT", int)], signature: int = 0) -> int:
for field, value in composition:
signature = field.write(value, signature)
return signature
@classmethod
def style(cls, signature: int):
def style(cls, signature: int = None) -> Sco_Style:
return FMT.ALL.get_style(signature)
FONT_NAME = Composante_fontname()
@ -617,6 +628,27 @@ class FMT(Enum):
ALL = Composante_all(FONT, FILL, BORDERS, ALIGNMENT, NUMBER_FORMAT)
fmt_atomics = {
FMT.FONT_NAME,
FMT.FONT_SIZE,
FMT.FONT_COLOR,
FMT.FONT_BOLD,
FMT.FONT_ITALIC,
FMT.FONT_OUTLINE,
FMT.BORDER_LEFT_STYLE,
FMT.BORDER_LEFT_COLOR,
FMT.BORDER_RIGHT_STYLE,
FMT.BORDER_RIGHT_COLOR,
FMT.BORDER_TOP_STYLE,
FMT.BORDER_TOP_COLOR,
FMT.BORDER_BOTTOM_STYLE,
FMT.BORDER_BOTTOM_COLOR,
FMT.FILL_BGCOLOR,
FMT.ALIGNMENT_HALIGN,
FMT.ALIGNMENT_VALIGN,
FMT.NUMBER_FORMAT,
}
HAIR_BLACK: int = FMT.BORDER_LEFT.make_zero_based_constant(
enums=[SCO_BORDERTHICKNESS.BORDER_HAIR, SCO_COLORS.BLACK]
)