basic-report 0.3.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. {basic_report-0.3.0 → basic_report-0.4.0}/PKG-INFO +1 -1
  2. {basic_report-0.3.0 → basic_report-0.4.0}/pyproject.toml +1 -1
  3. basic_report-0.4.0/src/basic_report/__init__.py +22 -0
  4. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/configs/default.yaml +6 -0
  5. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/heatmap.py +80 -42
  6. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/page.py +73 -16
  7. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/report.py +37 -7
  8. basic_report-0.4.0/src/basic_report/templates/elements/dictionary.html +10 -0
  9. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/utils.py +3 -6
  10. basic_report-0.3.0/src/basic_report/__init__.py +0 -20
  11. {basic_report-0.3.0 → basic_report-0.4.0}/LICENSE.md +0 -0
  12. {basic_report-0.3.0 → basic_report-0.4.0}/README.md +0 -0
  13. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/basic_report/section_title.css +0 -0
  14. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/bootstrap/bootstrap.min.css +0 -0
  15. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/bootstrap/bootstrap.min.js +0 -0
  16. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/bootstrap/jquery-3.3.1.slim.min.js +0 -0
  17. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/bootstrap/popper.min.js +0 -0
  18. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/datatables/dataTables.bootstrap4.min.js +0 -0
  19. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/datatables/jquery.dataTables.min.js +0 -0
  20. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/CalendarLabel.min.js +0 -0
  21. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/Legend.min.js +0 -0
  22. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/Tooltip.min.js +0 -0
  23. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/cal-heatmap.css +0 -0
  24. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/cal-heatmap.min.js +0 -0
  25. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/d3.v7.min.js +0 -0
  26. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/css_and_scripts/heatmap/popper.min.js +0 -0
  27. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/base.html +0 -0
  28. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/accordion.css +0 -0
  29. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/colors.css +0 -0
  30. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/heatmap.css +0 -0
  31. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/navbar.css +0 -0
  32. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/site.css +0 -0
  33. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/table.css +0 -0
  34. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/css/tabs.css +0 -0
  35. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/accordion.html +0 -0
  36. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/cards.html +0 -0
  37. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/columns.html +0 -0
  38. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/global_links.html +0 -0
  39. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/header.html +0 -0
  40. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/image.html +0 -0
  41. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/list.html +0 -0
  42. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/navbar_left.html +0 -0
  43. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/navbar_right.html +0 -0
  44. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/navbar_top.html +0 -0
  45. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/report_ball_section.html +0 -0
  46. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/report_header.html +0 -0
  47. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/section_title.html +0 -0
  48. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/sublevel.html +0 -0
  49. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/table.html +0 -0
  50. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/tabs.html +0 -0
  51. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/elements/text.html +0 -0
  52. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/base.html +0 -0
  53. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/button_next.html +0 -0
  54. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/button_prev.html +0 -0
  55. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/heatmap.html +0 -0
  56. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/legend.html +0 -0
  57. {basic_report-0.3.0 → basic_report-0.4.0}/src/basic_report/templates/heatmap/script.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basic-report
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: A lightweight Python package for generating clean, professional static HTML reports.
5
5
  Keywords: html,report,static,responsive,website
6
6
  Author: Björn Scholz
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "basic-report"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "A lightweight Python package for generating clean, professional static HTML reports."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,22 @@
1
+ from pathlib import Path
2
+
3
+ # Make the package dir available in the module
4
+ PACKAGE_DIR = Path(__file__).parent
5
+ PACKAGE_NAME = PACKAGE_DIR.name
6
+ CONFIG_DIR = PACKAGE_DIR / 'configs'
7
+ TEMPLATE_DIR = PACKAGE_DIR / 'templates'
8
+ SCRIPTS_DIR = PACKAGE_DIR / 'css_and_scripts'
9
+
10
+ # ruff: disable[E402]
11
+ from .report import Report as Report
12
+ from .heatmap import (
13
+ CalendarDomain as CalendarDomain,
14
+ CalendarLabel as CalendarLabel,
15
+ CalendarSubDomain as CalendarSubDomain,
16
+ CalendarDate as CalendarDate,
17
+ CalendarData as CalendarData,
18
+ CalendarColor as CalendarColor,
19
+ CalendarMisc as CalendarMisc,
20
+ CalendarHeatmap as CalendarHeatmap,
21
+ )
22
+ # ruff: enable[E402]
@@ -87,6 +87,9 @@ default_color_profiles:
87
87
  section_title:
88
88
  text: slate
89
89
  bars: slate
90
+ dictionary:
91
+ key: light-gray
92
+ value: slate
90
93
  card:
91
94
  text: steel
92
95
  background: dark-gray
