wbreport 2.2.1__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/__init__.py +1 -0
- wbreport/admin.py +87 -0
- wbreport/apps.py +6 -0
- wbreport/defaults/__init__.py +0 -0
- wbreport/defaults/factsheets/__init__.py +0 -0
- wbreport/defaults/factsheets/base.py +990 -0
- wbreport/defaults/factsheets/menu.py +93 -0
- wbreport/defaults/factsheets/mixins.py +35 -0
- wbreport/defaults/factsheets/multitheme.py +947 -0
- wbreport/dynamic_preferences_registry.py +15 -0
- wbreport/factories/__init__.py +8 -0
- wbreport/factories/data_classes.py +48 -0
- wbreport/factories/reports.py +79 -0
- wbreport/filters.py +37 -0
- wbreport/migrations/0001_initial_squashed_squashed_0007_report_key.py +238 -0
- wbreport/migrations/0008_alter_report_file_content_type.py +25 -0
- wbreport/migrations/0009_alter_report_color_palette.py +27 -0
- wbreport/migrations/0010_auto_20240103_0947.py +43 -0
- wbreport/migrations/0011_auto_20240207_1629.py +35 -0
- wbreport/migrations/0012_reportversion_lock.py +17 -0
- wbreport/migrations/0013_alter_reportversion_context.py +18 -0
- wbreport/migrations/0014_alter_reportcategory_options_and_more.py +25 -0
- wbreport/migrations/__init__.py +0 -0
- wbreport/mixins.py +183 -0
- wbreport/models.py +781 -0
- wbreport/pdf/__init__.py +0 -0
- wbreport/pdf/charts/__init__.py +0 -0
- wbreport/pdf/charts/legend.py +15 -0
- wbreport/pdf/charts/pie.py +169 -0
- wbreport/pdf/charts/timeseries.py +77 -0
- wbreport/pdf/sandbox/__init__.py +0 -0
- wbreport/pdf/sandbox/run.py +17 -0
- wbreport/pdf/sandbox/templates/__init__.py +0 -0
- wbreport/pdf/sandbox/templates/basic_factsheet.py +908 -0
- wbreport/pdf/sandbox/templates/fund_factsheet.py +864 -0
- wbreport/pdf/sandbox/templates/long_industry_exposure_factsheet.py +898 -0
- wbreport/pdf/sandbox/templates/multistrat_factsheet.py +872 -0
- wbreport/pdf/sandbox/templates/testfile.pdf +434 -0
- wbreport/pdf/tables/__init__.py +0 -0
- wbreport/pdf/tables/aggregated_tables.py +156 -0
- wbreport/pdf/tables/data_tables.py +75 -0
- wbreport/serializers.py +191 -0
- wbreport/tasks.py +60 -0
- wbreport/templates/__init__.py +0 -0
- wbreport/templatetags/__init__.py +0 -0
- wbreport/templatetags/portfolio_tags.py +35 -0
- wbreport/tests/__init__.py +0 -0
- wbreport/tests/conftest.py +24 -0
- wbreport/tests/test_models.py +253 -0
- wbreport/tests/test_tasks.py +17 -0
- wbreport/tests/test_viewsets.py +0 -0
- wbreport/tests/tests.py +12 -0
- wbreport/urls.py +29 -0
- wbreport/urls_public.py +10 -0
- wbreport/viewsets/__init__.py +10 -0
- wbreport/viewsets/configs/__init__.py +18 -0
- wbreport/viewsets/configs/buttons.py +193 -0
- wbreport/viewsets/configs/displays.py +116 -0
- wbreport/viewsets/configs/endpoints.py +23 -0
- wbreport/viewsets/configs/menus.py +8 -0
- wbreport/viewsets/configs/titles.py +30 -0
- wbreport/viewsets/viewsets.py +330 -0
- wbreport-2.2.1.dist-info/METADATA +6 -0
- wbreport-2.2.1.dist-info/RECORD +66 -0
- wbreport-2.2.1.dist-info/WHEEL +5 -0
- wbreport-2.2.1.dist-info/licenses/LICENSE +4 -0
wbreport/pdf/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from reportlab.graphics.charts.legends import Legend
|
|
2
|
+
from reportlab.graphics.shapes import Circle
|
|
3
|
+
from reportlab.lib.colors import transparent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CustomLegend(Legend):
|
|
7
|
+
def _defaultSwatch(self, x, thisy, dx, dy, fillColor, strokeWidth, strokeColor):
|
|
8
|
+
return Circle(
|
|
9
|
+
x,
|
|
10
|
+
thisy + dx / 2,
|
|
11
|
+
dx / 2,
|
|
12
|
+
fillColor=fillColor,
|
|
13
|
+
strokeColor=transparent,
|
|
14
|
+
strokeWidth=strokeWidth,
|
|
15
|
+
)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from reportlab.graphics.charts.piecharts import Pie
|
|
2
|
+
from reportlab.graphics.shapes import Drawing
|
|
3
|
+
from reportlab.lib import colors
|
|
4
|
+
from reportlab.lib.units import cm
|
|
5
|
+
from wbreport.pdf.charts.legend import CustomLegend
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_data_and_labels_from_df(df, color_palette, percent=True):
|
|
9
|
+
data = list()
|
|
10
|
+
colornamepairs = list()
|
|
11
|
+
|
|
12
|
+
for index, row in enumerate(df.itertuples()):
|
|
13
|
+
data.append(float(row.weighting))
|
|
14
|
+
|
|
15
|
+
label = f"{row[2]*100:.1f}%" if percent else f"{row[2]:.1f}"
|
|
16
|
+
colornamepairs.append((colors.HexColor(color_palette[index]), f"{row[1]} {label}"))
|
|
17
|
+
|
|
18
|
+
return data, colornamepairs
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_legend_height(df, r2_legend=0.3 * cm, legend_padding=1, legend_max_cols=7):
|
|
22
|
+
return min(len(df), legend_max_cols) * (r2_legend + legend_padding)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_pie_chart_vertical_height(
|
|
26
|
+
df,
|
|
27
|
+
r2=2.5 * cm,
|
|
28
|
+
padding=0.45 * cm,
|
|
29
|
+
r2_legend=0.3 * cm,
|
|
30
|
+
legend_padding=1,
|
|
31
|
+
legend_max_cols=7,
|
|
32
|
+
):
|
|
33
|
+
return 3 * padding + r2 + get_legend_height(df, r2_legend, legend_padding, legend_max_cols)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_pie_chart_vertical(
|
|
37
|
+
df,
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
color_palette,
|
|
41
|
+
r2=2.5 * cm,
|
|
42
|
+
padding=0.45 * cm,
|
|
43
|
+
r2_legend=0.3 * cm,
|
|
44
|
+
legend_padding=1,
|
|
45
|
+
legend_max_cols=7,
|
|
46
|
+
):
|
|
47
|
+
drawing = Drawing(width, height)
|
|
48
|
+
pie = Pie()
|
|
49
|
+
|
|
50
|
+
pie.width = r2
|
|
51
|
+
pie.height = r2
|
|
52
|
+
|
|
53
|
+
pie.x = (width - r2) / 2
|
|
54
|
+
pie.y = height - r2 - padding
|
|
55
|
+
df = df.sort_values(by=["weighting"], ascending=False)
|
|
56
|
+
data, colornamepairs = get_data_and_labels_from_df(df, color_palette)
|
|
57
|
+
|
|
58
|
+
for i, color in enumerate(colornamepairs):
|
|
59
|
+
pie.slices[i].fillColor = color[0]
|
|
60
|
+
pie.slices[i].strokeColor = colors.transparent
|
|
61
|
+
pie.slices[i].strokeWidth = 0 # Border width for wedge
|
|
62
|
+
pie.slices.strokeWidth = 0 # Width of the border around the pie chart.
|
|
63
|
+
pie.data = data
|
|
64
|
+
|
|
65
|
+
drawing.add(pie)
|
|
66
|
+
|
|
67
|
+
legend = CustomLegend()
|
|
68
|
+
|
|
69
|
+
legend.alignment = "right"
|
|
70
|
+
legend.boxAnchor = "nw"
|
|
71
|
+
legend.x = 0
|
|
72
|
+
legend.y = height - r2 - 2 * padding
|
|
73
|
+
legend.dx = legend.dy = r2_legend
|
|
74
|
+
legend.strokeWidth = -1
|
|
75
|
+
legend.columnMaximum = legend_max_cols
|
|
76
|
+
legend.colorNamePairs = colornamepairs
|
|
77
|
+
legend.fontName = "customfont"
|
|
78
|
+
legend.fontSize = 6
|
|
79
|
+
|
|
80
|
+
legend.deltax = 0 # Here
|
|
81
|
+
legend.deltay = legend_padding
|
|
82
|
+
legend.swdx = 12
|
|
83
|
+
legend.swdy = 0
|
|
84
|
+
|
|
85
|
+
legend.dxTextSpace = r2_legend
|
|
86
|
+
|
|
87
|
+
legend.variColumn = True
|
|
88
|
+
|
|
89
|
+
drawing.add(legend)
|
|
90
|
+
return drawing
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_pie_chart_horizontal_height(
|
|
94
|
+
df,
|
|
95
|
+
r2=2.5 * cm,
|
|
96
|
+
padding=0.45 * cm,
|
|
97
|
+
r2_legend=0.3 * cm,
|
|
98
|
+
legend_padding=1,
|
|
99
|
+
legend_max_cols=7,
|
|
100
|
+
):
|
|
101
|
+
return max(
|
|
102
|
+
2 * padding + r2,
|
|
103
|
+
2 * padding + get_legend_height(df, r2_legend, legend_padding, legend_max_cols),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_pie_chart_horizontal(
|
|
108
|
+
df,
|
|
109
|
+
width,
|
|
110
|
+
height,
|
|
111
|
+
color_palette,
|
|
112
|
+
col_width,
|
|
113
|
+
r2=2.5 * cm,
|
|
114
|
+
padding=0.45 * cm,
|
|
115
|
+
r2_legend=0.3 * cm,
|
|
116
|
+
legend_padding=1,
|
|
117
|
+
legend_max_cols=7,
|
|
118
|
+
legend_x=None,
|
|
119
|
+
):
|
|
120
|
+
drawing = Drawing(width, height)
|
|
121
|
+
pie = Pie()
|
|
122
|
+
|
|
123
|
+
pie.width = r2
|
|
124
|
+
pie.height = r2
|
|
125
|
+
|
|
126
|
+
pie.x = (col_width - r2) / 2
|
|
127
|
+
pie.y = height - r2 - padding
|
|
128
|
+
df = df.sort_values(by=["weighting"], ascending=False)
|
|
129
|
+
data, colornamepairs = get_data_and_labels_from_df(df, color_palette)
|
|
130
|
+
|
|
131
|
+
for i, color in enumerate(colornamepairs):
|
|
132
|
+
pie.slices[i].fillColor = color[0]
|
|
133
|
+
pie.slices[i].strokeColor = colors.transparent
|
|
134
|
+
pie.slices[i].strokeWidth = 0 # Border width for wedge.
|
|
135
|
+
pie.slices.strokeWidth = 0 # Width of the border around the pie chart.
|
|
136
|
+
pie.data = data
|
|
137
|
+
|
|
138
|
+
drawing.add(pie)
|
|
139
|
+
|
|
140
|
+
legend = CustomLegend()
|
|
141
|
+
|
|
142
|
+
# legend.x = 4.82 * cm
|
|
143
|
+
# legend.y += 0.78 * cm
|
|
144
|
+
|
|
145
|
+
legend_height = get_legend_height(df, r2_legend, legend_padding, legend_max_cols)
|
|
146
|
+
|
|
147
|
+
legend.alignment = "right"
|
|
148
|
+
legend.boxAnchor = "nw"
|
|
149
|
+
|
|
150
|
+
if legend_x:
|
|
151
|
+
legend.x = legend_x
|
|
152
|
+
else:
|
|
153
|
+
legend.x = width - col_width
|
|
154
|
+
|
|
155
|
+
legend.y = (height + legend_height) / 2
|
|
156
|
+
legend.dx = legend.dy = r2_legend
|
|
157
|
+
legend.strokeWidth = -1
|
|
158
|
+
legend.columnMaximum = legend_max_cols
|
|
159
|
+
legend.colorNamePairs = colornamepairs
|
|
160
|
+
legend.fontName = "customfont"
|
|
161
|
+
legend.fontSize = 6
|
|
162
|
+
|
|
163
|
+
legend.deltax = 0
|
|
164
|
+
legend.deltay = legend_padding
|
|
165
|
+
legend.swdx = 12
|
|
166
|
+
legend.swdy = 0
|
|
167
|
+
|
|
168
|
+
drawing.add(legend)
|
|
169
|
+
return drawing
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from reportlab.graphics.charts.axes import LogYValueAxis, NormalDateXValueAxis
|
|
4
|
+
from reportlab.graphics.charts.lineplots import LinePlot, SimpleTimeSeriesPlot
|
|
5
|
+
from reportlab.graphics.shapes import Drawing
|
|
6
|
+
from reportlab.lib import colors
|
|
7
|
+
from reportlab.lib.units import cm
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Scale(Enum):
|
|
11
|
+
ARITHMETIC = 1
|
|
12
|
+
LOGARITHMIC = 2
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LogScaleTimeSeriesPlot(LinePlot):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__()
|
|
18
|
+
|
|
19
|
+
class CustomYAxis(LogYValueAxis):
|
|
20
|
+
def _calcTickPositions(self):
|
|
21
|
+
return self._calcStepAndTickPositions()[1]
|
|
22
|
+
|
|
23
|
+
self.xValueAxis = NormalDateXValueAxis()
|
|
24
|
+
self.yValueAxis = CustomYAxis()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_timeseries_chart(
|
|
28
|
+
data, width, height, color, fill_color, grid_color, scale, x_label_format="{mm}/{yy}", **chart_attributes
|
|
29
|
+
):
|
|
30
|
+
width -= 0.65 * cm
|
|
31
|
+
chart_map = {
|
|
32
|
+
Scale.ARITHMETIC.value: SimpleTimeSeriesPlot,
|
|
33
|
+
Scale.LOGARITHMIC.value: LogScaleTimeSeriesPlot,
|
|
34
|
+
}
|
|
35
|
+
data = [list(filter(lambda x: x[1] > 0, data[0]))]
|
|
36
|
+
max_x_ticks = 20
|
|
37
|
+
|
|
38
|
+
# ensure that if the data count is too low (e.g. less than a month range), we don't overcrowds the X axis
|
|
39
|
+
if len(data[0]) < max_x_ticks:
|
|
40
|
+
x_label_format = "{dd}/{mm}/{yy}"
|
|
41
|
+
max_x_ticks = 10
|
|
42
|
+
|
|
43
|
+
drawing = Drawing(width, height)
|
|
44
|
+
|
|
45
|
+
chart = chart_map[scale]()
|
|
46
|
+
chart.width = width
|
|
47
|
+
chart.height = height
|
|
48
|
+
|
|
49
|
+
for key, value in chart_attributes.items():
|
|
50
|
+
setattr(chart, key, value)
|
|
51
|
+
|
|
52
|
+
chart.data = data
|
|
53
|
+
|
|
54
|
+
chart.lines[0].strokeWidth = 0.5
|
|
55
|
+
chart.lines[0].strokeColor = color
|
|
56
|
+
chart.lines[0].fillColor = fill_color
|
|
57
|
+
chart.lines[0].inFill = True
|
|
58
|
+
|
|
59
|
+
chart.yValueAxis.strokeWidth = -1
|
|
60
|
+
chart.yValueAxis.strokeColor = colors.white
|
|
61
|
+
chart.yValueAxis.labels.fontSize = 7
|
|
62
|
+
chart.yValueAxis.labels.fontName = "customfont"
|
|
63
|
+
chart.yValueAxis.labels.dx = width + 0.55 * cm
|
|
64
|
+
chart.yValueAxis.maximumTicks = 100
|
|
65
|
+
chart.yValueAxis.labelTextFormat = "%d"
|
|
66
|
+
|
|
67
|
+
chart.xValueAxis.strokeWidth = 0
|
|
68
|
+
chart.xValueAxis.labels.fontSize = 7
|
|
69
|
+
chart.xValueAxis.labels.fontName = "customfont"
|
|
70
|
+
chart.xValueAxis.maximumTicks = max_x_ticks
|
|
71
|
+
chart.xValueAxis.visibleGrid = 1
|
|
72
|
+
chart.xValueAxis.gridStrokeDashArray = (0.2, 0, 0.2)
|
|
73
|
+
chart.xValueAxis.gridStrokeColor = grid_color
|
|
74
|
+
chart.xValueAxis.xLabelFormat = x_label_format
|
|
75
|
+
|
|
76
|
+
drawing.add(chart)
|
|
77
|
+
return drawing
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import sys
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from wbportfolio.models import Product
|
|
6
|
+
|
|
7
|
+
report = sys.argv[1]
|
|
8
|
+
|
|
9
|
+
module = importlib.import_module("wbreport.pdf.sandbox.templates.{report}")
|
|
10
|
+
product = Product.objects.get(id=sys.argv[2])
|
|
11
|
+
|
|
12
|
+
start = datetime.strptime(sys.argv[3], "%Y-%m-%d").date()
|
|
13
|
+
end = datetime.strptime(sys.argv[4], "%Y-%m-%d").date()
|
|
14
|
+
context = product.report._get_file_context(start=start, end=end)
|
|
15
|
+
result = module.generate_report(context)
|
|
16
|
+
with open("portfolio/report/pdf/sandbox/templates/testfile.pdf", "wb") as test_file:
|
|
17
|
+
test_file.write(result.read())
|
|
File without changes
|