Coverage for jutil/openpyxl_helpers.py: 0%
64 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-07 16:40 -0500
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-07 16:40 -0500
1from datetime import datetime, date
2from decimal import Decimal
3from django.http import HttpResponse
4from django.utils.html import strip_tags
5from typing import Any, List, Optional
6import os
8try:
9 import openpyxl # type: ignore
11 if int(openpyxl.__version__.split(".", maxsplit=1)[0]) < 3:
12 raise Exception("Invalid version")
13except Exception as err:
14 raise Exception("Using jutil.openpyxl_helpers requires openpyxl>3.0 installed") from err
16from openpyxl import Workbook # type: ignore
17from openpyxl.styles import Alignment, NamedStyle # type: ignore
18from openpyxl.worksheet.worksheet import Worksheet # type: ignore
19from openpyxl.writer.excel import save_virtual_workbook # type: ignore
22class CellConfig:
23 decimal_format: str = "0.00"
24 int_format: str = "0"
25 float_format: str = "0.0"
26 number_alignment = Alignment(horizontal="right")
27 date_style = NamedStyle(name="datetime", number_format="YYYY-MM-DD")
30class ExcelResponse(HttpResponse):
31 def __init__(self, book: Workbook, filename: str, disposition: str = "", content_type: str = ""):
32 if not disposition:
33 disposition = "filename=" + os.path.basename(filename)
34 if not content_type:
35 content_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
36 super().__init__(
37 content=save_virtual_workbook(book),
38 content_type=content_type,
39 )
40 self["Content-Disposition"] = disposition
43def set_cell_value(sheet: Worksheet, row_index: int, column_index: int, val: Any, config: Optional[CellConfig] = None):
44 if config is None:
45 config = CellConfig()
46 c = sheet.cell(row_index + 1, column_index + 1)
47 if isinstance(val, Decimal):
48 c.number_format = config.decimal_format
49 c.alignment = config.number_alignment
50 elif isinstance(val, int):
51 c.number_format = config.int_format
52 c.alignment = config.number_alignment
53 elif isinstance(val, float):
54 c.number_format = config.float_format
55 c.alignment = config.number_alignment
56 elif isinstance(val, (datetime, date)):
57 c.style = config.date_style
58 if isinstance(val, datetime):
59 val = val.replace(tzinfo=None)
60 else:
61 val = strip_tags(str(val if val else ""))
62 c.value = val
63 return c
66def rows_to_sheet(sheet: Worksheet, rows: List[List[Any]], config: Optional[CellConfig]):
67 if config is None:
68 config = CellConfig()
69 for row_ix, row in enumerate(list(rows)):
70 for col_ix, val in enumerate(list(row)):
71 set_cell_value(sheet, row_ix, col_ix, val, config)
74def rows_to_workbook(rows: List[List[Any]], config: Optional[CellConfig] = None) -> Workbook:
75 if config is None:
76 config = CellConfig()
77 book = Workbook()
78 sheet = book.active
79 assert isinstance(sheet, Worksheet)
80 rows_to_sheet(sheet, rows, config)
81 return book