tesorotools-python 0.0.9__tar.gz → 0.0.10__tar.gz

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 (53) hide show
  1. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/PKG-INFO +1 -1
  2. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/pyproject.toml +1 -1
  3. tesorotools_python-0.0.10/tesorotools/__init__.py +6 -0
  4. tesorotools_python-0.0.10/tesorotools/artists/line_plot.py +243 -0
  5. tesorotools_python-0.0.9/tesorotools/artists/line_plot.py +0 -114
  6. tesorotools_python-0.0.9/tesorotools/utils/__init__.py +0 -0
  7. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/.gitignore +0 -0
  8. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/artists/__init__.py +0 -0
  9. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/artists/barh_plot.py +0 -0
  10. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/artists/table.py +0 -0
  11. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/artists/type_curve.py +0 -0
  12. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/README.md +0 -0
  13. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
  14. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
  15. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
  16. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
  17. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
  18. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
  19. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
  20. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
  21. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/fonts/README.md +0 -0
  22. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/plots.yaml +0 -0
  23. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/assets/tesoro.mplstyle +0 -0
  24. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/convert.py +0 -0
  25. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/data_sources/README.md +0 -0
  26. {tesorotools_python-0.0.9/tesorotools → tesorotools_python-0.0.10/tesorotools/data_sources}/__init__.py +0 -0
  27. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/data_sources/debug.py +0 -0
  28. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/data_sources/lseg.py +0 -0
  29. {tesorotools_python-0.0.9/tesorotools/data_sources → tesorotools_python-0.0.10/tesorotools/database}/__init__.py +0 -0
  30. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/database/push.py +0 -0
  31. {tesorotools_python-0.0.9/tesorotools/database → tesorotools_python-0.0.10/tesorotools/dependencies}/__init__.py +0 -0
  32. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/dependencies/functions.py +0 -0
  33. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/dependencies/node.py +0 -0
  34. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/dependencies/resolution.py +0 -0
  35. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/main.py +0 -0
  36. {tesorotools_python-0.0.9/tesorotools/dependencies → tesorotools_python-0.0.10/tesorotools/offsets}/__init__.py +0 -0
  37. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/offsets/offsets.py +0 -0
  38. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/offsets/outliers.py +0 -0
  39. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/__init__.py +0 -0
  40. {tesorotools_python-0.0.9/tesorotools/offsets → tesorotools_python-0.0.10/tesorotools/render/content}/__init__.py +0 -0
  41. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/content/content.py +0 -0
  42. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/content/images.py +0 -0
  43. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/content/section.py +0 -0
  44. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/content/table.py +0 -0
  45. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/headline.py +0 -0
  46. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/introduction.py +0 -0
  47. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/render/report.py +0 -0
  48. {tesorotools_python-0.0.9/tesorotools/render/content → tesorotools_python-0.0.10/tesorotools/utils}/__init__.py +0 -0
  49. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/utils/config.py +0 -0
  50. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/utils/globals.py +0 -0
  51. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/utils/matplotlib.py +0 -0
  52. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/utils/series.py +0 -0
  53. {tesorotools_python-0.0.9 → tesorotools_python-0.0.10}/tesorotools/utils/template.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tesorotools-python
3
- Version: 0.0.9
3
+ Version: 0.0.10
4
4
  Requires-Dist: babel
5
5
  Requires-Dist: eikon
6
6
  Requires-Dist: lseg-data
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tesorotools-python"
3
- version = "0.0.9"
3
+ version = "0.0.10"
4
4
  dependencies = ["psycopg2", "SQLAlchemy", "pandas", "matplotlib", "pyarrow", "python-docx", "openpyxl", "PyYAML", "babel", "eikon", "lseg-data"]
5
5
 
6
6
  [build-system]