@@ -109,6 +112,9 @@ default_color_profiles:
109
112
  section_title:
110
113
  text: slate
111
114
  bars: slate
115
+ dictionary:
116
+ key: steel
117
+ value: slate
112
118
  card:
113
119
  text: slate
114
120
  background: white
@@ -9,7 +9,7 @@ from pathlib import Path
9
9
  from jinja2 import Environment, FileSystemLoader
10
10
  from . import TEMPLATE_DIR
11
11
 
12
- DATE_PATTERN = r"^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$"
12
+ DATE_PATTERN = r'^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$'
13
13
 
14
14
  #-----------------------------------------------------------------------------------------------------------------------
15
15
  # region DOMAIN
@@ -20,7 +20,7 @@ DomainSortLiteral = Literal['asc', 'desc']
20
20
  @dataclass
21
21
  class CalendarDomain:
22
22
  """ Define the calendar domain
23
-
23
+
24
24
  See https://cal-heatmap.com/docs/options/domain for all options and what they mean
25
25
  """
26
26
  type: DomainTypeLiteral = 'month'
@@ -30,9 +30,10 @@ class CalendarDomain:
30
30
  sort: DomainSortLiteral = 'asc'
31
31
 
32
32
  def __post_init__(self):
33
+ """ Check data input """
33
34
  checks = [
34
35
  ['type', DomainTypeLiteral],
35
- ['sort', DomainSortLiteral]
36
+ ['sort', DomainSortLiteral],
36
37
  ]
37
38
 
38
39
  for name, literal in checks:
@@ -42,8 +43,8 @@ class CalendarDomain:
42
43
  msg = f'Domain parameter {name}={value} is not supported. Use one of {allowed}'
43
44
  logger.error(msg)
44
45
  raise ValueError(msg)
45
-
46
- if any([i < 0 for i in self.padding]):
46
+
47
+ if any(i < 0 for i in self.padding):
47
48
  msg = f'Domain padding={self.padding} cannot contain negative values, usage [T,R,B,L]'
48
49
  logger.error(msg)
49
50
  raise ValueError(msg)
@@ -57,15 +58,15 @@ LabelTextLiteral = Literal['YY', 'YYYY',
57
58
  'd', 'dd', 'ddd', 'dddd',
58
59
  'H', 'HH',
59
60
  'h', 'hh',
60
- None]
61
+ None] # noqa PYI061
61
62
  LabelPositionLiteral = Literal['top', 'right', 'bottom', 'left']
62
63
  LabelAlignLiteral = Literal['start', 'middle', 'end']
63
- LabelRotateLiteral = Literal['left', 'right', None]
64
+ LabelRotateLiteral = Literal['left', 'right', None] # noqa PYI061
64
65
 
65
66
  @dataclass
66
67
  class CalendarLabel:
67
68
  """ Define the calendar label
68
-
69
+
69
70
  See https://cal-heatmap.com/docs/options/domain/label for all options and what they mean
70
71
  """
71
72
  text: LabelTextLiteral | None = None
@@ -79,6 +80,7 @@ class CalendarLabel:
79
80
  font_weight: int = 450
80
81
 
81
82
  def __post_init__(self):
83
+ """ Check data input """
82
84
  checks = [
83
85
  ['text', LabelTextLiteral],
84
86
  ['position', LabelPositionLiteral],
@@ -93,14 +95,13 @@ class CalendarLabel:
93
95
  msg = f'Label parameter {name}={value} is not supported. Use one of {allowed}'
94
96
  logger.error(msg)
95
97
  raise ValueError(msg)
96
-
98
+
97
99
  for k in ['width', 'height', 'font_size', 'font_weight']:
98
100
  value = self.__dict__[k]
99
101
  if value < 0:
100
102
  msg = f'Label parameter `{k}={value}` cannot be negative.'
101
103
  logger.error(msg)
102
104
  raise ValueError(msg)
103
-
104
105
 
105
106
  #-----------------------------------------------------------------------------------------------------------------------
106
107
  # region SUB-DOMAIN
@@ -108,16 +109,16 @@ class CalendarLabel:
108
109
  SubdomainTypeLiteral = Literal['minute', 'hour', 'day', 'week', 'month', 'xDay', 'ghDay']
109
110
  SubdomainSortLiteral = Literal['asc', 'desc']
110
111
  SubdomainLabelLiteral = Literal['M', 'MM', 'MMM', 'MMMM',
111
- 'D', 'DD',
112
+ 'D', 'DD',
112
113
  'd', 'dd', 'ddd', 'dddd',
113
114
  'H', 'HH',
114
115
  'h', 'hh',
115
116
  'm', 'mm',
116
- None]
117
+ None] # noqa PYI061
117
118
  @dataclass
118
119
  class CalendarSubDomain:
