#26 Annual conservation report
* Adds download as excel sheet * improves db access performancepull/33/head
parent
419a48cff1
commit
4e89fd9678
@ -0,0 +1,112 @@
|
||||
"""
|
||||
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:
|
||||
|
||||
"""
|
||||
ws = self._workbook.active
|
||||
# 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
|
Binary file not shown.
Loading…
Reference in New Issue