@@ -0,0 +1,6 @@
1
+ from tesorotools.artists.line_plot import Format, Legend, LinePlot
2
+ from tesorotools.utils.config import TemplateLoader
3
+
4
+ TemplateLoader.add_constructor("!line_plot", LinePlot.from_yaml)
5
+ TemplateLoader.add_constructor("!format", Format.from_yaml)
6
+ TemplateLoader.add_constructor("!legend", Legend.from_yaml)
@@ -0,0 +1,243 @@
1
+ import datetime
2
+ import locale
3
+ from pathlib import Path
4
+ from typing import Any, Self
5
+
6
+ import matplotlib.pyplot as plt
7
+ import pandas as pd
8
+ from matplotlib.ticker import FuncFormatter
9
+ from yaml.nodes import MappingNode
10
+
11
+ from tesorotools.utils.config import TemplateLoader
12
+
13
+ locale.setlocale(locale.LC_ALL, "")
14
+
15
+ from tesorotools.utils.globals import DEBUG
16
+ from tesorotools.utils.matplotlib import (
17
+ PLOT_CONFIG,
18
+ format_annotation,
19
+ load_fonts,
20
+ )
21
+
22
+ load_fonts()
23
+
24
+ LINE_PLOT_CONFIG: dict[str, Any] = PLOT_CONFIG["line"]
25
+ AX_CONFIG: dict[str, Any] = PLOT_CONFIG["ax"]
26
+ FIG_CONFIG: dict[str, Any] = PLOT_CONFIG["figure"]
27
+
28
+
29
+ def _style_spines(
30
+ ax: plt.Axes,
31
+ decimals: int,
32
+ units: str,
33
+ *,
34
+ color: str,
35
+ linewidth: str,
36
+ ):
37
+ ax.grid(visible=True, axis="y")
38
+ for spine in ax.spines.values():
39
+ spine.set_color(color)
40
+ spine.set_linewidth(linewidth)
41
+ ax.yaxis.tick_right()
42
+ ax.yaxis.set_major_formatter(
43
+ FuncFormatter(lambda y, _: format_annotation(y, decimals, units))
44
+ )
45
+ ax.set_xlabel("")
46
+
47
+ ax.tick_params(which="minor", size=0, width=0)
48
+ ax.tick_params(axis="both", which="major")
49
+ for tick in ax.get_xticklines():
50
+ tick.set_markeredgecolor(color)
51
+ for tick in ax.get_yticklines():
52
+ tick.set_markeredgecolor(color)
53
+
54
+
55
+ def _style_baseline(ax: plt.Axes, reference: float = 0, **baseline_config):
56
+ color: str = baseline_config["color"]
57
+ bottom_lim, top_lim = ax.get_ylim()
58
+ ax.set_ylim(bottom=min(reference, bottom_lim), top=max(reference, top_lim))
59
+ bottom_lim, top_lim = ax.get_ylim()
60
+ if bottom_lim == reference:
61
+ ax.spines["bottom"].set_edgecolor(color)
62
+ elif top_lim == reference:
63
+ ax.spines["top"].set_edgecolor(color)
64
+ else:
65
+ ax.axhline(y=reference, **baseline_config)
66
+
67
+
68
+ def plot_line_chart(
69
+ out_name: Path,
70
+ data: pd.DataFrame,
71
+ *,
72
+ base_100: bool,
73
+ annotate: bool,
74
+ format: dict[str, Any],
75
+ **kwargs,
76
+ ):
77
+ if base_100:
78
+ data = data / data.iloc[0, :] * 100
79
+ if format["units"] == "p.b.":
80
+ data = data * 100
81
+ fig = plt.figure(**FIG_CONFIG)
82
+ ax = fig.add_subplot()
83
+ data.plot(ax=ax)
84
+ if annotate:
85
+ pass
86
+
87
+ reference = 100 if base_100 else 0
88
+ _style_spines(ax, **format, **AX_CONFIG["spines"])
89
+ _style_baseline(ax, reference, **AX_CONFIG["baseline"])
90
+ ax.legend(
91
+ loc="upper center",
92
+ bbox_to_anchor=(0.5, LINE_PLOT_CONFIG["legend_sep"]),
93
+ ncol=(
94
+ kwargs["legend"]["ncol"]
95
+ if kwargs is not None and kwargs.get("legend", None) is not None
96
+ else LINE_PLOT_CONFIG["ncol"]
97
+ ),
98
+ )
99
+
100
+ fig.savefig(out_name)
101
+
102
+
103
+ def plot_line_charts(data: pd.DataFrame, config_dicts: dict[str, Any]):
104
+ for name, config in config_dicts.items():
105
+ start_date: pd.Timestamp = pd.to_datetime(config["start_date"])
106
+ end_date_str: str | None = config["end_date"]
107
+ end_date: pd.Timestamp = (
108
+ data.index.max()
109
+ if end_date_str is None
110
+ else pd.to_datetime(end_date_str)
111
+ )
112
+ series: dict[str, str] = config["series"]
113
+ trimmed_data: pd.DataFrame = data.loc[
114
+ slice(start_date, end_date), series.keys()
115
+ ]
116
+ trimmed_data = trimmed_data.rename(columns=series)
117
+ out_name: Path = DEBUG / "line" / f"{name}.png"
118
+ plot_line_chart(out_name, trimmed_data, **config)
119
+
120
+
121
+ class Format:
122
+ def __init__(self, units: str = "", decimals: int = 0):
123
+ self.units = units
124
+ self.decimals = decimals
125
+
126
+ @classmethod
127
+ def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
128
+ loader.flatten_mapping(node)
129
+ format_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
130
+ format_cfg.pop("id")
131
+ return cls(**format_cfg)
132
+
133
+
134
+ class Legend:
135
+ def __init__(self, ncol: int = 5, sep: float = -0.125):
136
+ self.ncol = ncol
137
+ self.sep = sep
138
+
139
+ @classmethod
140
+ def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
141
+ legend_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
142
+ legend_cfg.pop("id")
143
+ return cls(**legend_cfg)
144
+
145
+
146
+ # as more stuff is needed, seems wise to make a class
147
+ class LinePlot:
148
+ def __init__(
149
+ self,
150
+ out_path: Path,
151
+ data_path: Path,
152
+ series: dict[str, str],
153
+ scale: float = 1,
154
+ start_date: datetime.datetime | None = None,
155
+ end_date: datetime.datetime | None = None,
156
+ base_100: bool = False,
157
+ annotate: bool = False,
158
+ baseline: bool = False,
159
+ format: Format | None = None,
160
+ legend: Legend | None = None,
161
+ ) -> None:
162
+
163
+ if out_path.suffix != ".png":
164
+ raise ValueError(f"The out file {out_path} should be a .png file")
165
+ self.out_path = out_path
166
+
167
+ if data_path.suffix != ".feather":
168
+ raise ValueError(
169
+ f"The data file {data_path} must be a .feather file"
170
+ )
171
+ self.data = pd.read_feather(data_path)
172
+
173
+ self.base_100 = base_100
174
+ self.annotate = annotate # unused for the moment
175
+ self.format = format
176
+ self.start_date = start_date
177
+ self.end_date = end_date
178
+ self.series = series
179
+ self.legend = legend
180
+ self.baseline = baseline
181
+ self.scale = scale
182
+
183
+ @classmethod
184
+ def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
185
+ line_plot_cfg: dict[str, Any] = loader.construct_mapping(
186
+ node, deep=True
187
+ )
188
+ line_plot_cfg.pop("id")
189
+ line_plot_cfg["out_path"] = Path(line_plot_cfg["out_path"])
190
+ line_plot_cfg["data_path"] = Path(line_plot_cfg["data_path"])
191
+ return cls(**line_plot_cfg)
192
+
193
+ def plot(self) -> plt.Axes:
194
+ start_date: pd.Timestamp = (
195
+ self.data.index.min()
196
+ if self.start_date is None
197
+ else pd.to_datetime(self.start_date)
198
+ )
199
+
200
+ end_date: pd.Timestamp = (
201
+ self.data.index.max()
202
+ if self.end_date is None
203
+ else pd.to_datetime(self.end_date)
204
+ )
205
+
206
+ plot_data: pd.DataFrame = self.data.loc[
207
+ slice(start_date, end_date), self.series.keys()
208
+ ]
209
+ plot_data = plot_data.rename(columns=self.series)
210
+
211
+ plot_data = plot_data * self.scale
212
+
213
+ if self.base_100: # maybe more flexible in the future
214
+ plot_data = plot_data / plot_data.iloc[0, :] * 100
215
+
216
+ fig = plt.figure(**FIG_CONFIG)
217
+ ax = fig.add_subplot()
218
+ plot_data.plot(ax=ax)
219
+
220
+ if self.annotate: # not implemented yet
221
+ pass
222
+
223
+ _style_spines( # maybe make this function accept a Format object
224
+ ax,
225
+ decimals=self.format.decimals,
226
+ units=self.format.units,
227
+ **AX_CONFIG["spines"],
228
+ )
229
+ if self.baseline:
230
+ reference = 100 if self.base_100 else 0
231
+ _style_baseline(ax, reference, **AX_CONFIG["baseline"])
232
+
233
+ if self.legend is not None:
234
+ ax.legend(
235
+ loc="upper center",
236
+ bbox_to_anchor=(0.5, LINE_PLOT_CONFIG["legend_sep"]),
237
+ ncol=self.legend.ncol,
238
+ )
239
+ else:
240
+ ax.legend().set_visible(False)
241
+
242
+ fig.savefig(self.out_path)
243
+ return ax
@@ -1,114 +0,0 @@
1
- import locale
2
- from pathlib import Path
3
- from typing import Any
4
-
5
- import matplotlib.pyplot as plt
6
- import pandas as pd
7
- from matplotlib.ticker import FuncFormatter
8
-
9
- locale.setlocale(locale.LC_ALL, "")
10
-
11
- from tesorotools.utils.globals import DEBUG
12
- from tesorotools.utils.matplotlib import (
13
- PLOT_CONFIG,
14
- format_annotation,
15
- load_fonts,
16
- )
17
-
18
- load_fonts()
19
-
20
- LINE_PLOT_CONFIG: dict[str, Any] = PLOT_CONFIG["line"]
21
- AX_CONFIG: dict[str, Any] = PLOT_CONFIG["ax"]
22
- FIG_CONFIG: dict[str, Any] = PLOT_CONFIG["figure"]
23
-
24
-
25
- def _style_spines(
26
- ax: plt.Axes,
27
- decimals: int,
28
- units: str,
29
- *,
30
- color: str,
31
- linewidth: str,
32
- ):
33
- ax.grid(visible=True, axis="y")
34
- for spine in ax.spines.values():
35
- spine.set_color(color)
36
- spine.set_linewidth(linewidth)
37
- ax.yaxis.tick_right()
38
- ax.yaxis.set_major_formatter(
39
- FuncFormatter(lambda y, _: format_annotation(y, decimals, units))
40
- )
41
- ax.set_xlabel("")
42
-
43
- ax.tick_params(which="minor", size=0, width=0)
44
- ax.tick_params(axis="both", which="major")
45
- for tick in ax.get_xticklines():
46
- tick.set_markeredgecolor(color)
47
- for tick in ax.get_yticklines():
48
- tick.set_markeredgecolor(color)
49
-
50
-
51
- def _style_baseline(ax: plt.Axes, reference: float = 0, **baseline_config):
52
- color: str = baseline_config["color"]
53
- bottom_lim, top_lim = ax.get_ylim()
54
- ax.set_ylim(bottom=min(reference, bottom_lim), top=max(reference, top_lim))
55
- bottom_lim, top_lim = ax.get_ylim()
56
- if bottom_lim == reference:
57
- ax.spines["bottom"].set_edgecolor(color)
58
- elif top_lim == reference:
59
- ax.spines["top"].set_edgecolor(color)
60
- else:
61
- ax.axhline(y=reference, **baseline_config)
62
-
63
-
64
- def plot_line_chart(
65
- out_name: Path,
66
- data: pd.DataFrame,
67
- *,
68
- base_100: bool,
69
- annotate: bool,
70
- format: dict[str, Any],
71
- **kwargs,
72
- ):
73
- if base_100:
74
- data = data / data.iloc[0, :] * 100
75
- if format["units"] == "p.b.":
76
- data = data * 100
77
- fig = plt.figure(**FIG_CONFIG)
78
- ax = fig.add_subplot()
79
- data.plot(ax=ax)
80
- if annotate:
81
- pass
82
-
83
- reference = 100 if base_100 else 0
84
- _style_spines(ax, **format, **AX_CONFIG["spines"])
85
- _style_baseline(ax, reference, **AX_CONFIG["baseline"])
86
- ax.legend(
87
- loc="upper center",
88
- bbox_to_anchor=(0.5, LINE_PLOT_CONFIG["legend_sep"]),
89
- ncol=(
90
- kwargs["legend"]["ncol"]
91
- if kwargs is not None and kwargs.get("legend", None) is not None
92
- else LINE_PLOT_CONFIG["ncol"]
93
- ),
94
- )
95
-
96
- fig.savefig(out_name)
97
-
98
-
99
- def plot_line_charts(data: pd.DataFrame, config_dicts: dict[str, Any]):
100
- for name, config in config_dicts.items():
101
- start_date: pd.Timestamp = pd.to_datetime(config["start_date"])
102
- end_date_str: str | None = config["end_date"]
103
- end_date: pd.Timestamp = (
104
- data.index.max()
105
- if end_date_str is None
106
- else pd.to_datetime(end_date_str)
107
- )
108
- series: dict[str, str] = config["series"]
109
- trimmed_data: pd.DataFrame = data.loc[
110
- slice(start_date, end_date), series.keys()
111
- ]
112
- trimmed_data = trimmed_data.rename(columns=series)
113
- out_name: Path = DEBUG / "line" / f"{name}.png"
114
- plot_line_chart(out_name, trimmed_data, **config)
File without changes