119
120
  """ Define the calendar sub domain
120
-
121
+
121
122
  See https://cal-heatmap.com/docs/options/subDomainfor all options and what they mean
122
123
  """
123
124
  type: SubdomainTypeLiteral = 'day'
@@ -132,6 +133,7 @@ class CalendarSubDomain:
132
133
  background_color: str = '#ededed'
133
134
 
134
135
  def __post_init__(self):
136
+ """ Check data input """
135
137
  checks = [
136
138
  ['type', SubdomainTypeLiteral],
137
139
  ['sort', SubdomainSortLiteral],
@@ -154,15 +156,17 @@ class CalendarSubDomain:
154
156
  raise ValueError(msg)
155
157
 
156
158
  if not re.match(r'^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$', self.background_color):
157
- raise ValueError(f'Sub-Domain `background_color` is an invalid hex color: `{self.background_color}`')
158
-
159
+ msg = f'Sub-Domain `background_color` is an invalid hex color: `{self.background_color}`'
160
+ logger.error(msg)
161
+ raise ValueError(msg)
162
+
159
163
  #-----------------------------------------------------------------------------------------------------------------------
160
164
  # region DATE
161
165
  #-----------------------------------------------------------------------------------------------------------------------
162
166
  @dataclass
163
167
  class CalendarDate:
164
168
  """ Define the calendar dates
165
-
169
+
166
170
  See https://cal-heatmap.com/docs/options/date for all options and what they mean
167
171
  """
168
172
  start: str = dt.today().strftime('%Y-%m-%d')
@@ -173,21 +177,22 @@ class CalendarDate:
173
177
  timezone: str = 'Europe/Berlin'
174
178
 
175
179
  def __post_init__(self):
180
+ """ Check data input """
176
181
  for k in ['start', 'min' , 'max']:
177
182
  value = self.__dict__[k]
178
183
  if value is not None and not re.match(DATE_PATTERN, value):
179
184
  msg = f'Date parameter `{k}={value}` does not match requirements (YYYY-MM-DD).'
180
185
  logger.error(msg)
181
186
  raise ValueError(msg)
182
-
187
+
183
188
  if self.highlight is not None:
184
189
  for d in self.highlight:
185
190
  if d != 'today' and not re.match(DATE_PATTERN, d):
186
191
  msg = f'Element in date `highlight={d}` does not match requirements (YYYY-MM-DD) or `today`.'
187
192
  logger.error(msg)
188
193
  raise ValueError(msg)
189
-
190
- if not (0 <= self.weekstart <= 6):
194
+
195
+ if not (0 <= self.weekstart <= 6): # noqa PLR2004
191
196
  msg = f'Date `weekstart` must be int from 0 (Sunday) - 6 (Saturday), got {self.weekstart}'
192
197
  logger.error(msg)
193
198
  raise ValueError(msg)
@@ -197,17 +202,17 @@ class CalendarDate:
197
202
  #-----------------------------------------------------------------------------------------------------------------------
198
203
  ColorSchemeLiteral = Literal['YlOrRd', 'Blues', 'Greens', 'Greys', 'Oranges', 'Reds', 'Purples', 'Turbo', 'Viridis',
199
204
  'Inferno', 'Magma', 'Plasma', 'Cividis', 'Warm', 'Cool', 'Cubehelix', 'BuPu', 'GnBu',
200
- 'OrRd', 'PuBuGn', 'PuBu', 'PuRd', 'RdPu', 'YlGnBu', 'YlGn', 'YlOrBr', 'YlOrRd',
201
- 'BrBG', 'PRGn', 'PiYG', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral',
202
- 'Rainbow', 'Sinebow', None]
203
- ColorInterpolateLiteral = Literal['rgb', 'hsl', 'lab', 'hcl', None]
205
+ 'OrRd', 'PuBuGn', 'PuBu', 'PuRd', 'RdPu', 'YlGnBu', 'YlGn', 'YlOrBr', 'BrBG', 'PRGn',
206
+ 'PiYG', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'Rainbow', 'Sinebow',
207
+ None] # noqa PYI061
208
+ ColorInterpolateLiteral = Literal['rgb', 'hsl', 'lab', 'hcl', None] # noqa PYI061
204
209
  ColorTypeLiteral = Literal['ordinal', 'linear', 'pow', 'sqrt', 'log', 'symlog', 'categorical', 'sequential', 'cyclical',
205
210
  'threshold', 'quantile', 'quantize', 'diverging', 'diverging-log', 'diverging-pow',
206
211
  'diverging-sqrt', 'diverging-symlog']
207
212
  @dataclass
208
213
  class CalendarColor:
209
214
  """ Define the calendar color scheme
210
-
215
+
211
216
  See https://cal-heatmap.com/docs/options/scale for all options and what they mean
212
217
  """
