rephorm 1.0.1__py3-none-any.whl

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 (73) hide show
  1. rephorm/__init__.py +19 -0
  2. rephorm/decorators/__init__.py +0 -0
  3. rephorm/decorators/settings_validation.py +49 -0
  4. rephorm/dict/__init__.py +1 -0
  5. rephorm/dict/colors.py +22 -0
  6. rephorm/dict/legend_positions.py +136 -0
  7. rephorm/dict/styles.py +206 -0
  8. rephorm/dict/update_layout.py +129 -0
  9. rephorm/dict/update_traces.py +90 -0
  10. rephorm/object_mappers/__init__.py +0 -0
  11. rephorm/object_mappers/_globals.py +16 -0
  12. rephorm/object_mappers/_utilities/__init__.py +0 -0
  13. rephorm/object_mappers/_utilities/chart_mapper_utility.py +34 -0
  14. rephorm/object_mappers/_utilities/grid_mapper_utility.py +11 -0
  15. rephorm/object_mappers/_utilities/table_mapper_utility.py +59 -0
  16. rephorm/object_mappers/chapter_mapper.py +82 -0
  17. rephorm/object_mappers/chart_mapper.py +120 -0
  18. rephorm/object_mappers/chart_series_mapper.py +52 -0
  19. rephorm/object_mappers/grid_mapper.py +106 -0
  20. rephorm/object_mappers/page_break_mapper.py +8 -0
  21. rephorm/object_mappers/report_mapper.py +76 -0
  22. rephorm/object_mappers/table_mapper.py +191 -0
  23. rephorm/object_mappers/table_section_mapper.py +41 -0
  24. rephorm/object_mappers/table_series_mapper.py +79 -0
  25. rephorm/object_mappers/text_mapper.py +36 -0
  26. rephorm/object_params/__init__.py +0 -0
  27. rephorm/object_params/settings.py +184 -0
  28. rephorm/objects/__init__.py +0 -0
  29. rephorm/objects/_utilities/__init__.py +0 -0
  30. rephorm/objects/_utilities/settings_container.py +8 -0
  31. rephorm/objects/chapter.py +44 -0
  32. rephorm/objects/chart.py +70 -0
  33. rephorm/objects/chart_series.py +40 -0
  34. rephorm/objects/footnote.py +13 -0
  35. rephorm/objects/grid.py +57 -0
  36. rephorm/objects/page_break.py +13 -0
  37. rephorm/objects/report.py +196 -0
  38. rephorm/objects/table.py +76 -0
  39. rephorm/objects/table_section.py +48 -0
  40. rephorm/objects/table_series.py +36 -0
  41. rephorm/objects/text.py +35 -0
  42. rephorm/utility/PDF.py +80 -0
  43. rephorm/utility/__init__.py +0 -0
  44. rephorm/utility/add_style_prefix.py +30 -0
  45. rephorm/utility/fonts/OpenSans-Bold.ttf +0 -0
  46. rephorm/utility/fonts/OpenSans-BoldItalic.ttf +0 -0
  47. rephorm/utility/fonts/OpenSans-Italic.ttf +0 -0
  48. rephorm/utility/fonts/OpenSans.ttf +0 -0
  49. rephorm/utility/fonts/__init__.py +0 -0
  50. rephorm/utility/fonts/font_loading.py +74 -0
  51. rephorm/utility/is_set.py +9 -0
  52. rephorm/utility/merge/__init__.py +0 -0
  53. rephorm/utility/merge/chart_properties_manager.py +80 -0
  54. rephorm/utility/merge/merge_settings.py +134 -0
  55. rephorm/utility/merge/merge_styles.py +79 -0
  56. rephorm/utility/merge/pdf_merger.py +29 -0
  57. rephorm/utility/report/__init__.py +0 -0
  58. rephorm/utility/report/add_footnotes.py +46 -0
  59. rephorm/utility/report/cleanup_utility.py +19 -0
  60. rephorm/utility/report/footnotes_counter.py +15 -0
  61. rephorm/utility/report/image_utility.py +14 -0
  62. rephorm/utility/report/layout_utility.py +59 -0
  63. rephorm/utility/report/range_utility.py +94 -0
  64. rephorm/utility/report/report_utility.py +93 -0
  65. rephorm/utility/report/resolve_color.py +39 -0
  66. rephorm/utility/report/table_utility.py +71 -0
  67. rephorm/utility/report/title_utility.py +49 -0
  68. rephorm/utility/unit_converter.py +12 -0
  69. rephorm-1.0.1.dist-info/METADATA +41 -0
  70. rephorm-1.0.1.dist-info/RECORD +73 -0
  71. rephorm-1.0.1.dist-info/WHEEL +5 -0
  72. rephorm-1.0.1.dist-info/licenses/LICENSE +21 -0
  73. rephorm-1.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,40 @@
