You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
konova/analysis/utils/excel/excel.py

115 lines
4.1 KiB
Python

"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 21.10.21
"""
from django.core.files.temp import NamedTemporaryFile
from openpyxl import load_workbook
class TempExcelFile:
""" A temporary excel sheet which will not be saved on the hard drive permanently.
Using a template file and a template_map dictionary, this class can be used to fill in automatically
predefined values into certain cells.
Can be used to create excel files from data and sending it as a response like
_file = TempExcelFile()
response = HttpResponse(
content=file.stream,
content_type="application/ms-excel",
)
response['Content-Disposition'] = 'attachment; filename=my_file.xlsx'
return response
"""
stream = None
_template_file_path = None
_template_map = {}
_data_obj = None
def __init__(self, template_file_path: str = None, template_map: dict = None):
self._template_map = template_map or {}
self._template_file_path = template_file_path
self._workbook = load_workbook(template_file_path)
self._file = NamedTemporaryFile()
self._replace_template_placeholders()
def _replace_template_placeholders(self, start_row: int = 0):
""" Replaces all placeholder inside the template according to _template_map
Args:
start_row (int): Defines where to start
Returns:
"""
sheets = self._workbook.worksheets
for sheet in sheets:
ws = sheet
# Always activate sheet protection
ws.protection.sheet = True
ws.protection.enable()
_rows = ws.iter_rows(start_row)
for row in _rows:
for cell in row:
val = cell.value
if val in self._template_map:
attr = self._template_map[val]
# If keyword '_iter' can be found inside the placeholder value it's an iterable and we
# need to process it differently
if isinstance(attr, dict):
# Read the iterable object and related attributes from the dict
_iter_obj = attr.get("iterable", None)
_attrs = attr.get("attrs", [])
self._add_cells_from_iterable(ws, cell, _iter_obj, _attrs)
# Since the sheet length did change now, we need to rerun this function starting with the new
# row counter
self._replace_template_placeholders(start_row=cell.row + len(_iter_obj))
else:
cell.value = attr
self._workbook.save(self._file.name)
self._file.seek(0)
self.stream = self._file.read()
def _add_cells_from_iterable(self, ws, start_cell, _iter_obj: iter, _attrs: list):
"""
Adds iterable data defined by _template_map like
...
"some_placeholder_iter": {
"iterable": iterable_object,
"attrs": [
"attr1",
"attr2",
"attr3",
...
]
},
...
Args:
ws (Workbook): The active workbook
_iter_obj (dict): Iterable definitions from template_map
Returns:
"""
# Save border style
border_style = start_cell.border.copy()
# Drop current row, since it is just placeholder
ws.delete_rows(start_cell.row)
# Add enoug empty rows for the data
ws.insert_rows(start_cell.row, len(_iter_obj))
i = 0
for _iter_entry in _iter_obj:
j = 0
for _iter_attr in _attrs:
_new_cell = ws.cell(start_cell.row + i, start_cell.column + j, getattr(_iter_entry, _iter_attr))
_new_cell.border = border_style
j += 1
i += 1