213
218
  domain: list[float]
@@ -216,10 +221,10 @@ class CalendarColor:
216
221
  interpolate: ColorInterpolateLiteral = None
217
222
  type: ColorTypeLiteral = 'linear'
218
223
 
219
-
220
224
  def __post_init__(self):
225
+ """ Check data input """
221
226
  if self.scheme is not None and self.range is not None:
222
- msg = f'Color `scheme` and `range` provided. Use either one or the other.'
227
+ msg = 'Color `scheme` and `range` provided. Use either one or the other.'
223
228
  logger.error(msg)
224
229
  raise ValueError(msg)
225
230
 
@@ -242,12 +247,16 @@ class CalendarColor:
242
247
  #-----------------------------------------------------------------------------------------------------------------------
243
248
  @dataclass
244
249
  class CalendarMisc:
250
+ """ Define the calendar miscellaneous configuration
251
+
252
+ See https://cal-heatmap.com/docs/options for all options and what they mean
253
+ """
245
254
  id: str = 'hm-' + str(uuid.uuid4())
246
255
  range: int = 12
247
256
  animation_duration: int = 200
248
257
  vertical_orientation: bool = False
249
258
  tooltip_enabled: bool = True
250
- tooltip_function: str = "predefined:date+value"
259
+ tooltip_function: str = 'predefined:date+value'
251
260
  tooltip_precision: int | None = None
252
261
  legend_width: int = 300
253
262
  legend_label: str | None = None
@@ -255,6 +264,7 @@ class CalendarMisc:
255
264
  legend_font_weight: int = 450
256
265
 
257
266
  def __post_init__(self):
267
+ """ Check data input """
258
268
  for k in ['animation_duration', 'range', 'legend_width', 'legend_font_size', 'legend_font_weight']:
259
269
  value = self.__dict__[k]
260
270
  if value < 0:
@@ -263,12 +273,12 @@ class CalendarMisc:
263
273
  raise ValueError(msg)
264
274
 
265
275
  if self.tooltip_function.startswith('predefined:'):
266
-
276
+
267
277
  if self.tooltip_precision is not None:
268
278
  value = f'${{(value ?? 0).toFixed({self.tooltip_precision})}}'
269
279
  else:
270
280
  value = '${value ?? 0}'
271
-
281
+
272
282
  options = self.tooltip_function.removeprefix('predefined:')
273
283
  defined_options = ['value', 'date+value', 'datetime+value']
274
284
  if options == 'value':
@@ -278,7 +288,8 @@ class CalendarMisc:
278
288
  elif options == 'datetime+value':
279
289
  tfn = f"${{dayjsDate.format('YYYY-MM-DD HH:mm:ss')}} : {value}"
280
290
  else:
281
- msg = f'Unknown predefined tooltip function {self.tooltip_function}. Use one of `predefined:{defined_options}`'
291
+ msg = (f'Unknown predefined tooltip function {self.tooltip_function}.'
292
+ f' Use one of `predefined:{defined_options}`')
282
293
  logger.error(msg)
283
294
  raise RuntimeError(msg)
284
295
 
@@ -291,11 +302,13 @@ DataTypeLiteral = Literal['date', 'timestamp']
291
302
 
292
303
  @dataclass
293
304
  class CalendarData:
305
+ """ The actual calendar data to be displayed """
294
306
  x: list[str]
295
307
  y: list[float]
296
308
  type: DataTypeLiteral = 'date'
297
309
 
298
310
  def __post_init__(self):
311
+ """ Check data input """
299
312
  checks = [
300
313
  ['type', DataTypeLiteral],
301
314
  ]
@@ -309,12 +322,14 @@ class CalendarData:
309
322
  raise ValueError(msg)
310
323
 
311
324
  def get_table(self):
312
- return [i for i in zip(self.x, self.y)]
313
-
325
+ """ Return the dataset as a 2d list"""
326
+ return list(zip(self.x, self.y, strict=True))
327
+
314
328
  #-----------------------------------------------------------------------------------------------------------------------
315
329
  # region HEATMAP
316
- #-----------------------------------------------------------------------------------------------------------------------
330
+ #-----------------------------------------------------------------------------------------------------------------------
317
331
  class CalendarHeatmap:
332
+ """ A calendar heatmap to display in the report """
318
333
  def __init__(
319
334
  self,
320
335
  misc: CalendarMisc,
@@ -324,6 +339,16 @@ class CalendarHeatmap:
324
339
  date: CalendarDate,
325
340
  color: CalendarColor,
326
341
  ):
