tesorotools-python 0.0.9__tar.gz → 0.0.11__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.
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/PKG-INFO +1 -1
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/pyproject.toml +1 -1
- tesorotools_python-0.0.11/tesorotools/__init__.py +6 -0
- tesorotools_python-0.0.11/tesorotools/artists/line_plot.py +243 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/__init__.py +2 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/table.py +9 -4
- tesorotools_python-0.0.11/tesorotools/render/content/text.py +23 -0
- tesorotools_python-0.0.9/tesorotools/artists/line_plot.py +0 -114
- tesorotools_python-0.0.9/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/.gitignore +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/artists/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/artists/table.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/convert.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/data_sources/README.md +0 -0
- {tesorotools_python-0.0.9/tesorotools → tesorotools_python-0.0.11/tesorotools/data_sources}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/data_sources/lseg.py +0 -0
- {tesorotools_python-0.0.9/tesorotools/data_sources → tesorotools_python-0.0.11/tesorotools/database}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.9/tesorotools/database → tesorotools_python-0.0.11/tesorotools/dependencies}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/dependencies/functions.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/main.py +0 -0
- {tesorotools_python-0.0.9/tesorotools/dependencies → tesorotools_python-0.0.11/tesorotools/offsets}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.9/tesorotools/offsets → tesorotools_python-0.0.11/tesorotools/render/content}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/headline.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/introduction.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.9/tesorotools/render/content → tesorotools_python-0.0.11/tesorotools/utils}/__init__.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/utils/template.py +0 -0
|
@@ -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,6 +1,7 @@
|
|
|
1
1
|
from tesorotools.render.content.images import Image, Images
|
|
2
2
|
from tesorotools.render.content.section import Section
|
|
3
3
|
from tesorotools.render.content.table import Table
|
|
4
|
+
from tesorotools.render.content.text import Text
|
|
4
5
|
from tesorotools.render.report import Report
|
|
5
6
|
from tesorotools.utils.template import TemplateLoader
|
|
6
7
|
|
|
@@ -9,3 +10,4 @@ TemplateLoader.add_constructor("!section", Section.from_yaml)
|
|
|
9
10
|
TemplateLoader.add_constructor("!image", Image.from_yaml)
|
|
10
11
|
TemplateLoader.add_constructor("!images", Images.from_yaml)
|
|
11
12
|
TemplateLoader.add_constructor("!table", Table.from_yaml)
|
|
13
|
+
TemplateLoader.add_constructor("!text", Text.from_yaml)
|
|
@@ -239,9 +239,9 @@ class Table:
|
|
|
239
239
|
|
|
240
240
|
def __init__(
|
|
241
241
|
self,
|
|
242
|
-
data_file: Path | None,
|
|
243
|
-
color_file: Path | None,
|
|
244
|
-
shade_file: Path | None,
|
|
242
|
+
data_file: Path | None = None,
|
|
243
|
+
color_file: Path | None = None,
|
|
244
|
+
shade_file: Path | None = None,
|
|
245
245
|
block_sep: bool = False,
|
|
246
246
|
title: str | None = None,
|
|
247
247
|
):
|
|
@@ -271,7 +271,12 @@ class Table:
|
|
|
271
271
|
data_file: Path = root_path / f"{file_prefix}_data.feather"
|
|
272
272
|
color_file: Path = root_path / f"{file_prefix}_color.feather"
|
|
273
273
|
shade_file: Path = root_path / f"{file_prefix}_shade.feather"
|
|
274
|
-
return cls(
|
|
274
|
+
return cls(
|
|
275
|
+
data_file,
|
|
276
|
+
color_file=color_file if color_file.exists() else None,
|
|
277
|
+
shade_file=shade_file if shade_file.exists() else None,
|
|
278
|
+
**table_cfg,
|
|
279
|
+
)
|
|
275
280
|
|
|
276
281
|
def render(self, document: Document) -> Document:
|
|
277
282
|
document = render_table(
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from typing import Any, Self
|
|
2
|
+
|
|
3
|
+
from docx.document import Document
|
|
4
|
+
from yaml import Loader, MappingNode
|
|
5
|
+
|
|
6
|
+
from tesorotools.render.content.content import Content
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Text:
|
|
10
|
+
def __init__(self, text: str) -> None:
|
|
11
|
+
self._text = text
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def from_yaml(cls, loader: Loader, node: MappingNode) -> Self:
|
|
15
|
+
values: dict[str, Any] = loader.construct_mapping(node, deep=True)
|
|
16
|
+
values.pop("id")
|
|
17
|
+
text: str = values.pop("text", None)
|
|
18
|
+
section: Self = cls(text=text)
|
|
19
|
+
return section
|
|
20
|
+
|
|
21
|
+
def render(self, document: Document) -> Document:
|
|
22
|
+
document.add_paragraph(self._text)
|
|
23
|
+
return document
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/dependencies/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/dependencies/resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/content.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.9 → tesorotools_python-0.0.11}/tesorotools/render/content/section.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|