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

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 

7 

8try: 

9 import openpyxl # type: ignore 

10 

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 

15 

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 

20 

21 

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") 

28 

29 

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 

41 

42 

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 

64 

65 

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) 

72 

73 

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