342
+ """ Create a new heatmap instance
343
+
344
+ Args:
345
+ misc (CalendarMisc): The miscellaneous configuration for the calendar heatmap
346
+ domain (CalendarDomain): The domain configuration for the calendar heatmap
347
+ label (CalendarLabel): The label configuration for the calendar heatmap
348
+ subdomain: (CalendarSubDomain): The subdomain configuration for the calendar heatmap
349
+ date (CalendarDate): The date configuration for the calendar heatmap
350
+ color (CalendarColor): The color configuration for the calendar heatmap
351
+ """
327
352
  self.misc: CalendarMisc = misc
328
353
  self.domain: CalendarDomain = domain
329
354
  self.label: CalendarLabel = label
@@ -337,6 +362,11 @@ class CalendarHeatmap:
337
362
 
338
363
  @classmethod
339
364
  def from_config_file(cls, cfg_file: str | Path):
365
+ """ Load a heatmap from a config file
366
+
367
+ Args:
368
+ cfg_file (str | Path): The path to the config file
369
+ """
340
370
  cfg = Path(cfg_file)
341
371
  with open(cfg, 'r') as f:
342
372
  hm_cfg = yaml.load(f, Loader=yaml.FullLoader)
@@ -347,14 +377,17 @@ class CalendarHeatmap:
347
377
  color = CalendarColor(**hm_cfg['color'])
348
378
  misc = CalendarMisc(**hm_cfg['misc'])
349
379
  return cls(misc, domain, label, subdomain, date, color)
350
-
351
- def set_color_range(self, range: list[float]):
352
- self.color.domain = range
380
+
381
+ def set_color_range(self, rng: list[float]):
382
+ """ Set the color range of the heatmap """
383
+ self.color.domain = rng
353
384
 
354
385
  def set_data(self, x: list[str], y: list[str], data_type: str='date'):
386
+ """ Update the data to be plotted in the heatmap """
355
387
  self.data = CalendarData(x, y, data_type)
356
-
388
+
357
389
  def make_button_div(self, direction: str):
390
+ """ Make the previous/next button div """
358
391
  if direction == 'next':
359
392
  template = self.jinja_env.get_template('heatmap/button_next.html')
360
393
  elif direction == 'previous':
@@ -363,18 +396,20 @@ class CalendarHeatmap:
363
396
  msg = 'Unknown button direction `{direction}`. Use either `next` or `previous`.'
364
397
  logger.error(msg)
365
398
  raise RuntimeError(msg)
366
-
399
+
367
400
  html = template.render()
368
401
  return html
369
402
 
370
403
  def make_heatmap_div(self):
404
+ """ Make the heatmap div section """
371
405
  template = self.jinja_env.get_template('heatmap/heatmap.html')
372
406
  html = template.render(
373
407
  misc_id=self.misc.id,
374
408
  )
375
409
  return html
376
-
410
+
377
411
  def make_legend_div(self):
412
+ """ Make the heatmap html legend section """
378
413
  template = self.jinja_env.get_template('heatmap/legend.html')
