wbreport 2.2.4__py2.py3-none-any.whl → 2.2.6__py2.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.
Potentially problematic release.
This version of wbreport might be problematic. Click here for more details.
- wbreport/fixtures/wbreport.yaml +1 -0
- wbreport/pdf/flowables/risk.py +88 -0
- wbreport/pdf/flowables/textboxes.py +143 -0
- wbreport/pdf/flowables/themes.py +179 -0
- {wbreport-2.2.4.dist-info → wbreport-2.2.6.dist-info}/METADATA +3 -2
- {wbreport-2.2.4.dist-info → wbreport-2.2.6.dist-info}/RECORD +8 -4
- {wbreport-2.2.4.dist-info → wbreport-2.2.6.dist-info}/WHEEL +1 -1
- {wbreport-2.2.4.dist-info → wbreport-2.2.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"model": "wbreport.colorgradient", "pk": 1, "fields": {"title": "Default Color Palette", "colors": "[\"#ffb166\", \"#d1c554\", \"#8cd66b\", \"#5fd785\", \"#05d6a1\", \"#00c1ab\", \"#01abaa\", \"#38c1d7\", \"#70d6ff\", \"#00b1ff\", \"#0585ff\", \"#2a5df2\", \"#5724d9\", \"#823edf\", \"#a359e5\", \"#e13da9\", \"#ef476f\"]"}}, {"model": "wbreport.reportasset", "pk": 1, "fields": {"key": "font-default", "description": "", "text": "", "asset": "report/assets/Roboto-Light.ttf"}}, {"model": "wbreport.reportasset", "pk": 2, "fields": {"key": "font-bd", "description": "", "text": "", "asset": "report/assets/Roboto-Medium.ttf"}}, {"model": "wbreport.reportasset", "pk": 3, "fields": {"key": "font-it", "description": "", "text": "", "asset": "report/assets/Roboto-LightItalic.ttf"}}, {"model": "wbreport.reportcategory", "pk": 1, "fields": {"title": "Test", "order": 0}}, {"model": "wbreport.reportclass", "pk": 1, "fields": {"title": "Base Product Report Class", "class_path": "wbreport.defaults.factsheets.base"}}, {"model": "wbreport.reportclass", "pk": 2, "fields": {"title": "Multindex Report Class", "class_path": "wbreport.defaults.factsheets.multitheme"}}, {"model": "wbreport.reportclass", "pk": 3, "fields": {"title": "Menu Report Class", "class_path": "wbreport.defaults.factsheets.menu"}}, {"model": "wbreport.report", "pk": 1, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#70D6FF", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Base Index Factsheet", "namespace": "base-index-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 2, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#05D6A1", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Base Product Factsheet", "namespace": "base-product-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 3, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#FFB166", "is_private": false, "mailing_list": null, "report_class": 2, "title": "Multi Indexes Factsheet", "namespace": "multi-indexes", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 4, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": 5, "is_active": true, "file_disabled": false, "base_color": "#EF476F", "is_private": false, "mailing_list": null, "report_class": 1, "title": "Share class Factsheet", "namespace": "share-class-factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.report", "pk": 5, "fields": {"content_type": null, "object_id": null, "file_content_type": "PDF", "category": null, "parent_report": null, "is_active": true, "file_disabled": false, "base_color": "#FFF000", "is_private": false, "mailing_list": null, "report_class": 3, "title": "Factsheet", "namespace": "factsheet", "logo_file": "", "color_palette": 1}}, {"model": "wbreport.reportversion", "pk": 1, "fields": {"uuid": "9ebd8023-7ebb-4389-a484-410c7c4aad2a", "lookup": "august-2021", "title": "August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:15:28.539Z", "update_date": "2021-11-09T13:00:00.665Z", "comment": "", "is_primary": true, "disabled": false, "report": 5}}, {"model": "wbreport.reportversion", "pk": 2, "fields": {"uuid": "8a675145-249e-4183-8f19-5cb023fce8c7", "lookup": "factsheet-august-2021", "title": "Share class - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:18:20.567Z", "update_date": "2021-11-09T13:00:01.418Z", "comment": "", "is_primary": true, "disabled": false, "report": 4}}, {"model": "wbreport.reportversion", "pk": 4, "fields": {"uuid": "8e186d05-5298-4ab3-bc0b-6a81398e622a", "lookup": "factsheet-mutli-index-august-2021", "title": "Mutli Index - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:05.102Z", "update_date": "2021-11-09T13:00:01.962Z", "comment": "", "is_primary": true, "disabled": false, "report": 3}}, {"model": "wbreport.reportversion", "pk": 6, "fields": {"uuid": "825c101e-54ec-4197-ad8a-678bcd6431dd", "lookup": "factsheet-base-product-august-2021", "title": "Base product - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:41.464Z", "update_date": "2021-11-09T13:00:02.271Z", "comment": "", "is_primary": true, "disabled": false, "report": 2}}, {"model": "wbreport.reportversion", "pk": 7, "fields": {"uuid": "5e1014f0-f211-4b81-b272-4d5284d7ef8c", "lookup": "factsheet-base-index-august-2021", "title": "Base Index - August 2021", "parameters": {"end": "2021-08-31", "start": "2021-07-30"}, "context": {}, "version_date": "2021-11-09", "creation_date": "2021-11-09T10:19:54.422Z", "update_date": "2021-11-09T13:00:02.616Z", "comment": "", "is_primary": true, "disabled": false, "report": 1}}]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from reportlab.lib.colors import HexColor, black, white
|
|
2
|
+
from reportlab.lib.units import cm
|
|
3
|
+
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
4
|
+
from reportlab.platypus import Flowable, Paragraph
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RiskScale(Flowable):
|
|
8
|
+
def __init__(self, risk, para_style, text=None):
|
|
9
|
+
super().__init__()
|
|
10
|
+
self.risk = risk
|
|
11
|
+
self.risk_text = "The actual risk can vary significantly if you cash in at an early stage and you may get back less. You may not be able to sell your product easily or you may have to sell at a price that significantly impacts on how much you get back."
|
|
12
|
+
self.additional_text = text
|
|
13
|
+
self.para_style = para_style
|
|
14
|
+
self.height = 3.564 * cm
|
|
15
|
+
if text:
|
|
16
|
+
self.height += 18
|
|
17
|
+
|
|
18
|
+
def draw(self):
|
|
19
|
+
width = 0.4 * cm
|
|
20
|
+
gap = 1.177 * cm
|
|
21
|
+
|
|
22
|
+
y = self.height - 0.868 * cm
|
|
23
|
+
x_offset = 0.883 * cm
|
|
24
|
+
self.canv.setFillColor(HexColor(0x9EA3AC))
|
|
25
|
+
for x in range(7):
|
|
26
|
+
_x = x * gap + x_offset
|
|
27
|
+
if x == self.risk - 1:
|
|
28
|
+
self.canv.setFillColor(HexColor(0x3C4859))
|
|
29
|
+
self.canv.circle(_x, y, width, fill=True, stroke=False)
|
|
30
|
+
self.canv.setFillColor(HexColor(0x9EA3AC))
|
|
31
|
+
else:
|
|
32
|
+
self.canv.circle(_x, y, width, fill=True, stroke=False)
|
|
33
|
+
|
|
34
|
+
self.canv.setFillColor(white)
|
|
35
|
+
self.canv.setFont("customfont-bd", 11)
|
|
36
|
+
self.canv.drawCentredString(_x, y - 4, str(x + 1))
|
|
37
|
+
self.canv.setFillColor(HexColor(0x9EA3AC))
|
|
38
|
+
|
|
39
|
+
self.canv.setFillColor(HexColor(0x6D7683))
|
|
40
|
+
self.canv.setStrokeColor(HexColor(0x6D7683))
|
|
41
|
+
|
|
42
|
+
arrow_offset = 0.2 * cm
|
|
43
|
+
|
|
44
|
+
p = self.canv.beginPath()
|
|
45
|
+
origin = (x_offset - arrow_offset, y - 0.868 * cm)
|
|
46
|
+
p.moveTo(*origin)
|
|
47
|
+
p.lineTo(origin[0] + 0.059 * cm, origin[1] + 0.08 * cm)
|
|
48
|
+
p.lineTo(origin[0] - 0.165 * cm, origin[1])
|
|
49
|
+
p.lineTo(origin[0] + 0.059 * cm, origin[1] - 0.08 * cm)
|
|
50
|
+
self.canv.drawPath(p, fill=True, stroke=False)
|
|
51
|
+
|
|
52
|
+
p = self.canv.beginPath()
|
|
53
|
+
origin = (6 * gap + x_offset + arrow_offset, y - 0.868 * cm)
|
|
54
|
+
p.moveTo(origin[0], origin[1])
|
|
55
|
+
p.lineTo(origin[0] - 0.059 * cm, origin[1] + 0.08 * cm)
|
|
56
|
+
p.lineTo(origin[0] + 0.165 * cm, origin[1])
|
|
57
|
+
p.lineTo(origin[0] - 0.059 * cm, origin[1] - 0.08 * cm)
|
|
58
|
+
self.canv.drawPath(p, fill=True, stroke=False)
|
|
59
|
+
|
|
60
|
+
self.canv.setLineWidth(0.02 * cm)
|
|
61
|
+
p = self.canv.beginPath()
|
|
62
|
+
self.canv.line(
|
|
63
|
+
x_offset - arrow_offset,
|
|
64
|
+
y - 0.868 * cm,
|
|
65
|
+
6 * gap + x_offset + arrow_offset,
|
|
66
|
+
y - 0.868 * cm,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
self.canv.setFont("customfont", 6)
|
|
70
|
+
self.canv.setFillColor(black)
|
|
71
|
+
self.canv.drawString(x_offset - arrow_offset - 0.165 * cm, y - 1.2 * cm, "LOWER RISK")
|
|
72
|
+
|
|
73
|
+
text_width = stringWidth("HIGHER RISK", "customfont", 6)
|
|
74
|
+
self.canv.drawString(
|
|
75
|
+
6 * gap + x_offset + arrow_offset + 0.165 * cm - text_width,
|
|
76
|
+
y - 1.2 * cm,
|
|
77
|
+
"HIGHER RISK",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
para = Paragraph(self.risk_text, style=self.para_style)
|
|
81
|
+
para.wrapOn(self.canv, 250, 8.954 * cm)
|
|
82
|
+
para.drawOn(self.canv, 0, y - 2.5 * cm)
|
|
83
|
+
|
|
84
|
+
if self.additional_text:
|
|
85
|
+
para = Paragraph(f"<i>* {self.additional_text}</i>", style=self.para_style)
|
|
86
|
+
|
|
87
|
+
para.wrapOn(self.canv, 250, 8.954 * cm)
|
|
88
|
+
para.drawOn(self.canv, 0, y - 3.2 * cm)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from reportlab.graphics import renderPDF
|
|
2
|
+
from reportlab.lib.colors import black
|
|
3
|
+
from reportlab.lib.units import cm
|
|
4
|
+
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
5
|
+
from reportlab.platypus import Flowable, Image, Paragraph, Table, TableStyle
|
|
6
|
+
from svglib.svglib import svg2rlg
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TextBox(Flowable):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
width,
|
|
13
|
+
height,
|
|
14
|
+
text,
|
|
15
|
+
text_style,
|
|
16
|
+
box_color,
|
|
17
|
+
grid_width=0.30 * cm,
|
|
18
|
+
offset=None,
|
|
19
|
+
debug=False,
|
|
20
|
+
):
|
|
21
|
+
super().__init__()
|
|
22
|
+
self.width = width
|
|
23
|
+
self.height = height
|
|
24
|
+
self.offset = offset or 0
|
|
25
|
+
self.grid_width = grid_width
|
|
26
|
+
|
|
27
|
+
self.text_table = Table([[Paragraph(text, style=text_style)]], rowHeights=[height - grid_width])
|
|
28
|
+
table_styles = [
|
|
29
|
+
("BACKGROUND", (0, 0), (0, 0), box_color),
|
|
30
|
+
("GRID", (0, 0), (0, 0), grid_width, box_color),
|
|
31
|
+
("VALIGN", (0, 0), (0, 0), "MIDDLE"),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
if debug:
|
|
35
|
+
table_styles.extend(
|
|
36
|
+
[
|
|
37
|
+
("BOX", (0, 0), (-1, -1), 0.25, black),
|
|
38
|
+
("INNERGRID", (0, 0), (-1, -1), 0.25, black),
|
|
39
|
+
]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.text_table.setStyle(TableStyle(table_styles))
|
|
43
|
+
|
|
44
|
+
def draw(self):
|
|
45
|
+
self.text_table.wrapOn(self.canv, self.width - self.grid_width - self.offset, self.height)
|
|
46
|
+
self.text_table.drawOn(self.canv, (self.offset or 0) + (self.grid_width / 2), (self.grid_width / 2))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class TextBoxWithImage(TextBox):
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
width,
|
|
53
|
+
height,
|
|
54
|
+
img,
|
|
55
|
+
img_x,
|
|
56
|
+
img_y,
|
|
57
|
+
img_width,
|
|
58
|
+
img_height,
|
|
59
|
+
text,
|
|
60
|
+
text_style,
|
|
61
|
+
box_color,
|
|
62
|
+
grid_width=0.28 * cm,
|
|
63
|
+
svg=False,
|
|
64
|
+
offset=None,
|
|
65
|
+
debug=False,
|
|
66
|
+
):
|
|
67
|
+
super().__init__(width, height, text, text_style, box_color, grid_width, offset, debug)
|
|
68
|
+
self.img = img
|
|
69
|
+
self.img_x = img_x
|
|
70
|
+
self.img_y = img_y
|
|
71
|
+
self.img_width = img_width
|
|
72
|
+
self.img_height = img_height
|
|
73
|
+
self.svg = svg
|
|
74
|
+
|
|
75
|
+
def draw(self):
|
|
76
|
+
super().draw()
|
|
77
|
+
|
|
78
|
+
if self.svg:
|
|
79
|
+
raise NotImplementedError("SVG is not yet implemented.")
|
|
80
|
+
else:
|
|
81
|
+
img = Image(self.img)
|
|
82
|
+
img.drawWidth = self.img_width
|
|
83
|
+
img.drawHeight = self.img_height
|
|
84
|
+
img.drawOn(self.canv, self.img_x, self.img_y)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TextWithIcon(Flowable):
|
|
88
|
+
def __init__(self, width, height, text, font, font_size, icon=None):
|
|
89
|
+
self.width = width
|
|
90
|
+
self.height = height
|
|
91
|
+
self.text = text
|
|
92
|
+
self.font = font
|
|
93
|
+
self.font_size = font_size
|
|
94
|
+
self.icon = icon
|
|
95
|
+
self.svg = None
|
|
96
|
+
if icon:
|
|
97
|
+
self.svg = self.icon.name.split(".")[-1] in ["svg", "SVG"]
|
|
98
|
+
|
|
99
|
+
def draw(self):
|
|
100
|
+
# Get text width and set font
|
|
101
|
+
text_width = stringWidth(self.text, self.font, self.font_size)
|
|
102
|
+
self.canv.setFont(self.font, self.font_size, leading=self.font_size)
|
|
103
|
+
|
|
104
|
+
# Get icon
|
|
105
|
+
icon = None
|
|
106
|
+
if self.svg:
|
|
107
|
+
icon = svg2rlg(self.icon)
|
|
108
|
+
elif self.icon and self.icon.name:
|
|
109
|
+
icon = Image(self.icon)
|
|
110
|
+
|
|
111
|
+
tx = self.width / (4 if icon else 2) - text_width / 2
|
|
112
|
+
ty = self.height / 2 - self.font_size / 2
|
|
113
|
+
self.canv.drawString(tx, ty, self.text)
|
|
114
|
+
|
|
115
|
+
if icon:
|
|
116
|
+
if self.svg:
|
|
117
|
+
icon_width = icon.width
|
|
118
|
+
icon_height = icon.height
|
|
119
|
+
else:
|
|
120
|
+
icon_width = icon.imageWidth
|
|
121
|
+
icon_height = icon.imageHeight
|
|
122
|
+
|
|
123
|
+
drawing_height = self.height / 2
|
|
124
|
+
scale = drawing_height / icon_height
|
|
125
|
+
drawing_width = icon_width * scale
|
|
126
|
+
|
|
127
|
+
allowed_width = (self.width / 2) - 0.2 * cm
|
|
128
|
+
|
|
129
|
+
if drawing_width > allowed_width:
|
|
130
|
+
drawing_width = allowed_width
|
|
131
|
+
scale = drawing_width / icon_width
|
|
132
|
+
drawing_height = icon_height * scale
|
|
133
|
+
|
|
134
|
+
dx = (self.width / 4) * 3 - (icon_width * scale) / 2
|
|
135
|
+
dy = (self.height - drawing_height) / 2
|
|
136
|
+
|
|
137
|
+
if self.svg:
|
|
138
|
+
icon.scale(scale, scale)
|
|
139
|
+
renderPDF.draw(icon, self.canv, dx, dy)
|
|
140
|
+
else:
|
|
141
|
+
icon.drawWidth = drawing_width
|
|
142
|
+
icon.drawHeight = drawing_height
|
|
143
|
+
icon.drawOn(self.canv, dx, dy)
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from reportlab.graphics.charts.barcharts import HorizontalBarChart
|
|
2
|
+
from reportlab.graphics.charts.piecharts import Pie
|
|
3
|
+
from reportlab.graphics.shapes import Drawing
|
|
4
|
+
from reportlab.lib.colors import HexColor, transparent
|
|
5
|
+
from reportlab.lib.formatters import DecimalFormatter
|
|
6
|
+
from reportlab.lib.units import cm
|
|
7
|
+
from reportlab.pdfbase.pdfmetrics import stringWidth
|
|
8
|
+
from reportlab.platypus import Flowable
|
|
9
|
+
from wbreport.pdf.charts.legend import CustomLegend
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ThemeBreakdown(Flowable):
|
|
13
|
+
def __init__(self, df, width, grid_color):
|
|
14
|
+
# self.df = self.df[self.df["allocation_end"] > 0]
|
|
15
|
+
self.df = df[df["allocation_end"] != 0]
|
|
16
|
+
self.pie_padding = 0.45 * cm
|
|
17
|
+
self.pie_diameter = 2.5 * cm
|
|
18
|
+
self.pie_legend_diameter = 0.3 * cm
|
|
19
|
+
self.pie_legend_padding = 1
|
|
20
|
+
self.pie_legend_max_cols = len(self.df)
|
|
21
|
+
|
|
22
|
+
self.bar_width = 0.24 * cm
|
|
23
|
+
self.bar_padding = 0.3 * cm
|
|
24
|
+
self.bar_max_label_width = 3.35 * cm
|
|
25
|
+
self.bar_label_offset = 5
|
|
26
|
+
|
|
27
|
+
self.font_size = 6
|
|
28
|
+
self.font_name = "customfont"
|
|
29
|
+
|
|
30
|
+
self.width = width
|
|
31
|
+
self.height = self.get_height()
|
|
32
|
+
|
|
33
|
+
self.grid_color = grid_color
|
|
34
|
+
|
|
35
|
+
def get_pie_chart_legend_height(self):
|
|
36
|
+
return min(len(self.df), self.pie_legend_max_cols) * (self.pie_legend_diameter + self.pie_legend_padding)
|
|
37
|
+
|
|
38
|
+
def get_pie_chart_height(self):
|
|
39
|
+
return max(
|
|
40
|
+
2 * self.pie_padding + self.pie_diameter,
|
|
41
|
+
2 * self.pie_padding + self.get_pie_chart_legend_height(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def get_bar_chart_height(self):
|
|
45
|
+
num_bars = len(self.df) * 2
|
|
46
|
+
|
|
47
|
+
return num_bars * self.bar_width + num_bars / 2 * self.bar_padding
|
|
48
|
+
|
|
49
|
+
def get_height(self):
|
|
50
|
+
pie_chart_height = self.get_pie_chart_height()
|
|
51
|
+
bar_chart_height = self.get_bar_chart_height()
|
|
52
|
+
|
|
53
|
+
return pie_chart_height + bar_chart_height + 40
|
|
54
|
+
|
|
55
|
+
def get_pie_chart_with_legend(self):
|
|
56
|
+
pie = Pie()
|
|
57
|
+
|
|
58
|
+
pie.x = (self.width / 2 - self.pie_diameter) / 2
|
|
59
|
+
pie.y = self.height - self.pie_diameter - self.pie_padding
|
|
60
|
+
pie.width = self.pie_diameter
|
|
61
|
+
pie.height = self.pie_diameter
|
|
62
|
+
|
|
63
|
+
pie_data = list()
|
|
64
|
+
pie_colornamepairs = list()
|
|
65
|
+
max_width_colornamepair = 0
|
|
66
|
+
|
|
67
|
+
self.df = self.df.sort_values(by=["allocation_end"], ascending=False).rename(
|
|
68
|
+
columns={"underlying_instrument__title_repr": "underlying_instrument__name_repr"}
|
|
69
|
+
)
|
|
70
|
+
for index, row in enumerate(self.df.itertuples()):
|
|
71
|
+
if row.allocation_end > 0:
|
|
72
|
+
label = f"{row.underlying_instrument__name_repr} {row.allocation_end*100:.1f}%"
|
|
73
|
+
pie_data.append(float(row.allocation_end))
|
|
74
|
+
pie.slices[index].fillColor = HexColor(row.color)
|
|
75
|
+
pie.slices[index].strokeColor = transparent
|
|
76
|
+
else:
|
|
77
|
+
label = f"{row.underlying_instrument__name_repr}"
|
|
78
|
+
pie_colornamepairs.append((HexColor(row.color), label))
|
|
79
|
+
max_width_colornamepair = max(max_width_colornamepair, stringWidth(label, "customfont", 6))
|
|
80
|
+
|
|
81
|
+
pie.slices.strokeWidth = -1
|
|
82
|
+
pie.data = pie_data
|
|
83
|
+
|
|
84
|
+
legend = CustomLegend()
|
|
85
|
+
|
|
86
|
+
legend.fontSize = 6
|
|
87
|
+
legend.fontName = "customfont"
|
|
88
|
+
legend.alignment = "right"
|
|
89
|
+
legend.boxAnchor = "nw"
|
|
90
|
+
legend.x = self.width / 2 - pie.width / 2 + self.pie_padding
|
|
91
|
+
legend.y = self.height - self.pie_padding
|
|
92
|
+
legend.dx = legend.dy = self.pie_legend_diameter
|
|
93
|
+
legend.columnMaximum = self.pie_legend_max_cols
|
|
94
|
+
legend.colorNamePairs = pie_colornamepairs
|
|
95
|
+
legend.strokeWidth = -1
|
|
96
|
+
|
|
97
|
+
legend.deltax = 0
|
|
98
|
+
legend.deltay = self.pie_legend_padding
|
|
99
|
+
legend.swdx = 12
|
|
100
|
+
legend.swdy = 0
|
|
101
|
+
|
|
102
|
+
return pie, legend
|
|
103
|
+
|
|
104
|
+
def get_bar_chart_with_legend(self):
|
|
105
|
+
bar = HorizontalBarChart()
|
|
106
|
+
|
|
107
|
+
self.df["contribution_total"] = self.df["contribution_total"] * 100
|
|
108
|
+
self.df["performance_total"] = self.df["performance_total"] * 100
|
|
109
|
+
self.df = self.df.sort_values(by=["performance_total"])
|
|
110
|
+
|
|
111
|
+
bar.data = [self.df.contribution_total.to_list(), self.df.performance_total.to_list()]
|
|
112
|
+
|
|
113
|
+
bar.x = 0
|
|
114
|
+
bar.y = 40
|
|
115
|
+
|
|
116
|
+
bar.width = self.width - self.bar_label_offset - stringWidth("00.00%", self.font_name, self.font_size)
|
|
117
|
+
bar.height = self.get_bar_chart_height()
|
|
118
|
+
|
|
119
|
+
bar.barLabels.dx = self.bar_label_offset
|
|
120
|
+
bar.barLabelFormat = DecimalFormatter(1, suffix="%")
|
|
121
|
+
bar.barLabels.dy = 0
|
|
122
|
+
bar.barLabels.boxAnchor = "w"
|
|
123
|
+
bar.barLabels.boxTarget = "hi"
|
|
124
|
+
bar.barLabels.fontSize = self.font_size
|
|
125
|
+
bar.barLabels.fontName = self.font_name
|
|
126
|
+
bar.barLabels.boxFillColor = None
|
|
127
|
+
bar.barLabels.boxStrokeColor = None
|
|
128
|
+
|
|
129
|
+
bar.groupSpacing = self.bar_padding
|
|
130
|
+
bar.barWidth = self.bar_width
|
|
131
|
+
bar.bars.strokeWidth = 0
|
|
132
|
+
bar.bars.strokeColor = None
|
|
133
|
+
|
|
134
|
+
for index, color in enumerate(self.df.color):
|
|
135
|
+
bar.bars[(0, index)].fillColor = HexColor(f"{color}80", hasAlpha=True)
|
|
136
|
+
bar.bars[(1, index)].fillColor = HexColor(color)
|
|
137
|
+
|
|
138
|
+
bar.valueAxis.labelTextFormat = DecimalFormatter(0, suffix="%")
|
|
139
|
+
bar.valueAxis.labels.fontName = "customfont"
|
|
140
|
+
bar.valueAxis.labels.fontSize = 6
|
|
141
|
+
bar.valueAxis.maximumTicks = 10
|
|
142
|
+
bar.valueAxis.strokeWidth = 0.5
|
|
143
|
+
bar.valueAxis.gridStrokeColor = self.grid_color
|
|
144
|
+
bar.valueAxis.gridStrokeDashArray = (0.2, 0, 0.2)
|
|
145
|
+
bar.valueAxis.visibleGrid = True
|
|
146
|
+
bar.valueAxis.forceZero = True
|
|
147
|
+
|
|
148
|
+
bar.categoryAxis.tickLeft = 0
|
|
149
|
+
bar.categoryAxis.strokeWidth = 0.5
|
|
150
|
+
|
|
151
|
+
legend = CustomLegend()
|
|
152
|
+
legend.x = 30
|
|
153
|
+
legend.y = 0
|
|
154
|
+
|
|
155
|
+
legend.alignment = "right"
|
|
156
|
+
legend.boxAnchor = "sw"
|
|
157
|
+
legend.strokeWidth = -1
|
|
158
|
+
legend.columnMaximum = 1
|
|
159
|
+
legend.colorNamePairs = [
|
|
160
|
+
(HexColor(0xAAAAAA), "Monthly Performance"),
|
|
161
|
+
(HexColor(0xAAAAAA80, hasAlpha=True), "Monthly Contribution"),
|
|
162
|
+
]
|
|
163
|
+
legend.fontName = self.font_name
|
|
164
|
+
legend.fontSize = self.font_size
|
|
165
|
+
legend.deltax = 0
|
|
166
|
+
legend.swdx = 12
|
|
167
|
+
legend.swdy = 0
|
|
168
|
+
|
|
169
|
+
return bar, legend
|
|
170
|
+
|
|
171
|
+
def draw(self):
|
|
172
|
+
drawing = Drawing(self.width, self.height)
|
|
173
|
+
pie_chart, pie_legend = self.get_pie_chart_with_legend()
|
|
174
|
+
bar_chart, bar_legend = self.get_bar_chart_with_legend()
|
|
175
|
+
drawing.add(pie_chart)
|
|
176
|
+
drawing.add(pie_legend)
|
|
177
|
+
drawing.add(bar_chart)
|
|
178
|
+
drawing.add(bar_legend)
|
|
179
|
+
drawing.drawOn(self.canv, 0, 0)
|
|
@@ -18,6 +18,7 @@ wbreport/defaults/factsheets/multitheme.py,sha256=Kje360mRE8XTIOugCOL7-44fjjIrnb
|
|
|
18
18
|
wbreport/factories/__init__.py,sha256=jyD1_R-xc2JPZIaSwtE7zYATdL9NIXIDqTwjLw1MolA,170
|
|
19
19
|
wbreport/factories/data_classes.py,sha256=BwYqbw75lnZSim8LNY9-mhf79GjGqdQdd8XQiPPNyvA,1486
|
|
20
20
|
wbreport/factories/reports.py,sha256=_oJBJp5WmoaZ0BjU8MwqYdBpSJxsQH61b3QQrOmYOOU,2285
|
|
21
|
+
wbreport/fixtures/wbreport.yaml,sha256=VKagUKjjW49ZMjKvUdO72AS9rQzWk-6YWzpiUjmHCXk,5467
|
|
21
22
|
wbreport/migrations/0001_initial_squashed_squashed_0007_report_key.py,sha256=ghAnYJK01-ISld9CUfU18sqdIstuw5UrOgWH2y8gcTs,10061
|
|
22
23
|
wbreport/migrations/0008_alter_report_file_content_type.py,sha256=qPR9kWiZLDvfw9H3-BsUdmdIEwlhm5iHSoSax8hzjNE,715
|
|
23
24
|
wbreport/migrations/0009_alter_report_color_palette.py,sha256=sEIqeMBPYfOg5Onom8H9VCNmlMkpqNI9qT51UVtoBTM,770
|
|
@@ -32,6 +33,9 @@ wbreport/pdf/charts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
32
33
|
wbreport/pdf/charts/legend.py,sha256=D-_-ZLDF7q7KlYLiNYVXHWLs4I-LgV8Ce8lk1sLfbPU,461
|
|
33
34
|
wbreport/pdf/charts/pie.py,sha256=EcWTDOv1ZD5Az6MnBt_w5pBXusPBB3zTHlpguBPNjfU,4287
|
|
34
35
|
wbreport/pdf/charts/timeseries.py,sha256=dN2N66qg9S3bW9OV8q-Vms3wFxUHhLMnaUnymzTNeWY,2381
|
|
36
|
+
wbreport/pdf/flowables/risk.py,sha256=KdE5RQU0kPUqZX6mpvqITVxpLFu1uDeA2C5lrtSA0mY,3443
|
|
37
|
+
wbreport/pdf/flowables/textboxes.py,sha256=BMzlva7WBD5UZ5DD1cwWV5ZPragt61YaJquikDYk7eU,4369
|
|
38
|
+
wbreport/pdf/flowables/themes.py,sha256=Y5LcgSTJDhIZM3uIWAPY68HVMhTY9aY9PDwWsG5_IVk,6548
|
|
35
39
|
wbreport/pdf/sandbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
40
|
wbreport/pdf/sandbox/run.py,sha256=Vkzp_xVVJHctToS49Uef2Mnpb_5KUOWPxNdTFHOqri8,584
|
|
37
41
|
wbreport/pdf/sandbox/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -60,7 +64,7 @@ wbreport/viewsets/configs/displays.py,sha256=py3Wcut9uKmer7LV-ftIi0xU97PFctJT-yK
|
|
|
60
64
|
wbreport/viewsets/configs/endpoints.py,sha256=wPbB0JnnKSF8hSX07W67rdGkP0VKqLBhTnjBKjZzaL0,799
|
|
61
65
|
wbreport/viewsets/configs/menus.py,sha256=trDZsdjXip8KQ4SY1uIudZWFqwb3rQHIRSU36dzRzy0,269
|
|
62
66
|
wbreport/viewsets/configs/titles.py,sha256=AYY1ekJp9V8iihcuR8Ygl-7_1AYpXoAp8WthpWWD5GE,782
|
|
63
|
-
wbreport-2.2.
|
|
64
|
-
wbreport-2.2.
|
|
65
|
-
wbreport-2.2.
|
|
66
|
-
wbreport-2.2.
|
|
67
|
+
wbreport-2.2.6.dist-info/METADATA,sha256=3q2x561TxCR1cqRo7V6BHvj4j7ltDchKQ4E6lepSCYY,185
|
|
68
|
+
wbreport-2.2.6.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
69
|
+
wbreport-2.2.6.dist-info/licenses/LICENSE,sha256=jvfVH0SY8_YMHlsJHKe_OajiscQDz4lpTlqT6x24sVw,172
|
|
70
|
+
wbreport-2.2.6.dist-info/RECORD,,
|
|
File without changes
|