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.
- rephorm/__init__.py +19 -0
- rephorm/decorators/__init__.py +0 -0
- rephorm/decorators/settings_validation.py +49 -0
- rephorm/dict/__init__.py +1 -0
- rephorm/dict/colors.py +22 -0
- rephorm/dict/legend_positions.py +136 -0
- rephorm/dict/styles.py +206 -0
- rephorm/dict/update_layout.py +129 -0
- rephorm/dict/update_traces.py +90 -0
- rephorm/object_mappers/__init__.py +0 -0
- rephorm/object_mappers/_globals.py +16 -0
- rephorm/object_mappers/_utilities/__init__.py +0 -0
- rephorm/object_mappers/_utilities/chart_mapper_utility.py +34 -0
- rephorm/object_mappers/_utilities/grid_mapper_utility.py +11 -0
- rephorm/object_mappers/_utilities/table_mapper_utility.py +59 -0
- rephorm/object_mappers/chapter_mapper.py +82 -0
- rephorm/object_mappers/chart_mapper.py +120 -0
- rephorm/object_mappers/chart_series_mapper.py +52 -0
- rephorm/object_mappers/grid_mapper.py +106 -0
- rephorm/object_mappers/page_break_mapper.py +8 -0
- rephorm/object_mappers/report_mapper.py +76 -0
- rephorm/object_mappers/table_mapper.py +191 -0
- rephorm/object_mappers/table_section_mapper.py +41 -0
- rephorm/object_mappers/table_series_mapper.py +79 -0
- rephorm/object_mappers/text_mapper.py +36 -0
- rephorm/object_params/__init__.py +0 -0
- rephorm/object_params/settings.py +184 -0
- rephorm/objects/__init__.py +0 -0
- rephorm/objects/_utilities/__init__.py +0 -0
- rephorm/objects/_utilities/settings_container.py +8 -0
- rephorm/objects/chapter.py +44 -0
- rephorm/objects/chart.py +70 -0
- rephorm/objects/chart_series.py +40 -0
- rephorm/objects/footnote.py +13 -0
- rephorm/objects/grid.py +57 -0
- rephorm/objects/page_break.py +13 -0
- rephorm/objects/report.py +196 -0
- rephorm/objects/table.py +76 -0
- rephorm/objects/table_section.py +48 -0
- rephorm/objects/table_series.py +36 -0
- rephorm/objects/text.py +35 -0
- rephorm/utility/PDF.py +80 -0
- rephorm/utility/__init__.py +0 -0
- rephorm/utility/add_style_prefix.py +30 -0
- rephorm/utility/fonts/OpenSans-Bold.ttf +0 -0
- rephorm/utility/fonts/OpenSans-BoldItalic.ttf +0 -0
- rephorm/utility/fonts/OpenSans-Italic.ttf +0 -0
- rephorm/utility/fonts/OpenSans.ttf +0 -0
- rephorm/utility/fonts/__init__.py +0 -0
- rephorm/utility/fonts/font_loading.py +74 -0
- rephorm/utility/is_set.py +9 -0
- rephorm/utility/merge/__init__.py +0 -0
- rephorm/utility/merge/chart_properties_manager.py +80 -0
- rephorm/utility/merge/merge_settings.py +134 -0
- rephorm/utility/merge/merge_styles.py +79 -0
- rephorm/utility/merge/pdf_merger.py +29 -0
- rephorm/utility/report/__init__.py +0 -0
- rephorm/utility/report/add_footnotes.py +46 -0
- rephorm/utility/report/cleanup_utility.py +19 -0
- rephorm/utility/report/footnotes_counter.py +15 -0
- rephorm/utility/report/image_utility.py +14 -0
- rephorm/utility/report/layout_utility.py +59 -0
- rephorm/utility/report/range_utility.py +94 -0
- rephorm/utility/report/report_utility.py +93 -0
- rephorm/utility/report/resolve_color.py +39 -0
- rephorm/utility/report/table_utility.py +71 -0
- rephorm/utility/report/title_utility.py +49 -0
- rephorm/utility/unit_converter.py +12 -0
- rephorm-1.0.1.dist-info/METADATA +41 -0
- rephorm-1.0.1.dist-info/RECORD +73 -0
- rephorm-1.0.1.dist-info/WHEEL +5 -0
- rephorm-1.0.1.dist-info/licenses/LICENSE +21 -0
- rephorm-1.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from fpdf import FontFace
|
|
2
|
+
|
|
3
|
+
from rephorm.object_mappers._utilities.table_mapper_utility import (
|
|
4
|
+
get_max_numerical_width,
|
|
5
|
+
get_max_width,
|
|
6
|
+
)
|
|
7
|
+
from rephorm.utility.report.add_footnotes import add_footnotes_at_bottom
|
|
8
|
+
from rephorm.utility.report.footnotes_counter import generate_footnote_numbers
|
|
9
|
+
from rephorm.utility.report.report_utility import get_page_dimensions
|
|
10
|
+
from rephorm.utility.report.resolve_color import resolve_color
|
|
11
|
+
from rephorm.utility.report.title_utility import add_title_with_references
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TableMapper:
|
|
15
|
+
def __init__(self, table):
|
|
16
|
+
self.table = table
|
|
17
|
+
|
|
18
|
+
def add_to_pdf(self, **kwargs):
|
|
19
|
+
|
|
20
|
+
pdf = kwargs["pdf"]
|
|
21
|
+
highlight = []
|
|
22
|
+
x = kwargs["x"]
|
|
23
|
+
w = kwargs["w"]
|
|
24
|
+
h = kwargs["h"]
|
|
25
|
+
y = kwargs["y"]
|
|
26
|
+
|
|
27
|
+
title_bottom_padding = self.table.settings.styles["report"]["title_bottom_padding"]
|
|
28
|
+
#Todo: Consider extracting context based on caller. So we do not need to deal with string checks. Discuss with Sergey.
|
|
29
|
+
context = kwargs.get("context", None)
|
|
30
|
+
custom_width = self.table.width
|
|
31
|
+
footer_top_padding = self.table.settings.styles["report"]["footer"]["top_padding"]
|
|
32
|
+
footer_top_margin = self.table.settings.styles["report"]["footer"]["top_margin"]
|
|
33
|
+
footer_last_y = kwargs["footer_last_y"] if "footer_last_y" in kwargs else None
|
|
34
|
+
|
|
35
|
+
# Todo: Consider replacing (full_width_grid) conditional based on grid row settings? if nrow and ncol = 1? Talk to Sergey why
|
|
36
|
+
full_width_grid = (w == pdf.epw)
|
|
37
|
+
is_multi_cell_grid = context == "grid" and not full_width_grid
|
|
38
|
+
|
|
39
|
+
skip_cols = 1
|
|
40
|
+
headers = [""]
|
|
41
|
+
|
|
42
|
+
pdf.set_xy(x, y)
|
|
43
|
+
page_width, _ = get_page_dimensions(pdf)
|
|
44
|
+
|
|
45
|
+
num_col_width = get_max_numerical_width(
|
|
46
|
+
self.table,
|
|
47
|
+
pdf,
|
|
48
|
+
font_family=self.table.settings.styles["table"]["series"]["font_family"],
|
|
49
|
+
font_size=self.table.settings.styles["table"]["series"]["font_size"],
|
|
50
|
+
biggest_nr="10000",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
title_col_width = get_max_width(
|
|
54
|
+
self.table,
|
|
55
|
+
pdf,
|
|
56
|
+
font_family=self.table.settings.styles["table"]["series"]["font_family"],
|
|
57
|
+
font_size=self.table.settings.styles["table"]["series"]["font_size"],
|
|
58
|
+
parameter="title",
|
|
59
|
+
max_width=0,
|
|
60
|
+
)
|
|
61
|
+
unit_col_width = get_max_width(
|
|
62
|
+
self.table,
|
|
63
|
+
pdf,
|
|
64
|
+
font_family=self.table.settings.styles["table"]["series"]["font_family"],
|
|
65
|
+
font_size=self.table.settings.styles["table"]["series"]["font_size"],
|
|
66
|
+
parameter="unit",
|
|
67
|
+
max_width=0,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if self.table.settings.highlight is not None:
|
|
71
|
+
highlight = self.table._get_table_highlight()
|
|
72
|
+
|
|
73
|
+
span = self.table._get_span()
|
|
74
|
+
|
|
75
|
+
if self.table.settings.show_units:
|
|
76
|
+
skip_cols += 1
|
|
77
|
+
|
|
78
|
+
if self.table.settings.show_units:
|
|
79
|
+
headers = headers + ["Units"]
|
|
80
|
+
|
|
81
|
+
heading_data = [
|
|
82
|
+
headers + list(span.to_compact_strings()),
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
heading = heading_data[0][skip_cols:]
|
|
86
|
+
|
|
87
|
+
num_col_padding = 10
|
|
88
|
+
|
|
89
|
+
col_widths = (
|
|
90
|
+
[
|
|
91
|
+
title_col_width + 10
|
|
92
|
+
] # TODO: first column width | We introduce param for this. the name of param: title_col_width
|
|
93
|
+
+ [unit_col_width + 0]
|
|
94
|
+
* self.table.settings.show_units # TODO: second column width | We introduce param for this. the name of param: unit_col_width
|
|
95
|
+
+ [num_col_width + num_col_padding] * len(heading) # numerical columns width
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# we use effective page width here (excluding margins)
|
|
99
|
+
page_width = pdf.epw
|
|
100
|
+
|
|
101
|
+
natural_table_width = sum(col_widths) + 10
|
|
102
|
+
|
|
103
|
+
if natural_table_width > page_width:
|
|
104
|
+
table_width = page_width
|
|
105
|
+
else:
|
|
106
|
+
if is_multi_cell_grid:
|
|
107
|
+
table_width = w
|
|
108
|
+
else:
|
|
109
|
+
table_width = natural_table_width
|
|
110
|
+
|
|
111
|
+
text_align = ["L"] * skip_cols + ["R"] * len(heading)
|
|
112
|
+
|
|
113
|
+
h_data = headers + heading
|
|
114
|
+
|
|
115
|
+
# ----------------- Set Table Title -----------------
|
|
116
|
+
|
|
117
|
+
footnote_references = generate_footnote_numbers(self.table.footnotes)
|
|
118
|
+
y = add_title_with_references(
|
|
119
|
+
pdf=pdf,
|
|
120
|
+
title=self.table.title,
|
|
121
|
+
references=footnote_references,
|
|
122
|
+
font_family=self.table.settings.styles["table"]["title"]["font_family"],
|
|
123
|
+
font_size=self.table.settings.styles["table"]["title"]["font_size"],
|
|
124
|
+
font_style=self.table.settings.styles["table"]["title"]["font_style"],
|
|
125
|
+
title_y=y,
|
|
126
|
+
cell_width=w,
|
|
127
|
+
)
|
|
128
|
+
if bool(self.table.footnotes):
|
|
129
|
+
footer_last_y = add_footnotes_at_bottom(
|
|
130
|
+
pdf,
|
|
131
|
+
self.table.footnotes,
|
|
132
|
+
footnote_references,
|
|
133
|
+
last_y=footer_last_y,
|
|
134
|
+
font_family=self.table.settings.styles["footnotes"]["font_family"],
|
|
135
|
+
font_size=self.table.settings.styles["footnotes"]["font_size"],
|
|
136
|
+
font_style=self.table.settings.styles["footnotes"]["font_style"],
|
|
137
|
+
footer_padding=footer_top_padding,
|
|
138
|
+
footer_top_margin=footer_top_margin,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Todo: Consider: title padding could be part of add_title_with_ref function, so we only pass here Y after calling that func)
|
|
142
|
+
pdf.set_y(y + title_bottom_padding)
|
|
143
|
+
|
|
144
|
+
# ----------------- Set Table Header -----------------
|
|
145
|
+
with pdf.table(
|
|
146
|
+
width=custom_width if custom_width else table_width,
|
|
147
|
+
col_widths=col_widths,
|
|
148
|
+
text_align=text_align,
|
|
149
|
+
align="l" if is_multi_cell_grid else "c",
|
|
150
|
+
# TODO: ADD border settings to styles for table
|
|
151
|
+
borders_layout="SINGLE_TOP_LINE",
|
|
152
|
+
# line height sets the height of rows, 1,4 is multiplier. (We cannot precisely extract height of the text)
|
|
153
|
+
# Todo: Consider extracting this to param/styles, like inrow_offset...
|
|
154
|
+
line_height=self.table.settings.styles["table"]["series"]["font_size"]
|
|
155
|
+
* 1.4,
|
|
156
|
+
) as pdf_table:
|
|
157
|
+
|
|
158
|
+
heading_row = pdf_table.row()
|
|
159
|
+
header_color = resolve_color(
|
|
160
|
+
self.table.settings.styles["table"]["heading"]["highlight_color"]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
for i, header in enumerate(h_data):
|
|
164
|
+
# Todo: Remove Grey and etc params from table object and move them to styles
|
|
165
|
+
style = FontFace(
|
|
166
|
+
emphasis=self.table.settings.styles["table"]["heading"][
|
|
167
|
+
"font_style"
|
|
168
|
+
],
|
|
169
|
+
size_pt=self.table.settings.styles["table"]["heading"]["font_size"],
|
|
170
|
+
fill_color=header_color if i in highlight else "#FFFFFF00",
|
|
171
|
+
family=self.table.settings.styles["table"]["heading"][
|
|
172
|
+
"font_family"
|
|
173
|
+
],
|
|
174
|
+
color=resolve_color(
|
|
175
|
+
self.table.settings.styles["table"]["heading"]["font_color"]
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
heading_row.cell(header, style=style)
|
|
180
|
+
|
|
181
|
+
# ----------------- Set Table Rows -----------------
|
|
182
|
+
for item in self.table.CHILDREN:
|
|
183
|
+
mapper = item._get_mapper()
|
|
184
|
+
mapper.add_to_pdf(
|
|
185
|
+
span=span,
|
|
186
|
+
pdf_table=pdf_table,
|
|
187
|
+
highlight=highlight,
|
|
188
|
+
n_cols=len(col_widths),
|
|
189
|
+
show_units=self.table.settings.show_units,
|
|
190
|
+
footer_last_y=footer_last_y,
|
|
191
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from fpdf import FontFace
|
|
2
|
+
from rephorm.utility.report.resolve_color import resolve_color
|
|
3
|
+
|
|
4
|
+
class TableSectionMapper:
|
|
5
|
+
def __init__(self, table_section):
|
|
6
|
+
self.table_section = table_section
|
|
7
|
+
|
|
8
|
+
def add_to_pdf(self, **kwargs):
|
|
9
|
+
|
|
10
|
+
n_cols = kwargs["n_cols"]
|
|
11
|
+
highlight = kwargs["highlight"]
|
|
12
|
+
pdf_table = kwargs["pdf_table"]
|
|
13
|
+
span = kwargs["span"]
|
|
14
|
+
title_row = pdf_table.row()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
for i in range(n_cols):
|
|
18
|
+
|
|
19
|
+
color = resolve_color(self.table_section.settings.styles["table"]["section"]["highlight_color"])
|
|
20
|
+
|
|
21
|
+
style = FontFace(
|
|
22
|
+
emphasis= self.table_section.settings.styles["table"]["section"]["font_style"],
|
|
23
|
+
size_pt=self.table_section.settings.styles["table"]["section"]["font_size"],
|
|
24
|
+
fill_color= color if i in highlight else "#FFFFFF00",
|
|
25
|
+
family=self.table_section.settings.styles["table"]["section"]["font_family"],
|
|
26
|
+
color=self.table_section.settings.styles["table"]["section"]["font_color"],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
content = self.table_section.TITLE if i == 0 else ""
|
|
30
|
+
title_row.cell(content, style=style)
|
|
31
|
+
|
|
32
|
+
for item in self.table_section.CHILDREN:
|
|
33
|
+
mapper = item._get_mapper()
|
|
34
|
+
mapper.add_to_pdf(span=span, pdf_table=pdf_table, highlight=highlight, show_units=self.table_section.settings.show_units)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
from fpdf import FontFace
|
|
3
|
+
|
|
4
|
+
from rephorm.utility.report.resolve_color import resolve_color
|
|
5
|
+
from rephorm.utility.report.table_utility import prepare_row
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TableSeriesMapper:
|
|
9
|
+
def __init__(self, series):
|
|
10
|
+
self.series = series
|
|
11
|
+
|
|
12
|
+
def add_to_pdf(self, **kwargs):
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
highlight = kwargs["highlight"]
|
|
16
|
+
pdf_table = kwargs["pdf_table"]
|
|
17
|
+
span = kwargs["span"]
|
|
18
|
+
show_units = kwargs["show_units"]
|
|
19
|
+
#fpdf = kwargs["fpdf"]#todo: add to kwargs
|
|
20
|
+
# last_y = kwargs["footer_last_y"] # We get last Y from parent,
|
|
21
|
+
# so to add footnote to the bottom of the page right after parent footnotes.
|
|
22
|
+
|
|
23
|
+
# Get start page nr before adding row
|
|
24
|
+
# start_page_nr = fpdf.page_no()
|
|
25
|
+
|
|
26
|
+
# add row
|
|
27
|
+
element_row = pdf_table.row()
|
|
28
|
+
|
|
29
|
+
#get page number after adding row
|
|
30
|
+
# current_page_nr = fpdf.page_no()
|
|
31
|
+
|
|
32
|
+
# In case the first element is on the next page, we need to reset last_y to none,
|
|
33
|
+
# so that the add_footnotes_at_bottom function recalculates footnote positions
|
|
34
|
+
# if current_page_nr > start_page_nr:
|
|
35
|
+
# last_y = None
|
|
36
|
+
|
|
37
|
+
#Gen references here, pass it down to prep row and later to add_footnotes_. ...
|
|
38
|
+
# footnote_references = generate_footnote_numbers(self.series.footnotes)
|
|
39
|
+
|
|
40
|
+
row = prepare_row(
|
|
41
|
+
series=self.series,
|
|
42
|
+
compare_style=self.series.settings.compare_style,
|
|
43
|
+
span=span,
|
|
44
|
+
show_units=show_units,
|
|
45
|
+
unit=self.series.unit,
|
|
46
|
+
title=self.series.title,
|
|
47
|
+
decimal_precision=self.series.settings.decimal_precision,
|
|
48
|
+
footnote_references=None, # Todo: footnote_references
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# add_footnotes_at_bottom(footnotes=self.series.footnotes,
|
|
52
|
+
# last_y=last_y,)
|
|
53
|
+
|
|
54
|
+
color = resolve_color(self.series.settings.styles["table"]["series"]["highlight_color"])
|
|
55
|
+
|
|
56
|
+
# I think we use similar logic in couple places, look if it's the case and put in utility.
|
|
57
|
+
for i, cell in enumerate(row): # Process each cell in the row
|
|
58
|
+
|
|
59
|
+
style = FontFace(
|
|
60
|
+
family=self.series.settings.styles["table"]["series"]["font_family"],
|
|
61
|
+
emphasis=self.series.settings.styles["table"]["series"]["font_style"],
|
|
62
|
+
size_pt=self.series.settings.styles["table"]["series"]["font_size"],
|
|
63
|
+
fill_color= color if i in highlight else "#FFFFFF00",
|
|
64
|
+
color=resolve_color(self.series.settings.styles["table"]["series"]["font_color"]),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
68
|
+
|
|
69
|
+
if self.series.settings.comparison_series:
|
|
70
|
+
padding = (-self.series.settings.styles["table"]["series"]["font_size"]*0.6, 0, 5, 0)
|
|
71
|
+
style.size_pt *= 0.8 # Sets size of comparison series
|
|
72
|
+
else:
|
|
73
|
+
padding = (0, 0, 0, 0)
|
|
74
|
+
|
|
75
|
+
element_row.cell(
|
|
76
|
+
cell,
|
|
77
|
+
style=style,
|
|
78
|
+
padding = padding,
|
|
79
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from rephorm.utility.report.title_utility import add_title_with_references
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TextMapper:
|
|
5
|
+
def __init__(self, text):
|
|
6
|
+
self.text = text
|
|
7
|
+
|
|
8
|
+
def add_to_pdf(self, **kwargs):
|
|
9
|
+
pdf = kwargs["pdf"]
|
|
10
|
+
w = kwargs["w"]
|
|
11
|
+
y = kwargs["y"]
|
|
12
|
+
x = kwargs["x"]
|
|
13
|
+
|
|
14
|
+
family = self.text.settings.styles["text"]["title"]["font_family"]
|
|
15
|
+
size = self.text.settings.styles["text"]["title"]["font_size"]
|
|
16
|
+
style = self.text.settings.styles["text"]["title"]["font_style"]
|
|
17
|
+
|
|
18
|
+
# todo: check multipliers everywhere and use line height const for it.
|
|
19
|
+
line_height = 1.5 # todo: consider extracting to styles, once again.
|
|
20
|
+
|
|
21
|
+
y = add_title_with_references(pdf=pdf, title=self.text.title, references=None,
|
|
22
|
+
font_family=family, font_size=size, font_style=style, title_y=y)
|
|
23
|
+
|
|
24
|
+
pdf.set_font(family=self.text.settings.styles["text"]["font_family"],
|
|
25
|
+
size=self.text.settings.styles["text"]["font_size"],
|
|
26
|
+
style=self.text.settings.styles["text"]["font_style"])
|
|
27
|
+
|
|
28
|
+
pdf.set_xy(x, y)
|
|
29
|
+
|
|
30
|
+
pdf.multi_cell(
|
|
31
|
+
w=w,
|
|
32
|
+
h=self.text.settings.styles["text"]["font_size"] * line_height,
|
|
33
|
+
txt=self.text.TEXT,
|
|
34
|
+
align=self.text.align,
|
|
35
|
+
markdown=self.text.markdown,
|
|
36
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
import irispie as ir
|
|
3
|
+
|
|
4
|
+
from rephorm.dict.styles import default_styles
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
This Dictionary defines all possible parameters that can be applied to any object within the report.
|
|
8
|
+
|
|
9
|
+
- default_value - default value of the parameter
|
|
10
|
+
- type - for type checking | if type is None, then it is not being checked ("useful" in case of Complex data Types)
|
|
11
|
+
- ultimates - list of objects that should get/receive this parameter
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
object_params = {
|
|
15
|
+
"span": {
|
|
16
|
+
"default_value": ir.start >> ir.end,
|
|
17
|
+
"type": ir.Span,
|
|
18
|
+
"ultimates": ["Chart", "Table", "ChartSeries", "TableSeries"],
|
|
19
|
+
},
|
|
20
|
+
"highlight" : {
|
|
21
|
+
"default_value": None,
|
|
22
|
+
"type": ir.Span,
|
|
23
|
+
"ultimates": ["Chart", "Table", "TableSection", "TableSeries"],
|
|
24
|
+
},
|
|
25
|
+
"show_legend" : {
|
|
26
|
+
"default_value": True,
|
|
27
|
+
"type": bool,
|
|
28
|
+
"ultimates": ["ChartSeries"],
|
|
29
|
+
},
|
|
30
|
+
"yaxis": {
|
|
31
|
+
"default_value": "left",
|
|
32
|
+
"type": str,
|
|
33
|
+
"possible_values":
|
|
34
|
+
{"left", "right"},
|
|
35
|
+
"ultimates": ["ChartSeries"],
|
|
36
|
+
},
|
|
37
|
+
"update_traces" : { # todo: check if this is used
|
|
38
|
+
"default_value": None,
|
|
39
|
+
"type": Dict,
|
|
40
|
+
"ultimates": ["ChartSeries"],
|
|
41
|
+
},
|
|
42
|
+
"show_grid" : {
|
|
43
|
+
"default_value": True,
|
|
44
|
+
"type": bool,
|
|
45
|
+
"ultimates": ["Chart"],
|
|
46
|
+
},
|
|
47
|
+
"axis_border" : {
|
|
48
|
+
"default_value": False,
|
|
49
|
+
"type": bool,
|
|
50
|
+
"ultimates": ["Chart"],
|
|
51
|
+
},
|
|
52
|
+
"xaxis_title" : {
|
|
53
|
+
"default_value": "",
|
|
54
|
+
"type": str,
|
|
55
|
+
"ultimates": ["Chart"],
|
|
56
|
+
},
|
|
57
|
+
"yaxis_title" : {
|
|
58
|
+
"default_value": "",
|
|
59
|
+
"type": str,
|
|
60
|
+
"ultimates": ["Chart"],
|
|
61
|
+
},
|
|
62
|
+
"yaxis2_title" : {
|
|
63
|
+
"default_value": "",
|
|
64
|
+
"type": str,
|
|
65
|
+
"ultimates": ["Chart"],
|
|
66
|
+
},
|
|
67
|
+
"legend_orientation" : {
|
|
68
|
+
"default_value": "h",
|
|
69
|
+
"type": str,
|
|
70
|
+
"ultimates": ["Chart"],
|
|
71
|
+
},
|
|
72
|
+
"legend_position" : {
|
|
73
|
+
"default_value": "SO",
|
|
74
|
+
"type": str,
|
|
75
|
+
"possible_values":
|
|
76
|
+
{"N", "S", "E", "W",
|
|
77
|
+
"NE", "NW", "SE", "SW",
|
|
78
|
+
"NO", "SO", "EO", "WO",
|
|
79
|
+
"NEO", "NWO", "SEO", "SWO"},
|
|
80
|
+
"ultimates": ["Chart"],
|
|
81
|
+
},
|
|
82
|
+
"ncol" : {
|
|
83
|
+
"default_value": 2,
|
|
84
|
+
"type": int,
|
|
85
|
+
"ultimates": ["Grid"],
|
|
86
|
+
},
|
|
87
|
+
"nrow" : {
|
|
88
|
+
"default_value": 2,
|
|
89
|
+
"type": int,
|
|
90
|
+
"ultimates": ["Grid"],
|
|
91
|
+
},
|
|
92
|
+
"apply_report_layout": {
|
|
93
|
+
"default_value": False,
|
|
94
|
+
"type": bool,
|
|
95
|
+
"ultimates": ["Chart"],
|
|
96
|
+
},
|
|
97
|
+
"layout": {
|
|
98
|
+
"default_value": None,
|
|
99
|
+
"type": None,
|
|
100
|
+
"ultimates": ["Grid"],
|
|
101
|
+
},
|
|
102
|
+
"comparison_series" : {
|
|
103
|
+
"default_value": False,
|
|
104
|
+
"type": bool,
|
|
105
|
+
"ultimates": ["TableSeries"],
|
|
106
|
+
},
|
|
107
|
+
"series_type" : {
|
|
108
|
+
"default_value": "line",
|
|
109
|
+
"type": str,
|
|
110
|
+
"possible_values": {"line", "bar", "contribution_bar", "barcon", "conbar", "bar_stack", "bar_group", "bar_overlay", "bar_relative"},
|
|
111
|
+
"ultimates": ["Chart", "ChartSeries"],
|
|
112
|
+
},
|
|
113
|
+
"markers_mode" : {
|
|
114
|
+
"default_value": "lines",
|
|
115
|
+
"type": str,
|
|
116
|
+
"possible_values": {"lines+markers", "lines", "markers"},
|
|
117
|
+
"ultimates": ["ChartSeries"], # TEST IT: Chart was removed as Chart should not use it in any way
|
|
118
|
+
},
|
|
119
|
+
"legend" : {
|
|
120
|
+
"default_value": None,
|
|
121
|
+
"type": tuple,
|
|
122
|
+
"ultimates": ["Chart", "ChartSeries"],
|
|
123
|
+
},
|
|
124
|
+
"compare_style" : {
|
|
125
|
+
"default_value": "",
|
|
126
|
+
"type": str,
|
|
127
|
+
"ultimates": ["TableSeries"],
|
|
128
|
+
},
|
|
129
|
+
"marker_symbol" : {
|
|
130
|
+
"default_value": "asterisk",
|
|
131
|
+
"type": str,
|
|
132
|
+
"ultimates": ["Chart", "ChartSeries"],
|
|
133
|
+
},
|
|
134
|
+
"orientation" : {
|
|
135
|
+
"default_value": "P",
|
|
136
|
+
"type": str,
|
|
137
|
+
"ultimates": ["Report"],
|
|
138
|
+
},
|
|
139
|
+
"unit" : {
|
|
140
|
+
"default_value": "pt",
|
|
141
|
+
"type": str,
|
|
142
|
+
"ultimates": ["Report"],
|
|
143
|
+
},
|
|
144
|
+
"format" : {
|
|
145
|
+
"default_value": "A4",
|
|
146
|
+
"type": str,
|
|
147
|
+
"ultimates": ["Report"],
|
|
148
|
+
},
|
|
149
|
+
"decimal_precision" : {
|
|
150
|
+
"default_value": 1,
|
|
151
|
+
"type": int,
|
|
152
|
+
"ultimates": ["TableSeries"],
|
|
153
|
+
},
|
|
154
|
+
"show_units" : {
|
|
155
|
+
"default_value": True,
|
|
156
|
+
"type": bool,
|
|
157
|
+
"ultimates": ["TableSection", "Table"],
|
|
158
|
+
},
|
|
159
|
+
"zeroline" : {
|
|
160
|
+
"default_value": False,
|
|
161
|
+
"type": bool,
|
|
162
|
+
"ultimates": ["Chart"],
|
|
163
|
+
},
|
|
164
|
+
"legend_ncol" : {
|
|
165
|
+
"default_value": None,
|
|
166
|
+
"type": int,
|
|
167
|
+
"ultimates": ["Chart"],
|
|
168
|
+
},
|
|
169
|
+
"frequency" : {
|
|
170
|
+
"default_value": None,
|
|
171
|
+
"type": None, #Cant check this one
|
|
172
|
+
"ultimates": ["Table"],
|
|
173
|
+
},
|
|
174
|
+
"styles": {
|
|
175
|
+
"default_value": default_styles(),
|
|
176
|
+
"type": None, # TODO: change to Dict later on
|
|
177
|
+
"ultimates": ["Report", "Chapter", "TableSection", "ChartSeries", "TableSeries", "Table", "Chart", "Grid", "Text"],
|
|
178
|
+
},
|
|
179
|
+
"logo": {
|
|
180
|
+
"default_value": "",
|
|
181
|
+
"type": str,
|
|
182
|
+
"ultimates": ["Report"],
|
|
183
|
+
}
|
|
184
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
from typing import Optional, Union, List
|
|
3
|
+
|
|
4
|
+
from rephorm.object_mappers.chapter_mapper import ChapterMapper
|
|
5
|
+
from rephorm.objects.chart import Chart
|
|
6
|
+
from rephorm.objects.page_break import PageBreak
|
|
7
|
+
from rephorm.objects.grid import Grid
|
|
8
|
+
from rephorm.objects._utilities.settings_container import SettingsContainer
|
|
9
|
+
from rephorm.objects.table import Table
|
|
10
|
+
from rephorm.objects.text import Text
|
|
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 Chapter:
|
|
16
|
+
@validate_kwargs
|
|
17
|
+
def __init__(self, title: str = None, footnotes: Optional[List[str]] = None, **kwargs):
|
|
18
|
+
"""
|
|
19
|
+
:param title: (str) Title of the chapter.
|
|
20
|
+
:key styles (Dict): Styles dictionary for additional customization (for details refer to report object).
|
|
21
|
+
"""
|
|
22
|
+
self.title = title
|
|
23
|
+
self.footnotes = footnotes
|
|
24
|
+
self.CHILDREN = []
|
|
25
|
+
# if "styles" in kwargs:
|
|
26
|
+
# kwargs["styles"]=adjust_styles(kwargs["styles"], caller)
|
|
27
|
+
self.settings = SettingsContainer(**kwargs)
|
|
28
|
+
if hasattr(self.settings, "styles"):
|
|
29
|
+
self.settings.styles = add_prefix_to_styles("chapter", self.settings.styles)
|
|
30
|
+
|
|
31
|
+
def add(
|
|
32
|
+
self,
|
|
33
|
+
chapter_child: Union[Grid, Table, Text, PageBreak, Chart] = None,
|
|
34
|
+
):
|
|
35
|
+
if chapter_child is not None and isinstance(chapter_child, (Grid, Table, Text, PageBreak, Chart)):
|
|
36
|
+
copy_chapter_child = copy.deepcopy(chapter_child)
|
|
37
|
+
self.CHILDREN.append(copy_chapter_child)
|
|
38
|
+
else: raise Exception("Chapter: chapter child of wrong type or None")
|
|
39
|
+
|
|
40
|
+
def _get_mapper(self):
|
|
41
|
+
return ChapterMapper(self)
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return f"{type(self).__name__}"
|
rephorm/objects/chart.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import plotly
|
|
3
|
+
from rephorm.object_mappers.chart_mapper import ChartMapper
|
|
4
|
+
from rephorm.objects.chart_series import ChartSeries
|
|
5
|
+
from rephorm.objects._utilities.settings_container import SettingsContainer
|
|
6
|
+
from rephorm.utility.add_style_prefix import add_prefix_to_styles
|
|
7
|
+
|
|
8
|
+
from rephorm.utility.report.range_utility import get_span, get_highlight
|
|
9
|
+
from rephorm.decorators.settings_validation import validate_kwargs
|
|
10
|
+
|
|
11
|
+
class Chart:
|
|
12
|
+
|
|
13
|
+
@validate_kwargs
|
|
14
|
+
def __init__(self, title: str = None, figure: plotly.graph_objs.Figure = None, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
:args title (str): title of the chart
|
|
17
|
+
:args figure (plotly.graph_objs.Figure): Plotly figure object. If provided, it will plot the given figure object.
|
|
18
|
+
:args span (ir.Span): Span of the chart
|
|
19
|
+
:key apply_report_layout (bool): If True, applies the standard report layout to the provided figure (can only be used for a custom Plotly figure).
|
|
20
|
+
:key highlight (ir.Span): Span of the highlight.
|
|
21
|
+
:key show_legend (bool): to show/hide the legend.
|
|
22
|
+
:key show_grid (bool): to show/hide the grid.
|
|
23
|
+
:key axis_border (bool): enable/disable axis borders.
|
|
24
|
+
:key xaxis_title (str): The title for the X-axis.
|
|
25
|
+
:key yaxis_title (str): The title for the Y-axis.
|
|
26
|
+
:key yaxis2_title (str): The title for the second Y-axis.
|
|
27
|
+
:key legend_orientation (str): legend orientation. "v" or "h".
|
|
28
|
+
:key legend_position (str): Position of the legend. e.g. "SO" - South Outside. "S" - South Inside.
|
|
29
|
+
:key series_type (str): Type of series to display - "line" for line charts, "bar" for bar charts,
|
|
30
|
+
"bar_contribution" for contribution bar charts; use "line" with markers_mode="markers" for marker-based charts.
|
|
31
|
+
:key markers_mode (str): Display style for data points - "lines+markers" (lines with symbols/dots),
|
|
32
|
+
"lines" (lines only), or "markers" (symbols/dots only).
|
|
33
|
+
:key legend (tuple): Specifies the legend labels for the series. For multivariate series, provide multiple labels like ("Label 1", "Label 2", ...).
|
|
34
|
+
:key marker_symbol (str): Symbol name for markers, default is "asterisk".
|
|
35
|
+
:key zeroline (bool): If True, displays a horizontal line at Y=0 to help indicate the zero baseline.
|
|
36
|
+
:key styles (Dict): Styles dictionary for additional customization (for details refer to report object).
|
|
37
|
+
"""
|
|
38
|
+
self.CHILDREN = []
|
|
39
|
+
self.title = title
|
|
40
|
+
self.figure = figure
|
|
41
|
+
self.settings = SettingsContainer(**kwargs)
|
|
42
|
+
|
|
43
|
+
if hasattr(self.settings, "styles"):
|
|
44
|
+
self.settings.styles = add_prefix_to_styles("chart", self.settings.styles)
|
|
45
|
+
|
|
46
|
+
def add(
|
|
47
|
+
self,
|
|
48
|
+
chart_child: ChartSeries = None):
|
|
49
|
+
# Todo: add docstring later on.
|
|
50
|
+
"""
|
|
51
|
+
:param chart_child: (ChartSeries) Child of the chart.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
if chart_child is not None and isinstance(chart_child, ChartSeries):
|
|
55
|
+
copy_chart_child = copy.deepcopy(chart_child)
|
|
56
|
+
self.CHILDREN.append(copy_chart_child)
|
|
57
|
+
|
|
58
|
+
else: raise Exception("CHART: Chart child of wrong type or None")
|
|
59
|
+
|
|
60
|
+
def __repr__(self):
|
|
61
|
+
return f"{type(self).__name__}"
|
|
62
|
+
|
|
63
|
+
def _get_mapper(self):
|
|
64
|
+
return ChartMapper(self)
|
|
65
|
+
|
|
66
|
+
def _get_span(self):
|
|
67
|
+
return get_span(self)
|
|
68
|
+
|
|
69
|
+
def _get_highlight(self):
|
|
70
|
+
return get_highlight(self)
|