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)
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()
if form.validate_on_submit():
@ -1428,7 +1428,7 @@ def import_donnees():
Config.SCODOC_VAR_DIR, "tmp", secure_filename(file.filename)
)
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)
if lm is None or len(lm) < 2:
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.cell import WriteOnlyCell
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
from openpyxl.worksheet.worksheet import Worksheet
import app.scodoc.sco_utils as scu
from app.scodoc import notesdb
from app.scodoc import sco_preferences
from app import log
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):
try:
filelike = io.BytesIO(bytes_content)
return _excel_to_list(filelike)
except:
except Exception as exc:
raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible !
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):
try:
return _excel_to_list(filename)
except:
except Exception as exc:
raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible !
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
"""
)
) from exc
def excel_file_to_list_are(filename):
try:
return _excel_to_list_are(filename)
except:
except Exception as exc:
raise ScoValueError(
"""Le fichier xlsx attendu n'est pas lisible !
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):
"""returns list of list
convert_to_string is a conversion function applied to all non-string values (ie numbers)
"""
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 !"
)
"""returns list of list"""
workbook = _open_workbook(filelike)
diag = [] # liste de chaines pour former message d'erreur
# n'utilise que la première feuille
if len(wb.get_sheet_names()) < 1:
if len(workbook.get_sheet_names()) < 1:
diag.append("Aucune feuille trouvée dans le classeur !")
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 !")
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
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 = {}
for row in ws.iter_rows():
for row in sheet.iter_rows():
for cell in row:
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)
)
diag.append(f"Aucune valeur trouvée dans la feuille {sheet_name} !")
return diag, None
indexes = list(values.keys())
# search numbers of rows and cols
@ -665,76 +680,38 @@ def _excel_to_list(filelike):
cols = [x[1] for x in indexes]
nbcols = max(cols) + 1
nbrows = max(rows) + 1
m = []
matrix = []
for _ in range(nbrows):
m.append([""] * nbcols)
matrix.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.decode(scu.SCO_ENCODING), len(m))
)
# diag.append(str(M))
#
return diag, m
matrix[row_idx][col_idx] = v
diag.append(f'Feuille "{sheet_name}", {len(matrix)} lignes')
return diag, matrix
def _excel_to_list_are(filelike):
"""returns list of list
convert_to_string is a conversion function applied to all non-string values (ie numbers)
def _excel_workbook_to_list(filelike):
"""Lit un classeur (workbook): chaque feuille est lue
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:
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 !"
)
workbook = _open_workbook(filelike)
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 !")
return diag, None
lm = []
for sheet_name in wb.get_sheet_names():
matrix_list = []
for sheet_name in workbook.get_sheet_names():
# fill matrix
ws = wb.get_sheet_by_name(sheet_name)
sheet_name = sheet_name.encode(scu.SCO_ENCODING, "backslashreplace")
values = {}
for row in ws.iter_rows():
for cell in row:
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
sheet = workbook.get_sheet_by_name(sheet_name)
matrix, diag_sheet = _excel_sheet_to_list(sheet, sheet_name)
diag += diag_sheet
matrix_list.append(matrix)
return diag, matrix_list
def excel_feuille_listeappel(