1
+ from irispie import Series as IrisSeries
2
+
3
+ from rephorm.object_mappers.chart_series_mapper import ChartSeriesMapper
4
+ from rephorm.objects._utilities.settings_container import SettingsContainer
5
+ from rephorm.decorators.settings_validation import validate_kwargs
6
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
7
+
8
+
9
+ class ChartSeries:
10
+
11
+ @validate_kwargs
12
+ def __init__( self, data: IrisSeries = None, **kwargs):
13
+ """
14
+ :param data (IrisSeries, Required): Data for the chart series.
15
+ :key yaxis (str, Optional): Y-axis position for the chart series. Defaults to "left".
16
+ :key span (ir.Span): Span of the chart series.
17
+ :key show_legend (bool): to show/hide the legend for this data series.
18
+ :key legend (tuple): Specifies the legend labels for the series. For a single series, use ("Label",).
19
+ For multivariate series, provide multiple labels like ("Label 1", "Label 2", ...).
20
+ :key highlight (ir.Span): Span of the highlight.
21
+ :key series_type (str): Type of series to display - "line" for line charts, "bar" for bar charts,
22
+ "bar_contribution" for contribution bar charts; use "line" with markers_mode="markers" for marker-based charts.
23
+ :key markers_mode (str): Display style for data points - "lines+markers" (lines with symbols/dots),
24
+ "lines" (lines only), or "markers" (symbols/dots only).
25
+ :key marker_symbol (str): Symbol used for markers in the series. e.g., "asterisk".
26
+ :key update_traces (Dict): Traces configuration for the chart series. (Plotly specific) todo: check if this is used
27
+ :key styles (Dict): Styles dictionary for additional customization (for details refer to report object).
28
+ """
29
+
30
+ self.data = data
31
+ self.settings = SettingsContainer(**kwargs)
32
+
33
+ if hasattr(self.settings, "styles"):
34
+ self.settings.styles = add_prefix_to_styles("chart.series", self.settings.styles)
35
+
36
+ def _get_mapper(self):
37
+ return ChartSeriesMapper(self)
38
+
39
+ def __repr__(self):
40
+ return f"{type(self).__name__}"
@@ -0,0 +1,13 @@
1
+ class Footnote:
2
+ # ----------------------------------
3
+ # Class initiator
4
+ # ----------------------------------
5
+
6
+ def __init__(
7
+ self,
8
+ text: str = "",
9
+ ):
10
+ self.TEXT = text
11
+
12
+ def __repr__(self):
13
+ return f"{type(self).__name__}"
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+ import copy
3
+ from typing import Text, Optional, Union, List
4
+
5
+ from rephorm.objects.table import Table
6
+ from rephorm.objects.text import Text
7
+ from rephorm.objects.page_break import PageBreak
8
+ from rephorm.objects.chart import Chart
9
+ from rephorm.object_mappers.grid_mapper import GridMapper
10
+ from rephorm.objects._utilities.settings_container import SettingsContainer
11
+ from rephorm.decorators.settings_validation import validate_kwargs
12
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
13
+
14
+
15
+ class Grid:
16
+
17
+ @validate_kwargs
18
+ def __init__(self, title: str = "", footnotes: Optional[List[str]] = None, **kwargs):
19
+ """
20
+ :param title: (str) Title of the grid.
21
+ :param footnotes: (Tuple(str)) Footnotes for the title.
22
+ :key ncol (int): Number of columns in the grid.
23
+ :key nrow (int): Number of rows in the grid.
24
+ :key layout (Dict): layout configuration for the chart (see documentation).
25
+ :key styles (Dict): Styles dict (refer to documentation).
26
+ """
27
+ self.title = title
28
+ self.CHILDREN = []
29
+ self.footnotes = footnotes
30
+ self.settings = SettingsContainer(**kwargs)
31
+ if hasattr(self.settings, "styles"):
32
+ self.settings.styles = add_prefix_to_styles("grid", self.settings.styles)
33
+
34
+ def add(
35
+ self,
36
+ grid_child : Union[Grid, Table, Text, PageBreak, Chart] = None,
37
+ ):
38
+
39
+ #12/10/2024: We should have checks in the objects and not object mappers
40
+ # We would also not have problem with imports here, because this module itself is within OBJECTS
41
+ # Todo: Update latest docs, explaining it is not possible to nest grids within grids, and the reason why.
42
+ if grid_child is not None and isinstance(grid_child, (Table, Text, PageBreak, Chart)):
43
+ # grid_child._on_add_callback()
44
+ copy_grid_child = copy.deepcopy(grid_child)
45
+ self.CHILDREN.append(copy_grid_child)
46
+ else: raise Exception("Grid: grid child of wrong type or None")
47
+
48
+
49
+ def _get_mapper(self):
50
+ return GridMapper(self)
51
+
52
+ def __repr__(self):
53
+ return f"{type(self).__name__}"
54
+
55
+ def _on_add_callback(self):
56
+ pass
57
+ # This would be called by whoever is adding the grid object.
@@ -0,0 +1,13 @@
1
+ from rephorm.object_mappers.page_break_mapper import PageBreakMapper
2
+
3
+
4
+ class PageBreak:
5
+
6
+ def __init__(self, ):
7
+ pass
8
+
9
+ def _get_mapper(self):
10
+ return PageBreakMapper(self)
11
+
12
+ def __repr__(self):
13
+ return f"{type(self).__name__}"
@@ -0,0 +1,196 @@
1
+ import copy
2
+ from typing import Optional, Dict, Text, Union
3
+
4
+ from rephorm.objects.grid import Grid
5
+ from rephorm.objects.table import Table
6
+ from rephorm.objects.text import Text
7
+ from rephorm.objects.page_break import PageBreak
8
+ from rephorm.objects.chart import Chart
9
+ from rephorm.objects.chapter import Chapter
10
+
11
+ from rephorm.objects._utilities.settings_container import SettingsContainer
12
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
13
+ from rephorm.utility.fonts.font_loading import get_fonts, add_custom_fonts
14
+ from rephorm.object_mappers.report_mapper import ReportMapper
15
+ from rephorm.utility.PDF import PDF
16
+ from rephorm.utility.merge.merge_settings import merge_settings
17
+ from rephorm.decorators.settings_validation import validate_kwargs
18
+
19
+
20
+ class Report:
21
+ @validate_kwargs
22
+ # Todo: delete report name from here, it is not needed since you can specify it on output method call
23
+ def __init__(self, title: str = "", subtitle: str = "", load_fonts: Optional[list[Dict[str, str]]] = None, abstract: str="", **kwargs):
24
+ # Todo: consider moving `load_fonts` to `kwargs` (Currently, it would be hard to validate entry/value if we move to kwargs, bcs of it's complex value type)
25
+ # Todo: add new setting `show_title_page=True` to control whether to create the front page or not (and make sure the header creation is aware of this parameter)
26
+ """
27
+ **Params:**
28
+ - ``title`` (str): Title of the report. Placed in the front page.
29
+ - ``subtitle`` (str): Subtitle of the report. Placed in the front page after the title.
30
+ - ``load_fonts`` (list[Dict[str, str]]): Fonts to load for the report.
31
+ For ``load_fonts`` you need to specify font_family, font_style and font_path as dictionary.
32
+
33
+ **Kwargs:**
34
+ - ``orientation`` (str): Orientation of the report, e.g., 'P' for Portrait, 'L' for Landscape.
35
+ - ``unit`` (str): Measurement unit for the report, e.g., 'pt', 'mm', 'cm'.
36
+ - ``format`` (str): Paper format for the report, e.g., 'A4', 'Letter'.
37
+ - ``logo`` (str): Path to the logo file for the report.
38
+ - ``styles`` (Dict): Styles dictionary for additional customization. See below for keys.
39
+
40
+ **Styles Dictionary Keys:**
41
+
42
+ **Table**
43
+ *title*
44
+ - ``table.title.font_style`` (str)
45
+ - ``table.title.font_family`` (str)
46
+ - ``table.title.font_color`` (str)
47
+ - ``table.title.font_size`` (float)
48
+
49
+ *heading*
50
+ - ``table.heading.font_style`` (str)
51
+ - ``table.heading.font_family`` (str)
52
+ - ``table.heading.font_color`` (str)
53
+ - ``table.heading.font_size`` (float)
54
+ - ``table.heading.highlight_color`` (str): color name
55
+
56
+ *series*
57
+ - ``table.series.font_style`` (str)
58
+ - ``table.series.font_family`` (str)
59
+ - ``table.series.font_color`` (str)
60
+ - ``table.series.font_size`` (float)
61
+ - ``table.series.highlight_color`` (str): color name
62
+
63
+ **section**
64
+ - ``table.section.font_family`` (str)
65
+ - ``table.section.font_color`` (str)
66
+ - ``table.section.font_size`` (float)
67
+ - ``table.section.highlight_color`` (str): color name
68
+
69
+ **Chart:**
70
+ **general**
71
+ - ``chart.highlight_color`` (str)
72
+ - ``chart.grid_color`` (str)
73
+ - ``chart.bg_color`` (str): Background color of the chart.
74
+ - ``chart.chart_border_color`` (str): Border color of the chart. If border is applied
75
+ **legend**
76
+ - ``chart.legend.border_width`` (int):
77
+ - ``chart.legend.bg_color`` (str):
78
+ - ``chart.legend.font_style`` (str):
79
+ - ``chart.legend.font_family`` (str):
80
+ - ``chart.legend.font_color`` (str):
81
+ - ``chart.legend.font_size`` (float):
82
+
83
+ **title**
84
+ - ``chart.title.font_style`` (str)
85
+ - ``chart.title.font_family`` (str)
86
+ - ``chart.title.font_color`` (str)
87
+ - ``chart.title.font_size`` (float)
88
+
89
+ **x_axis:**
90
+ *ticks:*
91
+ - ``chart.x_axis.ticks.font_size`` (float)
92
+ - ``chart.x_axis.ticks.font_color`` (str)
93
+ - ``chart.x_axis.ticks.font_family`` (str)
94
+ *label:*
95
+ - ``chart.x_axis.label.font_size`` (float)
96
+ - ``chart.x_axis.label.font_color`` (str)
97
+ - ``chart.x_axis.label.font_family`` (str)
98
+
99
+ **y_axis:**
100
+ *ticks:*
101
+ - ``chart.y_axis.ticks.font_size`` (float)
102
+ - ``chart.y_axis.ticks.font_color`` (str)
103
+ - ``chart.y_axis.ticks.font_family`` (str)
104
+ *label:*
105
+ - ``chart.y_axis.label.font_size`` (float)
106
+ - ``chart.y_axis.label.font_color`` (str)
107
+ - ``chart.y_axis.label.font_family`` (str)
108
+
109
+ **series:**
110
+ - ``chart.series.bar_edge_color`` (str): Border color for bars in bar charts, default black.
111
+ - ``chart.series.bar_edge_width`` (int): Border width for bars in bar charts.
112
+ - ``chart.series.bar_face_color`` (str): Fill color for bars in bar charts, default green.
113
+ - ``chart.series.line_width`` (int): Line width, default 1.
114
+ - ``chart.series.line_style`` (str): 'solid', 'dot', 'dash', 'longdash', 'dashdot', 'longdashdot'.
115
+ - ``chart.series.line_color`` (str): Color of the line.
116
+ - ``chart.series.marker_color`` (str): Color of the marker symbol.
117
+ - ``chart.series.marker_size`` (int): Size of the marker symbol.
118
+ - ``chart.series.marker_width`` (int): Marker width default is 0; some, like asterisks, require a width of 1.
119
+
120
+ **Report:**
121
+ - ``report.font_style`` (str)
122
+ - ``report.font_family`` (str)
123
+ - ``report.font_color`` (str)
124
+ - ``report.font_size`` (float)
125
+ - ``report.page_margin_left`` (int)
126
+ - ``report.page_margin_top`` (int)
127
+
128
+ **Text:**
129
+ - ``text.font_style`` (str): as string: "B", "I", "BI"
130
+ - ``text.font_family`` (str)
131
+ - ``text.font_size`` (float)
132
+
133
+ **Chapter:**
134
+ - ``chapter.font_style`` (str)
135
+ - ``chapter.font_family`` (str)
136
+ - ``chapter.font_size`` (float)
137
+
138
+ :key kwargs (any): You can provide any key-value pair to customize settings. If Object A contains Object B, settings for Object B can be defined in Object A and will apply to all instances of Object B, unless overridden for a specific instance
139
+ """
140
+
141
+ self.CHILDREN = []
142
+ self.title = title
143
+ self.subtitle = subtitle
144
+ self.load_fonts = get_fonts(load_fonts)
145
+ self.abstract = abstract
146
+ self.settings = SettingsContainer(**kwargs)
147
+
148
+ if hasattr(self.settings, "styles"):
149
+ self.settings.styles = add_prefix_to_styles("report", self.settings.styles)
150
+
151
+ merge_settings(self, self.settings.__dict__)
152
+
153
+ def add(self, report_child : Union[Grid, Table, Text, PageBreak, Chart, Chapter] = None):
154
+ if report_child is not None and isinstance(report_child, (Grid, Table, Text, PageBreak, Chart, Chapter)):
155
+ copy_report_child = copy.deepcopy(report_child)
156
+ self.CHILDREN.append(copy_report_child)
157
+ merge_settings(self, self.settings.__dict__)
158
+ else: raise Exception("Report: report child of wrong type or None")
159
+
160
+ # TODO: Output needs to know about mapper, so regarding circularity, extracting output would help us.
161
+ # Should be part of report mapper
162
+ def output(self, file_name: str = "report", cleanup: bool = True):
163
+ """
164
+ :args file_name (str): name of the report output file
165
+ :args cleanup (bool): if True, figures will be kept within /tmp directory
166
+ """
167
+
168
+ pdf = PDF(
169
+ orientation = self.settings.orientation,
170
+ unit = self.settings.unit,
171
+ format = self.settings.format,
172
+ report_styles=self.settings.styles["report"],
173
+ report_title=self.title,
174
+ )
175
+
176
+ add_custom_fonts(pdf, self.load_fonts)
177
+
178
+ pdf.set_margins(left=self.settings.styles["report"]["page_margin_left"],
179
+ top=self.settings.styles["report"]["page_margin_top"])
180
+
181
+ reportMapper = ReportMapper(pdf)
182
+
183
+ reportMapper.compile(self, file_name, cleanup)
184
+
185
+ def set_front_page(self, title: str = "", subtitle: str = "", abstract: str="", logo: Optional[str] = None):
186
+ if bool(title):
187
+ self.title = title
188
+ if bool(subtitle):
189
+ self.subtitle = subtitle
190
+ if bool(abstract):
191
+ self.abstract = abstract
192
+ if bool(logo):
193
+ self.settings.logo = logo
194
+
195
+
196
+
@@ -0,0 +1,76 @@
1
+ import copy
2
+ from typing import Optional, List, Union
3
+
4
+ from rephorm.objects.table_section import TableSection
5
+ from rephorm.objects.table_series import TableSeries
6
+ from rephorm.object_mappers.table_mapper import TableMapper
7
+ from rephorm.objects._utilities.settings_container import SettingsContainer
8
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
9
+ from rephorm.utility.report.range_utility import get_span, get_highlight
10
+ from rephorm.decorators.settings_validation import validate_kwargs
11
+ from rephorm.utility.report.table_utility import get_table_highlight
12
+
13
+
14
+ class Table:
15
+
16
+ @validate_kwargs
17
+ def __init__(self, title: str = "", footnotes: Optional[List[str]] = None, width: int = None, **kwargs):
18
+ """
19
+ :param title: Title of the table.
20
+ :param footnotes: Footnotes for the title.
21
+ :param width (int): The maximum width allowed for rendering the table.
22
+ :key span (ir.Span, Required): Span of the table.
23
+ :key highlight (ir.Span): Span of the highlight.
24
+ :key show_units (bool): Display/hide units in the table.
25
+ :key frequency (str): Frequency of the data in the table.
26
+ :key decimal_precision (int): Sets number of decimal places after the decimal point.
27
+ :key compare_style (str): Comparison style for the table series. "diff", "pct".
28
+ :key layout (Dict): Layout configuration for the table (Plotly specific).
29
+ :key styles (Dict): Styles dictionary for additional customization (for details refer to report object).
30
+ """
31
+ self.CHILDREN = []
32
+ self.footnotes = footnotes
33
+ self.title = title
34
+ self.width = width
35
+ self.settings = SettingsContainer(**kwargs)
36
+
37
+ if hasattr(self.settings, "styles"):
38
+ self.settings.styles = add_prefix_to_styles("table", self.settings.styles)
39
+
40
+
41
+ def add(self, table_child: Union[TableSeries, TableSection] = None,):
42
+
43
+ self._on_add_callback()
44
+
45
+ if table_child is not None and isinstance(table_child, (TableSeries, TableSection)):
46
+ copy_table_child = copy.deepcopy(table_child)
47
+ self.CHILDREN.append(copy_table_child)
48
+ else:
49
+ raise Exception("Table: Table child of wrong type or None")
50
+
51
+ def _get_mapper(self):
52
+ return TableMapper(self)
53
+
54
+ def _get_span(self):
55
+ return get_span(self)
56
+
57
+ def _get_highlight(self):
58
+ return get_highlight(self)
59
+
60
+ def _get_table_highlight(self):
61
+ return get_table_highlight(self)
62
+
63
+ # Because sometimes we may want to print the name of the object
64
+ def __repr__(self):
65
+ return f"{type(self).__name__}"
66
+
67
+ def _on_add_callback(self):
68
+
69
+ if hasattr(self.settings, 'styles'):
70
+ highlight_color = self.settings.styles.get("table.highlight_color")
71
+
72
+ if highlight_color:
73
+ # Set default values for various highlight_color properties
74
+ self.settings.styles.setdefault("table.series.highlight_color", highlight_color)
75
+ self.settings.styles.setdefault("table.heading.highlight_color", highlight_color)
76
+ self.settings.styles.setdefault("table.section.highlight_color", highlight_color)
@@ -0,0 +1,48 @@
1
+ import copy
2
+
3
+ from rephorm.object_mappers.table_section_mapper import TableSectionMapper
4
+ from rephorm.objects.table_series import TableSeries
5
+ from rephorm.objects._utilities.settings_container import SettingsContainer
6
+ from rephorm.decorators.settings_validation import validate_kwargs
7
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
8
+
9
+
10
+ class TableSection:
11
+
12
+ @validate_kwargs
13
+ def __init__(self, title: str = None, **kwargs):
14
+ """
15
+ :param title: (str) Title of the table section.
16
+ :key styles (Dict): Styles dictionary for the table section (refer to documentation).
17
+ :key highlight (ir.Span): Span of the highlight in the table section. (applies to all table_series added within the section)
18
+ :key show_units (bool): To show/hide units for the table section. (applies to all table_series added within the section)
19
+ """
20
+ self.TITLE = title
21
+ self.CHILDREN = []
22
+ self.settings = SettingsContainer(**kwargs)
23
+ if hasattr(self.settings, "styles"):
24
+ self.settings.styles = add_prefix_to_styles("table.section", self.settings.styles)
25
+
26
+ def add (self, table_section_child: TableSeries = None):
27
+
28
+ self._on_add_callback()
29
+
30
+ if table_section_child is not None and isinstance(table_section_child, TableSeries):
31
+ copy_table_section_child = copy.deepcopy(table_section_child)
32
+ self.CHILDREN.append(copy_table_section_child)
33
+ else:
34
+ raise Exception("TableSection: Table Section child of wrong type or None")
35
+
36
+ def _get_mapper(self):
37
+ return TableSectionMapper(self)
38
+
39
+ def __repr__(self):
40
+ return f"{type(self).__name__}"
41
+
42
+ def _on_add_callback(self):
43
+ if hasattr(self.settings, 'styles'):
44
+ highlight_color = self.settings.styles.get("table.section.highlight_color")
45
+ if highlight_color:
46
+ self.settings.styles.setdefault("table.series.highlight_color", highlight_color)
47
+ self.settings.styles.setdefault("table.section.highlight_color", highlight_color)
48
+
@@ -0,0 +1,36 @@
1
+ from irispie import Series as IrisSeries
2
+
3
+ from rephorm.object_mappers.table_series_mapper import TableSeriesMapper
4
+ from rephorm.objects._utilities.settings_container import SettingsContainer
5
+ from rephorm.decorators.settings_validation import validate_kwargs
6
+ from rephorm.utility.add_style_prefix import add_prefix_to_styles
7
+
8
+
9
+ class TableSeries:
10
+
11
+ @validate_kwargs
12
+ def __init__(self, title: str = None, unit: str = None, data: IrisSeries = None, **kwargs):
13
+ """
14
+ :param data: (IrisSeries, Required) Data for the table series.
15
+ :param title: (str) Name of the table series / row.
16
+ :param unit: (str) The text value for the "units" column in this specific row of the table series.
17
+ This defines what will be displayed as the unit for the corresponding row.
18
+ :key span (ir.Span): Span of the table series.
19
+ :key highlight (ir.Span): Span of the highlight in the table series.
20
+ :key compare_style (str): Comparison style for the table series. "diff", "pct".
21
+ :key decimal_precision (int): Sets number of decimal places after the decimal point.
22
+ :key comparison_series (bool): Whether the series should be displayed as comparison series.
23
+ :key styles (Dict): Styles dictionary for additional customization (for details refer to report object).
24
+ """
25
+ self.data = data
26
+ self.title = title
27
+ self.unit = unit
28
+ self.settings = SettingsContainer(**kwargs)
29
+ if hasattr(self.settings, "styles"):
30
+ self.settings.styles = add_prefix_to_styles("table.series", self.settings.styles)
31
+
32
+ def _get_mapper(self):
33
+ return TableSeriesMapper(self)
34
+
35
+ def __repr__(self):
36
+ return f"{type(self).__name__}"
@@ -0,0 +1,35 @@
1
+ from typing import Literal
2
+ from rephorm.decorators.settings_validation import validate_kwargs
3
+ from rephorm.object_mappers.text_mapper import TextMapper
4
+ from rephorm.objects._utilities.settings_container import SettingsContainer
5
+
6
+
7
+ class Text:
8
+ # ----------------------------------
9
+ # Class initiator
10
+ # ----------------------------------
11
+ @validate_kwargs
12
+ def __init__(
13
+ self,
14
+ title: str = "",
15
+ text: str = "",
16
+ align: Literal["x", "c", "l", "j", "r"] = "j",
17
+ markdown = False,
18
+ **kwargs
19
+ ):
20
+ self.title = title
21
+ self.TEXT = text
22
+ self.align = align
23
+ self.markdown = markdown
24
+ self.settings = SettingsContainer(**kwargs)
25
+
26
+ def _get_mapper(self):
27
+ return TextMapper(self)
28
+
29
+ def __repr__(self):
30
+ return f"{type(self).__name__}"
31
+
32
+
33
+
34
+
35
+
rephorm/utility/PDF.py ADDED
@@ -0,0 +1,80 @@
1
+ from datetime import datetime
2
+
3
+ from fpdf import FPDF
4
+
5
+ """
6
+ This subclass extends FPDF to include customized header and footer functionality,
7
+ making it easier to apply consistent formatting across all pages of the document.
8
+ By overriding the `header` and `footer` methods, we ensure that these elements
9
+ are automatically added to each page without manual intervention.
10
+ """
11
+
12
+ class PDF(FPDF):
13
+ def __init__(self, report_styles = None, report_title = None, *args, **kwargs):
14
+ super().__init__(*args, **kwargs)
15
+ self.report_styles = report_styles
16
+ self.report_title = report_title
17
+ self.timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
18
+
19
+ def header(self):
20
+ header_height = self.report_styles["header"]["height"]
21
+ self.set_font(family=self.report_styles["font_family"], size=8)
22
+ # Todo: here, H should be set to header height
23
+ # Set absolute position for the header
24
+ # We need to skip first cell (bcs of logo).
25
+ header_width = self.epw / 3
26
+ y = self.get_y()
27
+
28
+ layout = self._get_header_layout(self.page_no(), self.timestamp, self.report_title)
29
+
30
+ for item in layout:
31
+ x = self.l_margin + item["slot"] * header_width
32
+ self.set_xy(x, y)
33
+ self.cell(
34
+ w=header_width,
35
+ h=header_height,
36
+ text=item["text"],
37
+ align=item.get("align", "L"),
38
+ # border=1 #for debugging
39
+ )
40
+ # Todo: was easier to extract it like this, and in the future we could rewr this function
41
+ # further in order to specify exact positions of elements.
42
+ def _get_header_layout(self, page_no, timestamp, report_title):
43
+ layout = [
44
+ {
45
+ "id": "title",
46
+ "text": report_title,
47
+ "align": "L",
48
+ "slot": 0
49
+ },
50
+ {
51
+ "id": "page",
52
+ "text": f"Page {page_no}",
53
+ "align": "C",
54
+ "slot": 1
55
+ },
56
+ {
57
+ "id": "timestamp",
58
+ "text": timestamp,
59
+ "align": "R",
60
+ "slot": 2
61
+ }
62
+ ]
63
+
64
+ # Todo: modify this, once we have show_title_page option on report object that controls whether the
65
+ # title page shown or not.
66
+ if page_no == 1:
67
+ layout = [{
68
+ "id": "timestamp",
69
+ "text": timestamp,
70
+ "align": "R",
71
+ "slot": 2
72
+ }]
73
+
74
+ return layout
75
+
76
+
77
+
78
+
79
+
80
+
File without changes
@@ -0,0 +1,30 @@
1
+ # The 'prefixes' tuple contains the current object name prefixes.
2
+ # When defining styles, avoid using object names as keys.
3
+ # For example, a structure like Grid: { chart: { } }
4
+ # will cause issues, because chart is also an object name.
5
+
6
+ prefixes = (
7
+ "chart.",
8
+ "grid.",
9
+ "report.",
10
+ "chapter.",
11
+ "table.",
12
+ "text.",
13
+ )
14
+
15
+ def flatten_dict(d, parent_key='', sep='.'):
16
+ items = []
17
+ for k, v in d.items():
18
+ new_key = f"{parent_key}{sep}{k}" if parent_key else k
19
+ if isinstance(v, dict):
20
+ items.extend(flatten_dict(v, new_key, sep=sep).items())
21
+ else:
22
+ items.append((new_key, v))
23
+ return dict(items)
24
+
25
+ def add_prefix_to_styles(obj_name, styles):
26
+ flatten_styles = flatten_dict(styles)
27
+ return {
28
+ key if key.startswith(prefixes) else f"{obj_name}.{key}": value
29
+ for key, value in flatten_styles.items()
30
+ }
Binary file
Binary file
File without changes