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.

Files changed (66) hide show
  1. wbreport/__init__.py +1 -0
  2. wbreport/admin.py +87 -0
  3. wbreport/apps.py +6 -0
  4. wbreport/defaults/__init__.py +0 -0
  5. wbreport/defaults/factsheets/__init__.py +0 -0
  6. wbreport/defaults/factsheets/base.py +990 -0
  7. wbreport/defaults/factsheets/menu.py +93 -0
  8. wbreport/defaults/factsheets/mixins.py +35 -0
  9. wbreport/defaults/factsheets/multitheme.py +947 -0
  10. wbreport/dynamic_preferences_registry.py +15 -0
  11. wbreport/factories/__init__.py +8 -0
  12. wbreport/factories/data_classes.py +48 -0
  13. wbreport/factories/reports.py +79 -0
  14. wbreport/filters.py +37 -0
  15. wbreport/migrations/0001_initial_squashed_squashed_0007_report_key.py +238 -0
  16. wbreport/migrations/0008_alter_report_file_content_type.py +25 -0
  17. wbreport/migrations/0009_alter_report_color_palette.py +27 -0
  18. wbreport/migrations/0010_auto_20240103_0947.py +43 -0
  19. wbreport/migrations/0011_auto_20240207_1629.py +35 -0
  20. wbreport/migrations/0012_reportversion_lock.py +17 -0
  21. wbreport/migrations/0013_alter_reportversion_context.py +18 -0
  22. wbreport/migrations/0014_alter_reportcategory_options_and_more.py +25 -0
  23. wbreport/migrations/__init__.py +0 -0
  24. wbreport/mixins.py +183 -0
  25. wbreport/models.py +781 -0
  26. wbreport/pdf/__init__.py +0 -0
  27. wbreport/pdf/charts/__init__.py +0 -0
  28. wbreport/pdf/charts/legend.py +15 -0
  29. wbreport/pdf/charts/pie.py +169 -0
  30. wbreport/pdf/charts/timeseries.py +77 -0
  31. wbreport/pdf/sandbox/__init__.py +0 -0
  32. wbreport/pdf/sandbox/run.py +17 -0
  33. wbreport/pdf/sandbox/templates/__init__.py +0 -0
  34. wbreport/pdf/sandbox/templates/basic_factsheet.py +908 -0
  35. wbreport/pdf/sandbox/templates/fund_factsheet.py +864 -0
  36. wbreport/pdf/sandbox/templates/long_industry_exposure_factsheet.py +898 -0
  37. wbreport/pdf/sandbox/templates/multistrat_factsheet.py +872 -0
  38. wbreport/pdf/sandbox/templates/testfile.pdf +434 -0
  39. wbreport/pdf/tables/__init__.py +0 -0
  40. wbreport/pdf/tables/aggregated_tables.py +156 -0
  41. wbreport/pdf/tables/data_tables.py +75 -0
  42. wbreport/serializers.py +191 -0
  43. wbreport/tasks.py +60 -0
  44. wbreport/templates/__init__.py +0 -0
  45. wbreport/templatetags/__init__.py +0 -0
  46. wbreport/templatetags/portfolio_tags.py +35 -0
  47. wbreport/tests/__init__.py +0 -0
  48. wbreport/tests/conftest.py +24 -0
  49. wbreport/tests/test_models.py +253 -0
  50. wbreport/tests/test_tasks.py +17 -0
  51. wbreport/tests/test_viewsets.py +0 -0
  52. wbreport/tests/tests.py +12 -0
  53. wbreport/urls.py +29 -0
  54. wbreport/urls_public.py +10 -0
  55. wbreport/viewsets/__init__.py +10 -0
  56. wbreport/viewsets/configs/__init__.py +18 -0
  57. wbreport/viewsets/configs/buttons.py +193 -0
  58. wbreport/viewsets/configs/displays.py +116 -0
  59. wbreport/viewsets/configs/endpoints.py +23 -0
  60. wbreport/viewsets/configs/menus.py +8 -0
  61. wbreport/viewsets/configs/titles.py +30 -0
  62. wbreport/viewsets/viewsets.py +330 -0
  63. wbreport-2.2.1.dist-info/METADATA +6 -0
  64. wbreport-2.2.1.dist-info/RECORD +66 -0
  65. wbreport-2.2.1.dist-info/WHEEL +5 -0
  66. wbreport-2.2.1.dist-info/licenses/LICENSE +4 -0
