Amélioration sco_excel

This commit is contained in:
Emmanuel Viennet 2022-07-07 11:56:18 +02:00
parent 17b73936de
commit 48912032c4
2 changed files with 70 additions and 93 deletions

View File

@ -1419,7 +1419,7 @@ def get_import_donnees_file_sample():
@permission_required(Permission.RelationsEntreprisesExport) @permission_required(Permission.RelationsEntreprisesExport)
def import_donnees(): def import_donnees():
""" """
Permet d'importer des entreprises a l'aide d'un fichier excel (.xlsx) Permet d'importer des entreprises à partir d'un fichier excel (.xlsx)
""" """
form = ImportForm() form = ImportForm()
if form.validate_on_submit(): if form.validate_on_submit():
@ -1428,7 +1428,7 @@ def import_donnees():
Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename) Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename)
) )
file.save(file_path) file.save(file_path)
diag, lm = sco_excel.excel_file_to_list_are(file_path) diag, lm = sco_excel.excel_workbook_to_list(file_path)
os.remove(file_path) os.remove(file_path)
if lm is None or len(lm) < 2: if lm is None or len(lm) < 2:
flash("Veuillez utilisez la feuille excel à remplir") flash("Veuillez utilisez la feuille excel à remplir")

View File

@ -40,10 +40,9 @@ from openpyxl.comments import Comment
from openpyxl import Workbook, load_workbook from openpyxl import Workbook, load_workbook
from openpyxl.cell import WriteOnlyCell from openpyxl.cell import WriteOnlyCell
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
from openpyxl.worksheet.worksheet import Worksheet
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc import notesdb
from app.scodoc import sco_preferences
from app import log from app import log
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
@ -593,71 +592,87 @@ def excel_feuille_saisie(e, titreannee, description, lines):
def excel_bytes_to_list(bytes_content): def excel_bytes_to_list(bytes_content):
try: try:
filelike = io.BytesIO(bytes_content) filelike = io.BytesIO(bytes_content)
return _excel_to_list(filelike) except Exception as exc:
except:
raise ScoValueError( raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible ! """Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ..) Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ..)
""" """
) ) from exc
return _excel_to_list(filelike)
def excel_file_to_list(filename): def excel_file_to_list(filename):
try: try:
return _excel_to_list(filename) return _excel_to_list(filename)
except: except Exception as exc:
raise ScoValueError( raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible ! """Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
""" """
) ) from exc
def excel_file_to_list_are(filename): def excel_file_to_list_are(filename):
try: try:
return _excel_to_list_are(filename) return _excel_to_list_are(filename)
except: except Exception as exc:
raise ScoValueError( raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible ! """Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...) Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
""" """
) ) from exc
def _open_workbook(filelike, dump_debug=False) -> Workbook:
"""Open document.
On error, if dump-debug is True, dump data in /tmp for debugging purpose
"""
try:
workbook = load_workbook(filename=filelike, read_only=True, data_only=True)
except Exception as exc:
log("Excel_to_list: failure to import document")
if dump_debug:
dump_filename = "/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX
log(f"Dumping problemetic file on {dump_filename}")
with open(dump_filename, "wb") as f:
f.write(filelike)
raise ScoValueError(
"Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel xlsx !"
) from exc
return workbook
def _excel_to_list(filelike): def _excel_to_list(filelike):
"""returns list of list """returns list of list"""
convert_to_string is a conversion function applied to all non-string values (ie numbers) workbook = _open_workbook(filelike)
"""
try:
wb = load_workbook(filename=filelike, read_only=True, data_only=True)
except:
log("Excel_to_list: failure to import document")
with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f:
f.write(filelike)
raise ScoValueError(
"Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !"
)
diag = [] # liste de chaines pour former message d'erreur diag = [] # liste de chaines pour former message d'erreur
# n'utilise que la première feuille if len(workbook.get_sheet_names()) < 1:
if len(wb.get_sheet_names()) < 1:
diag.append("Aucune feuille trouvée dans le classeur !") diag.append("Aucune feuille trouvée dans le classeur !")
return diag, None return diag, None
if len(wb.get_sheet_names()) > 1: # n'utilise que la première feuille:
if len(workbook.get_sheet_names()) > 1:
diag.append("Attention: n'utilise que la première feuille du classeur !") diag.append("Attention: n'utilise que la première feuille du classeur !")
sheet_name = workbook.get_sheet_names()[0]
ws = workbook[sheet_name]
matrix, diag_sheet = _excel_sheet_to_list(ws, sheet_name)
diag += diag_sheet
return diag, matrix
def _excel_sheet_to_list(sheet: Worksheet, sheet_name: str) -> tuple[list, list]:
"""read a spreadsheet sheet, and returns:
- diag : a list of strings (error messages aimed at helping the user)
- a list of lists: the spreadsheet cells
"""
diag = []
# fill matrix # fill matrix
sheet_name = wb.get_sheet_names()[0]
ws = wb.get_sheet_by_name(sheet_name)
sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace")
values = {} values = {}
for row in ws.iter_rows(): for row in sheet.iter_rows():
for cell in row: for cell in row:
if cell.value is not None: if cell.value is not None:
values[(cell.row - 1, cell.column - 1)] = str(cell.value) values[(cell.row - 1, cell.column - 1)] = str(cell.value)
if not values: if not values:
diag.append( diag.append(f"Aucune valeur trouvée dans la feuille {sheet_name} !")
"Aucune valeur trouvée dans la feuille %s !"
% sheet_name.decode(scu.SCO_ENCODING)
)
return diag, None return diag, None
indexes = list(values.keys()) indexes = list(values.keys())
# search numbers of rows and cols # search numbers of rows and cols
@ -665,76 +680,38 @@ def _excel_to_list(filelike):
cols = [x[1] for x in indexes] cols = [x[1] for x in indexes]
nbcols = max(cols) + 1 nbcols = max(cols) + 1
nbrows = max(rows) + 1 nbrows = max(rows) + 1
m = [] matrix = []
for _ in range(nbrows): for _ in range(nbrows):
m.append([""] * nbcols) matrix.append([""] * nbcols)
for row_idx, col_idx in indexes: for row_idx, col_idx in indexes:
v = values[(row_idx, col_idx)] v = values[(row_idx, col_idx)]
# if isinstance(v, six.text_type): matrix[row_idx][col_idx] = v
# v = v.encode(scu.SCO_ENCODING, "backslashreplace") diag.append(f'Feuille "{sheet_name}", {len(matrix)} lignes')
# elif convert_to_string:
# v = convert_to_string(v) return diag, matrix
m[row_idx][col_idx] = v
diag.append(
'Feuille "%s", %d lignes' % (sheet_name.decode(scu.SCO_ENCODING), len(m))
)
# diag.append(str(M))
#
return diag, m
def _excel_to_list_are(filelike): def _excel_workbook_to_list(filelike):
"""returns list of list """Lit un classeur (workbook): chaque feuille est lue
convert_to_string is a conversion function applied to all non-string values (ie numbers) et est convertie en une liste de listes.
Returns:
- diag : a list of strings (error messages aimed at helping the user)
- a list of lists: the spreadsheet cells
""" """
try: workbook = _open_workbook(filelike)
wb = load_workbook(filename=filelike, read_only=True, data_only=True)
except:
log("Excel_to_list: failure to import document")
with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f:
f.write(filelike)
raise ScoValueError(
"Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !"
)
diag = [] # liste de chaines pour former message d'erreur diag = [] # liste de chaines pour former message d'erreur
if len(wb.get_sheet_names()) < 1: if len(workbook.get_sheet_names()) < 1:
diag.append("Aucune feuille trouvée dans le classeur !") diag.append("Aucune feuille trouvée dans le classeur !")
return diag, None return diag, None
lm = [] matrix_list = []
for sheet_name in wb.get_sheet_names(): for sheet_name in workbook.get_sheet_names():
# fill matrix # fill matrix
ws = wb.get_sheet_by_name(sheet_name) sheet = workbook.get_sheet_by_name(sheet_name)
sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace") matrix, diag_sheet = _excel_sheet_to_list(sheet, sheet_name)
values = {} diag += diag_sheet
for row in ws.iter_rows(): matrix_list.append(matrix)
for cell in row: return diag, matrix_list
if cell.value is not None:
values[(cell.row - 1, cell.column - 1)] = str(cell.value)
if not values:
diag.append(
"Aucune valeur trouvée dans la feuille %s !"
% sheet_name.decode(scu.SCO_ENCODING)
)
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)]
m[row_idx][col_idx] = v
diag.append(
'Feuille "%s", %d lignes' % (sheet_name.decode(scu.SCO_ENCODING), len(m))
)
lm.append(m)
return diag, lm
def excel_feuille_listeappel( def excel_feuille_listeappel(