379
414
  html = template.render(
380
415
  misc_id=self.misc.id,
@@ -382,14 +417,15 @@ class CalendarHeatmap:
382
417
  return html
383
418
 
384
419
  def make_heatmap_script(self):
420
+ """ Make the heatmap html script section """
385
421
  template = self.jinja_env.get_template('heatmap/script.html')
386
422
  html = template.render(
387
- # MISC
423
+ # MISC
388
424
  misc_id=self.misc.id,
389
425
  misc_range=self.misc.range,
390
426
  misc_animation_duration=self.misc.animation_duration,
391
427
  misc_vertical_orientation=str(self.misc.vertical_orientation).lower(),
392
-
428
+
393
429
  # TOOLTIP
394
430
  tooltip_enabled=str(self.misc.tooltip_enabled).lower(),
395
431
  tooltip_text=self.misc.tooltip_function,
@@ -431,7 +467,7 @@ class CalendarHeatmap:
431
467
  date_weekstart=self.date.weekstart,
432
468
  date_timezone=self.date.timezone,
433
469
 
434
- # COLOR
470
+ # COLOR
435
471
  color_scheme=self.color.scheme,
436
472
  color_range=self.color.range,
437
473
  color_interpolate=self.color.interpolate,
@@ -445,6 +481,7 @@ class CalendarHeatmap:
445
481
  return html
446
482
 
447
483
  def make_heatmap(self):
484
+ """ Create the actual heatmap html """
448
485
  template = self.jinja_env.get_template('heatmap/base.html')
449
486
  html = template.render(
450
487
  heatmap_div=self.make_heatmap_div(),
@@ -456,6 +493,7 @@ class CalendarHeatmap:
456
493
  return html
457
494
 
458
495
  def make_css(self):
496
+ """ Create the CSS file for the heatmap """
459
497
  template = self.jinja_env.get_template('css/heatmap.css')
460
498
  return template.render(
461
499
  misc_id=self.misc.id,
@@ -19,7 +19,7 @@ from .heatmap import (
19
19
  CalendarDate,
20
20
  CalendarColor,
21
21
  CalendarMisc,
22
- CalendarHeatmap
22
+ CalendarHeatmap,
23
23
  )
24
24
 
25
25
  #----------------------------------------------------------------------------------------------------------------------#
@@ -249,7 +249,7 @@ class ReportPage:
249
249
  msg = f'Unknown alignment `{alignment}`. Use one of {valid_alignments}'
250
250
  logger.error(msg)
251
251
  raise RuntimeError(msg)
252
-
252
+
253
253
  alignment = default(alignment, 'center')
254
254
  text_color = default(text_color, self.color_map.get_default_color('section_title.text'))
255
255
  bar_color = default(bar_color, self.color_map.get_default_color('section_title.bars'))
@@ -261,6 +261,40 @@ class ReportPage:
261
261
  alignment=alignment)
262
262
  self._update_content(html)
263
263
 
264
+ # region Dictionary
265
+ # -----------------
266
+ def add_dictionary(
267
+ self,
268
+ dictionary: dict[str,str] | list[list[str]],
269
+ key_color: str | None=None,
270
+ key_width: str | None=None,
271
+ value_color: str | None=None,
272
+ ):
273
+ """ Add a dictionary as key value list to the report
274
+
275
+ Args:
276
+ dictionary (dict[str, str] | list[list[str]]): The dictionary that should be added to the report. The key
277
+ will be right aligned in one column, whereas the values will be left aligned in the next column. If you
278
+ pass a list make sure it is a list of lists with 2 elements. The first will be assumed to be the key
279
+ the second represents the value
280
+ key_color (str | None): The text color for the keys
281
+ key_width (str | None): The width of the key column. Use any of the html specifications, e.g. 150px etc.
282
+ value_color (str | None): The color of the value text
283
+ """
284
+ key_width = default(key_width, None)
285
+ key_color = default(key_color, self.color_map.get_default_color('dictionary.key'))
286
+ value_color = default(value_color, self.color_map.get_default_color('dictionary.value'))
287
+
288
+ if isinstance(dictionary, dict):
289
+ dictionary = [[k,v] for k,v in dictionary.items()]
290
+
291
+ template = self.jinja_env.get_template('elements/dictionary.html')
292
+ html = template.render(dictionary=dictionary,
293
+ key_color=key_color,
294
+ key_width=key_width,
295
+ value_color=value_color)
296
+ self._update_content(html)
297
+
264
298
  # region Table
265
299
  # ------------
266
300
  def add_table(
@@ -383,8 +417,23 @@ class ReportPage:
383
417
  color: CalendarColor | None = None,
384
418
  x: list[str] | None = None,
385
419
  y: list[float | int] | None = None,
386
- custom_config: dict[str, Any] | None = None
387
- ):
420
+ custom_config: dict[str, Any] | None = None,
421
+ ):
422
+ """ Add a calendar heatmap to the report
423
+
424
+ Args:
425
+ misc (CalendarMisc | None): The miscellaneous configuration for the calendar heatmap
426
+ domain (CalendarDomain | None): The domain configuration for the calendar heatmap
427
+ label (CalendarLabel | None): The label configuration for the calendar heatmap
428
+ subdomain: (CalendarSubDomain | None): The subdomain configuration for the calendar heatmap
429
+ date (CalendarDate | None): The date configuration for the calendar heatmap
430
+ color (CalendarColor | None): The color configuration for the calendar heatmap
431
+ x (list[str] | None): The time data array, e.g., dates
432
+ y (list[float | int] | None): The corresponding value array
433
+ custom_config (dict[str, Any] | None): A custom config dict to easily override any defaults set in the
434
+ report. See the documentation for the calendar heatmap for an in depth discussion of the available
435
+ parameters
436
+ """
388
437
  # Incorporate the custom config
389
438
  if custom_config is None:
390
439
  custom_config = {}
@@ -406,13 +455,13 @@ class ReportPage:
406
455
  msg = 'Both x and y need to be set'
407
456
  logger.error(msg)
408
457
  raise RuntimeError(msg)
409
-
458
+
410
459
  # Add to report
411
460
  hm = CalendarHeatmap(misc, domain, label, subdomain, date, color)
412
461
  hm.set_data(x=x, y=y)
413
462
 
414
463
  # We need to keep track of the heatmaps so we can properly add the style sheets later
415
- self.cal_heatmaps.append(hm)
464
+ self.cal_heatmaps.append(hm)
416
465
 
417
466
  self.add_custom_html_code(hm.make_heatmap())
418
467
 
@@ -563,7 +612,7 @@ class ReportPage:
563
612
  # ------------
564
613
  def open_cards(self, cards_type: Literal['group', 'deck', 'columns']):
565
614
  """ Open a new cards environment
566
-
615
+
567
616
  Args:
568
617
  cards_type (Literal['group', 'deck', 'columns']): Define the layout of how cards are grouped together
569
618
  """
@@ -603,7 +652,7 @@ class ReportPage:
603
652
  footer_text_color: str | None = None,
604
653
  footer_background_color: str | None = None,
605
654
  footer_border_color: str | None = None,
606
- max_width: str='18rem'
655
+ max_width: str='18rem',
607
656
  ):
608
657
  """ Add a section title to the page
609
658
 
@@ -637,18 +686,26 @@ class ReportPage:
637
686
  'image_source': image,
638
687
  'max_width': max_width,
639
688
 
640
- 'background_color': default(background_color, self.color_map.get_default_color('card.background')),
641
- 'border_color': default(border_color, self.color_map.get_default_color('card.border')),
689
+ 'background_color': default(background_color,
690
+ self.color_map.get_default_color('card.background')),
691
+ 'border_color': default(border_color,
692
+ self.color_map.get_default_color('card.border')),
642
693
 
643
694
  'header_text': header_text,
644
- 'header_text_color': default(header_text_color, self.color_map.get_default_color('card.header_text')),
645
- 'header_background_color': default(header_background_color, self.color_map.get_default_color('card.header_background')),
646
- 'header_border_color': default(header_border_color, self.color_map.get_default_color('card.header_border')),
695
+ 'header_text_color': default(header_text_color,
696
+ self.color_map.get_default_color('card.header_text')),
697
+ 'header_background_color': default(header_background_color,
698
+ self.color_map.get_default_color('card.header_background')),
699
+ 'header_border_color': default(header_border_color,
700
+ self.color_map.get_default_color('card.header_border')),
647
701
 
648
702
  'footer_text': footer_text,
649
- 'footer_text_color': default(footer_text_color, self.color_map.get_default_color('card.footer_text')),
650
- 'footer_background_color': default(footer_background_color, self.color_map.get_default_color('card.footer_background')),
651
- 'footer_border_color': default(footer_border_color, self.color_map.get_default_color('card.footer_border')),
703
+ 'footer_text_color': default(footer_text_color,
704
+ self.color_map.get_default_color('card.footer_text')),
705
+ 'footer_background_color': default(footer_background_color,
706
+ self.color_map.get_default_color('card.footer_background')),
707
+ 'footer_border_color': default(footer_border_color,
708
+ self.color_map.get_default_color('card.footer_border')),
652
709
  }
653
710
  card_cfg = json.dumps(card_cfg)
654
711
 
@@ -14,10 +14,8 @@ from .heatmap import (
14
14
  CalendarLabel,
15
15
  CalendarSubDomain,
16
16
  CalendarDate,
17
- CalendarData,
18
17
  CalendarColor,
19
18
  CalendarMisc,
20
- CalendarHeatmap
21
19
  )
22
20
 
23
21
  #----------------------------------------------------------------------------------------------------------------------#
@@ -130,7 +128,7 @@ class Report:
130
128
  for page in self.pages.values():
131
129
  for hm in page.cal_heatmaps:
132
130
  css.append(hm.make_css())
133
-
131
+
134
132
  css_file = output_dir / 'heatmap' / 'custom_cal_heatmap.css'
135
133
  css_file.write_text('\n'.join(css))
136
134
 
@@ -152,7 +150,7 @@ class Report:
152
150
 
153
151
  def dump(self, *, overwrite: bool=False):
154
152
  """ Save the report to file and add the CSS files
155
-
153
+
156
154
  Args:
157
155
  overwrite (bool): If true an existing report dir will be deleted first. Use with caution, there are
158
156
  no additional checks and balances implemented. If you rm your root its on you.
@@ -241,7 +239,7 @@ class Report:
241
239
  chosen side
242
240
  """
243
241
  self.current.add_section_title(title_text, text_color, bar_color, alignment)
244
-
242
+
245
243
  # Miscellaneous elements
246
244
  # ----------------------
247
245
  def add_text(self, text: str, color: Optional[str]=None, align: Optional[Literal['left', 'center', 'right']]=None):
@@ -328,7 +326,21 @@ class Report:
328
326
  y: list[float | int] | None = None,
329
327
  custom_config: dict[str, Any] | None = None,
330
328
  ):
331
- """ Add a new calendar heatmap to the current page"""
329
+ """ Add a calendar heatmap to the report
330
+
331
+ Args:
332
+ misc (CalendarMisc | None): The miscellaneous configuration for the calendar heatmap
333
+ domain (CalendarDomain | None): The domain configuration for the calendar heatmap
334
+ label (CalendarLabel | None): The label configuration for the calendar heatmap
335
+ subdomain: (CalendarSubDomain | None): The subdomain configuration for the calendar heatmap
336
+ date (CalendarDate | None): The date configuration for the calendar heatmap
337
+ color (CalendarColor | None): The color configuration for the calendar heatmap
338
+ x (list[str] | None): The time data array, e.g., dates
339
+ y (list[float | int] | None): The corresponding value array
340
+ custom_config (dict[str, Any] | None): A custom config dict to easily override any defaults set in the
341
+ report. See the documentation for the calendar heatmap for an in depth discussion of the available
342
+ parameters
343
+ """
332
344
  self.current.add_calendar_heatmap(
333
345
  misc=misc,
334
346
  domain=domain,
@@ -349,6 +361,24 @@ class Report:
349
361
  """
350
362
  self.current.add_list(list_items)
351
363
 
364
+ def add_dictionary(
365
+ self,
366
+ dictionary: dict[str,str] | list[list[str]],
367
+ key_color: str | None=None,
368
+ key_width: str | None=None,
369
+ value_color: str | None=None,
370
+ ):
371
+ """ Add a dictionary as key value list to the report
372
+
373
+ Args:
374
+ dictionary (dict[str, str] | list[list[str]]): The dictionary that should be added to the report. The key
375
+ will be right aligned in one column, whereas the values will be left aligned in the next column
376
+ key_color (str | None): The text color for the keys
377
+ key_width (str | None): The width of the key column. Use any of the html specifications, e.g. 150px etc.
378
+ value_color (str | None): The color of the value text
379
+ """
380
+ self.current.add_dictionary(dictionary, key_color, key_width, value_color)
381
+
352
382
  def add_image(self,
353
383
  image_source: Union[str, Path],
354
384
  remove_source: Optional[bool]=None,
@@ -454,7 +484,7 @@ class Report:
454
484
  footer_text_color: str | None = None,
455
485
  footer_background_color: str | None = None,
456
486
  footer_border_color: str | None = None,
457
- max_width: str='18rem'
487
+ max_width: str='18rem',
458
488
  ):
459
489
  """ Add a section title to the page
460
490
 
@@ -0,0 +1,10 @@
1
+ <div class="container mb-3">
2
+ <table style="border-collapse: collapse;">
3
+ {%- for item in dictionary %}
4
+ <tr>
5
+ <td class="text-{{ key_color }}" style="text-align: right; vertical-align: top"{% if key_width is not none %} width="{{ key_width }}"{% endif %}><strong>{{ item[0] }}</strong></td>
6
+ <td class="text-{{ value_color}}" style="padding-left: 1em">{{ item[1] }}</td>
7
+ </tr>
8
+ {%- endfor %}
9
+ </table>
10
+ </div>
@@ -107,12 +107,9 @@ class ColorMap:
107
107
  Returns:
108
108
  str - The color to be used
109
109
  """
110
- if field.startswith('section_title'):
111
- sub_field = field.removeprefix('section_title.')
112
- color = self.config['default_color_profiles'][self.color_mode]['section_title'][sub_field]
113
- elif field.startswith('card'):
114
- sub_field = field.removeprefix('card.')
115
- color = self.config['default_color_profiles'][self.color_mode]['card'][sub_field]
110
+ split_fields = field.split('.')
111
+ if split_fields[0] in ['section_title', 'card', 'dictionary']:
112
+ color = self.config['default_color_profiles'][self.color_mode][split_fields[0]][split_fields[1]]
116
113
  elif self.text_color is None or field not in ['text', 'table', 'report_ball']:
117
114
  color = self.config['default_color_profiles'][self.color_mode].get(field)
118
115
  else:
@@ -1,20 +0,0 @@
1
- from pathlib import Path
2
-
3
- # Make the package dir available in the module
4
- PACKAGE_DIR = Path(__file__).parent
5
- PACKAGE_NAME = PACKAGE_DIR.name
6
- CONFIG_DIR = PACKAGE_DIR / 'configs'
7
- TEMPLATE_DIR = PACKAGE_DIR / 'templates'
8
- SCRIPTS_DIR = PACKAGE_DIR / 'css_and_scripts'
9
-
10
- from .report import Report as Report #noqa: E402
11
- from .heatmap import (
12
- CalendarDomain,
13
- CalendarLabel,
14
- CalendarSubDomain,
15
- CalendarDate,
16
- CalendarData,
17
- CalendarColor,
18
- CalendarMisc,
19
- CalendarHeatmap
20
- )
File without changes
File without changes