@@ -0,0 +1,93 @@
1
+ from typing import Any, Dict
2
+
3
+ from django.db import models
4
+ from django.template.loader import get_template
5
+ from rest_framework.reverse import reverse
6
+ from wbportfolio.models import Instrument
7
+ from wbreport.mixins import ReportMixin
8
+
9
+ from .mixins import FactsheetReportMixin
10
+
11
+
12
+ class ReportClass(FactsheetReportMixin, ReportMixin):
13
+ HTML_TEMPLATE_FILE = "report/factsheet_menu.html"
14
+
15
+ @classmethod
16
+ def generate_html(cls, context: Dict[str, Any]) -> str:
17
+ template = get_template(cls.HTML_TEMPLATE_FILE)
18
+ return template.render(context)
19
+
20
+ generate_file = None
21
+
22
+ # @classmethod
23
+ # def generate_file(cls, context: Dict[str, Any]):
24
+ # return None
25
+
26
+ @classmethod
27
+ def get_context(cls, version: models.Model) -> Dict[str, Any]:
28
+ parameters = cls.parse_parameters(version.parameters)
29
+ end = parameters["end"]
30
+ products = []
31
+ benchmarks = {}
32
+ parent_report = version.report
33
+
34
+ for report in parent_report.child_reports.filter(is_active=True, parameters__is_private=False).order_by(
35
+ "object_id"
36
+ ):
37
+ product = report.content_object
38
+ if product.get_prices().exists() and (
39
+ report_version := report.versions.filter(parameters=parameters).first()
40
+ ):
41
+ latest_price = product.get_latest_valid_price(end)
42
+ table = {}
43
+ # if product.ticker:
44
+ # table["ticker"] = product.ticker
45
+ table["url"] = reverse("public_report:report_version", args=[report_version.lookup])
46
+ table["title"] = product.title
47
+ table["currency"] = product.currency.key
48
+ table["isin"] = product.isin
49
+ table["price"] = float(latest_price.net_value)
50
+ table["launch_date"] = product.inception_date
51
+ table["bank_title"] = product.bank.title
52
+ if report.logo_file:
53
+ table["logo_file"] = report.logo_file
54
+ table["color"] = report.base_color
55
+ product_perfs = product.get_last_performance_summary(end=latest_price.date).to_dict("records")[0]
56
+ table["product_perf"] = product_perfs
57
+
58
+ benchmarks_perf = {}
59
+ # First element is the primary benchmark
60
+
61
+ ordered_benchmarks_ids = product.to_benchmarks.order_by("-is_primary").values_list(
62
+ "benchmark", flat=True
63
+ )
64
+ if len(ordered_benchmarks_ids) > 0:
65
+ for benchmark_id in ordered_benchmarks_ids:
66
+ instrument = Instrument.objects.get(id=benchmark_id)
67
+ if instrument.id not in benchmarks:
68
+ benchmarks[instrument.id] = instrument.get_prices_df(from_date=latest_price.date)
69
+ tmp_val = benchmarks[instrument.id].truncate(
70
+ before=product.inception_date, after=latest_price.date
71
+ )
72
+ benchmark_perf = {"daily": None, "monthly": None, "yearly": None, "inception": None}
73
+ if not tmp_val.empty:
74
+ benchmark_daily = Instrument.extract_daily_performance_df(tmp_val).performance.iloc[-1]
75
+ benchmark_monthly = Instrument.extract_monthly_performance_df(tmp_val).performance.iloc[-1]
76
+ benchmark_yearly = Instrument.extract_annual_performance_df(tmp_val).performance.iloc[-1]
77
+ benchmark_inception = Instrument.extract_inception_performance_df(tmp_val)
78
+ benchmark_perf = {
79
+ "daily": product_perfs["daily"] - benchmark_daily,
80
+ "monthly": product_perfs["monthly"] - benchmark_monthly,
81
+ "yearly": product_perfs["yearly"] - benchmark_yearly,
82
+ "inception": product_perfs["inception"] - benchmark_inception,
83
+ }
84
+ benchmarks_perf[instrument.title] = benchmark_perf
85
+
86
+ primary_benchmark = Instrument.objects.get(id=ordered_benchmarks_ids[0])
87
+
88
+ table["reference_name"] = primary_benchmark.title
89
+ table["reference_perf"] = benchmarks_perf.pop(primary_benchmark.title)
90
+ table["benchmark_perf"] = benchmarks_perf
91
+
92
+ products.append(table)
93
+ return {"products": products, "title": parent_report.title, "date": end}
@@ -0,0 +1,35 @@
1
+ from calendar import monthrange
2
+ from datetime import date, datetime, timedelta
3
+ from typing import Any, Dict
4
+
5
+
6
+ class FactsheetReportMixin:
7
+ @classmethod
8
+ def get_version_title(cls, report_title: str, parameters: Dict[str, Any]) -> str:
9
+ parameters = cls.parse_parameters(parameters)
10
+ end = parameters["end"]
11
+ return f'{report_title} - {end.strftime("%b %Y")}'
12
+
13
+ @classmethod
14
+ def parse_parameters(cls, parameters: Dict[str, str]) -> Dict[str, Any]:
15
+ return {
16
+ "start": datetime.strptime(parameters["start"], "%Y-%m-%d"),
17
+ "end": datetime.strptime(parameters["end"], "%Y-%m-%d"),
18
+ }
19
+
20
+ @classmethod
21
+ def get_next_parameters(cls, parameters: Dict[str, str]) -> Dict[str, str]:
22
+ parameters = cls.parse_parameters(parameters)
23
+ end = parameters["end"]
24
+ start = end
25
+ next_month = end.month + 1
26
+ next_year = end.year
27
+ if next_month > 12:
28
+ next_month = 1
29
+ next_year += 1
30
+ end = date(next_year, next_month, day=monthrange(next_year, next_month)[1])
31
+
32
+ while end.weekday() in [5, 6]: # Mon-Fri are 0-4
33
+ end -= timedelta(days=1)
34
+
35
+ return {"start": start.strftime("%Y-%m-%d"), "end": end.strftime("%Y-%m-%d")}