tesorotools-python 0.0.25__tar.gz → 0.0.27__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.25 → tesorotools_python-0.0.27}/PKG-INFO +1 -1
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/pyproject.toml +1 -1
- tesorotools_python-0.0.27/src/tesorotools/artists/__init__.py +5 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/line_plot.py +131 -4
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/stacked.py +97 -21
- tesorotools_python-0.0.27/src/tesorotools/pipeline/diagnose.py +54 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/pipeline/rules.py +47 -3
- tesorotools_python-0.0.27/src/tesorotools/render/__init__.py +0 -0
- tesorotools_python-0.0.27/src/tesorotools/render/content/__init__.py +0 -0
- tesorotools_python-0.0.27/src/tesorotools/utils/config.py +98 -0
- tesorotools_python-0.0.25/src/tesorotools/__init__.py +0 -9
- tesorotools_python-0.0.25/src/tesorotools/artists/__init__.py +0 -9
- tesorotools_python-0.0.25/src/tesorotools/render/__init__.py +0 -17
- tesorotools_python-0.0.25/src/tesorotools/utils/config.py +0 -38
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/.gitignore +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/data_sources → tesorotools_python-0.0.27/src/tesorotools}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/barh.md +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/table.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/convert.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/README.md +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/dependencies → tesorotools_python-0.0.27/src/tesorotools/data_sources}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/lseg.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/offsets → tesorotools_python-0.0.27/src/tesorotools/dependencies}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/main.py +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/pipeline → tesorotools_python-0.0.27/src/tesorotools/offsets}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/providers → tesorotools_python-0.0.27/src/tesorotools/pipeline}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.0.25/src/tesorotools/render/content → tesorotools_python-0.0.27/src/tesorotools/providers}/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/table.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/utils/template.py +0 -0
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/line_plot.py
RENAMED
|
@@ -26,6 +26,76 @@ AX_CONFIG: dict[str, Any] = PLOT_CONFIG["ax"]
|
|
|
26
26
|
FIG_CONFIG: dict[str, Any] = PLOT_CONFIG["figure"]
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
def adjust_figure_for_plot_size(
|
|
30
|
+
fig: Figure,
|
|
31
|
+
ax: Axes,
|
|
32
|
+
plot_size: tuple[float, float],
|
|
33
|
+
) -> None:
|
|
34
|
+
"""Resize the figure so the axes area matches *plot_size*.
|
|
35
|
+
|
|
36
|
+
Call this **after** adding the legend. The figure
|
|
37
|
+
height grows to accommodate the legend while keeping
|
|
38
|
+
the plot area at the requested size.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
fig
|
|
43
|
+
The figure to resize.
|
|
44
|
+
ax
|
|
45
|
+
The axes whose area should match *plot_size*.
|
|
46
|
+
plot_size
|
|
47
|
+
Desired ``(width, height)`` of the axes area in
|
|
48
|
+
inches.
|
|
49
|
+
"""
|
|
50
|
+
fig.canvas.draw() # type: ignore[reportUnknownMemberType]
|
|
51
|
+
renderer = fig.canvas.get_renderer() # type: ignore[reportUnknownMemberType]
|
|
52
|
+
ax_bbox = ax.get_tightbbox(renderer) # type: ignore[reportUnknownArgumentType]
|
|
53
|
+
fig_bbox = fig.get_tightbbox(renderer) # type: ignore[reportUnknownArgumentType]
|
|
54
|
+
if ax_bbox is None or fig_bbox is None: # type: ignore[reportUnnecessaryComparison]
|
|
55
|
+
return
|
|
56
|
+
fig_w, fig_h = fig.get_size_inches()
|
|
57
|
+
ax_w_in = ax_bbox.width / fig.dpi
|
|
58
|
+
ax_h_in = ax_bbox.height / fig.dpi
|
|
59
|
+
if ax_w_in <= 0 or ax_h_in <= 0:
|
|
60
|
+
return
|
|
61
|
+
new_w = fig_w * (plot_size[0] / ax_w_in)
|
|
62
|
+
new_h = fig_h * (plot_size[1] / ax_h_in)
|
|
63
|
+
fig.set_size_inches(new_w, new_h)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def auto_ncol(ax: Axes, labels: list[str]) -> int:
|
|
67
|
+
"""Choose the maximum legend ncol that fits the axes.
|
|
68
|
+
|
|
69
|
+
Renders each label as a temporary Text to measure its
|
|
70
|
+
width, then packs as many columns as the axes width
|
|
71
|
+
allows, with padding for the legend handle + spacing.
|
|
72
|
+
Falls back to ``len(labels)`` (single row) when all
|
|
73
|
+
labels are short enough.
|
|
74
|
+
"""
|
|
75
|
+
fig = ax.get_figure()
|
|
76
|
+
if fig is None:
|
|
77
|
+
return len(labels)
|
|
78
|
+
renderer = fig.canvas.get_renderer() # type: ignore[reportUnknownMemberType]
|
|
79
|
+
ax_width_px = ax.get_window_extent(renderer).width # type: ignore[reportUnknownArgumentType]
|
|
80
|
+
|
|
81
|
+
# Measure widest label in pixels.
|
|
82
|
+
handle_pad_px = 40.0 # handle icon + spacing estimate
|
|
83
|
+
max_label_px = 0.0
|
|
84
|
+
for label in labels:
|
|
85
|
+
t = ax.text( # type: ignore[reportUnknownMemberType]
|
|
86
|
+
0, 0, label
|
|
87
|
+
)
|
|
88
|
+
bbox = t.get_window_extent(renderer) # type: ignore[reportUnknownArgumentType]
|
|
89
|
+
max_label_px = max(max_label_px, bbox.width)
|
|
90
|
+
t.remove()
|
|
91
|
+
|
|
92
|
+
col_width = max_label_px + handle_pad_px
|
|
93
|
+
if col_width <= 0:
|
|
94
|
+
return len(labels)
|
|
95
|
+
ncol = max(1, int(ax_width_px / col_width))
|
|
96
|
+
return min(ncol, len(labels))
|
|
97
|
+
|
|
98
|
+
|
|
29
99
|
def style_spines(
|
|
30
100
|
ax: Axes,
|
|
31
101
|
decimals: int,
|
|
@@ -62,6 +132,36 @@ def style_spines(
|
|
|
62
132
|
tick.set_markeredgecolor(color)
|
|
63
133
|
|
|
64
134
|
|
|
135
|
+
def export_legend(
|
|
136
|
+
ax: Axes,
|
|
137
|
+
out_path: Path,
|
|
138
|
+
*,
|
|
139
|
+
ncol: int = 5,
|
|
140
|
+
dpi: int = 500,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Save the legend of *ax* as a standalone PNG.
|
|
143
|
+
|
|
144
|
+
The plot's own legend is removed after export.
|
|
145
|
+
"""
|
|
146
|
+
handles, labels = ax.get_legend_handles_labels()
|
|
147
|
+
fig_leg = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
148
|
+
figsize=(6, 0.5), dpi=dpi
|
|
149
|
+
)
|
|
150
|
+
fig_leg.legend( # type: ignore[reportUnknownMemberType]
|
|
151
|
+
handles,
|
|
152
|
+
labels,
|
|
153
|
+
loc="center",
|
|
154
|
+
ncol=ncol,
|
|
155
|
+
)
|
|
156
|
+
fig_leg.savefig( # type: ignore[reportUnknownMemberType]
|
|
157
|
+
out_path, bbox_inches="tight"
|
|
158
|
+
)
|
|
159
|
+
plt.close(fig_leg)
|
|
160
|
+
legend = ax.get_legend()
|
|
161
|
+
if legend is not None:
|
|
162
|
+
legend.remove()
|
|
163
|
+
|
|
164
|
+
|
|
65
165
|
def style_baseline(
|
|
66
166
|
ax: Axes,
|
|
67
167
|
reference: float = 0,
|
|
@@ -161,7 +261,11 @@ class Format:
|
|
|
161
261
|
|
|
162
262
|
|
|
163
263
|
class Legend:
|
|
164
|
-
def __init__(
|
|
264
|
+
def __init__(
|
|
265
|
+
self,
|
|
266
|
+
ncol: int | None = None,
|
|
267
|
+
sep: float = -0.125,
|
|
268
|
+
) -> None:
|
|
165
269
|
self.ncol = ncol
|
|
166
270
|
self.sep = sep
|
|
167
271
|
|
|
@@ -190,6 +294,9 @@ class LinePlot:
|
|
|
190
294
|
format: Format | None = None,
|
|
191
295
|
legend: Legend | None = None,
|
|
192
296
|
data: pd.DataFrame | None = None,
|
|
297
|
+
figsize: tuple[float, float] | None = None,
|
|
298
|
+
series_styles: dict[str, dict[str, Any]] | None = None,
|
|
299
|
+
plot_size: tuple[float, float] | None = None,
|
|
193
300
|
) -> None:
|
|
194
301
|
|
|
195
302
|
if out_path.suffix != ".png":
|
|
@@ -221,6 +328,9 @@ class LinePlot:
|
|
|
221
328
|
self.legend = legend
|
|
222
329
|
self.baseline = baseline
|
|
223
330
|
self.scale = scale
|
|
331
|
+
self.figsize = figsize
|
|
332
|
+
self.series_styles = series_styles or {}
|
|
333
|
+
self.plot_size = plot_size
|
|
224
334
|
|
|
225
335
|
@classmethod
|
|
226
336
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -255,11 +365,19 @@ class LinePlot:
|
|
|
255
365
|
if self.base_100: # maybe more flexible in the future
|
|
256
366
|
plot_data = plot_data / plot_data.iloc[0, :] * 100
|
|
257
367
|
|
|
368
|
+
fig_kw = dict(FIG_CONFIG)
|
|
369
|
+
if self.figsize is not None:
|
|
370
|
+
fig_kw["figsize"] = self.figsize
|
|
258
371
|
fig: Figure = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
259
|
-
**
|
|
372
|
+
**fig_kw
|
|
260
373
|
)
|
|
261
374
|
ax = fig.add_subplot()
|
|
262
|
-
|
|
375
|
+
if self.series_styles:
|
|
376
|
+
for col in plot_data.columns:
|
|
377
|
+
style = self.series_styles.get(col, {})
|
|
378
|
+
plot_data[col].plot(ax=ax, label=col, **style)
|
|
379
|
+
else:
|
|
380
|
+
plot_data.plot(ax=ax)
|
|
263
381
|
|
|
264
382
|
if self.annotate: # not implemented yet
|
|
265
383
|
pass
|
|
@@ -276,19 +394,28 @@ class LinePlot:
|
|
|
276
394
|
style_baseline(ax, reference, **AX_CONFIG["baseline"])
|
|
277
395
|
|
|
278
396
|
if self.legend is not None:
|
|
397
|
+
labels = list(plot_data.columns)
|
|
398
|
+
ncol = (
|
|
399
|
+
self.legend.ncol
|
|
400
|
+
if self.legend.ncol is not None
|
|
401
|
+
else auto_ncol(ax, labels)
|
|
402
|
+
)
|
|
279
403
|
ax.legend( # type: ignore[reportUnknownMemberType]
|
|
280
404
|
loc="upper center",
|
|
281
405
|
bbox_to_anchor=(
|
|
282
406
|
0.5,
|
|
283
407
|
LINE_PLOT_CONFIG["legend_sep"],
|
|
284
408
|
),
|
|
285
|
-
ncol=
|
|
409
|
+
ncol=ncol,
|
|
286
410
|
)
|
|
287
411
|
else:
|
|
288
412
|
ax.legend().set_visible( # type: ignore[reportUnknownMemberType]
|
|
289
413
|
False
|
|
290
414
|
)
|
|
291
415
|
|
|
416
|
+
if self.plot_size is not None:
|
|
417
|
+
adjust_figure_for_plot_size(fig, ax, self.plot_size)
|
|
418
|
+
|
|
292
419
|
fig.savefig( # type: ignore[reportUnknownMemberType]
|
|
293
420
|
self.out_path
|
|
294
421
|
)
|
|
@@ -15,12 +15,13 @@ from tesorotools.artists.line_plot import (
|
|
|
15
15
|
FIG_CONFIG,
|
|
16
16
|
Format,
|
|
17
17
|
Legend,
|
|
18
|
+
adjust_figure_for_plot_size,
|
|
19
|
+
auto_ncol,
|
|
18
20
|
style_baseline,
|
|
19
21
|
style_spines,
|
|
20
22
|
)
|
|
21
23
|
from tesorotools.utils.config import TemplateLoader
|
|
22
24
|
|
|
23
|
-
_DEFAULT_NCOL = 5
|
|
24
25
|
_DEFAULT_SEP = -0.125
|
|
25
26
|
|
|
26
27
|
|
|
@@ -44,6 +45,8 @@ class StackedAreaPlot:
|
|
|
44
45
|
baseline: bool = False,
|
|
45
46
|
format: Format | None = None,
|
|
46
47
|
legend: Legend | None = None,
|
|
48
|
+
figsize: tuple[float, float] | None = None,
|
|
49
|
+
plot_size: tuple[float, float] | None = None,
|
|
47
50
|
) -> None:
|
|
48
51
|
if out_path.suffix != ".png":
|
|
49
52
|
raise ValueError(f"out_path must be .png: {out_path}")
|
|
@@ -56,6 +59,8 @@ class StackedAreaPlot:
|
|
|
56
59
|
self.baseline = baseline
|
|
57
60
|
self.format = format or Format()
|
|
58
61
|
self.legend = legend
|
|
62
|
+
self.figsize = figsize
|
|
63
|
+
self.plot_size = plot_size
|
|
59
64
|
|
|
60
65
|
@classmethod
|
|
61
66
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -82,8 +87,11 @@ class StackedAreaPlot:
|
|
|
82
87
|
plot_data = self.data.loc[start:end, list(self.series.keys())].dropna()
|
|
83
88
|
plot_data = plot_data * self.scale
|
|
84
89
|
|
|
90
|
+
fig_kw = dict(FIG_CONFIG)
|
|
91
|
+
if self.figsize is not None:
|
|
92
|
+
fig_kw["figsize"] = self.figsize
|
|
85
93
|
fig: Figure = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
86
|
-
**
|
|
94
|
+
**fig_kw
|
|
87
95
|
)
|
|
88
96
|
ax: Axes = fig.add_subplot()
|
|
89
97
|
|
|
@@ -107,7 +115,8 @@ class StackedAreaPlot:
|
|
|
107
115
|
if self.baseline:
|
|
108
116
|
style_baseline(ax, 0, **AX_CONFIG["baseline"])
|
|
109
117
|
|
|
110
|
-
|
|
118
|
+
legend_ncol = self.legend.ncol if self.legend else None
|
|
119
|
+
ncol = legend_ncol if legend_ncol is not None else auto_ncol(ax, labels)
|
|
111
120
|
sep = self.legend.sep if self.legend else _DEFAULT_SEP
|
|
112
121
|
ax.legend( # type: ignore[reportUnknownMemberType]
|
|
113
122
|
loc="upper center",
|
|
@@ -115,6 +124,9 @@ class StackedAreaPlot:
|
|
|
115
124
|
ncol=ncol,
|
|
116
125
|
)
|
|
117
126
|
|
|
127
|
+
if self.plot_size is not None:
|
|
128
|
+
adjust_figure_for_plot_size(fig, ax, self.plot_size)
|
|
129
|
+
|
|
118
130
|
fig.savefig( # type: ignore[reportUnknownMemberType]
|
|
119
131
|
self.out_path
|
|
120
132
|
)
|
|
@@ -127,6 +139,13 @@ class StackedBarPlot:
|
|
|
127
139
|
|
|
128
140
|
Positive and negative values are stacked separately so
|
|
129
141
|
that bars extend in both directions from the baseline.
|
|
142
|
+
|
|
143
|
+
The ``plot`` method delegates to overridable hooks so
|
|
144
|
+
subclasses can customise individual steps without
|
|
145
|
+
reimplementing the full render pipeline:
|
|
146
|
+
|
|
147
|
+
* ``_prepare_data`` — slice, scale, resample
|
|
148
|
+
* ``_format_xticks`` — tick positions, labels, rotation
|
|
130
149
|
"""
|
|
131
150
|
|
|
132
151
|
def __init__(
|
|
@@ -141,6 +160,11 @@ class StackedBarPlot:
|
|
|
141
160
|
baseline: bool = True,
|
|
142
161
|
format: Format | None = None,
|
|
143
162
|
legend: Legend | None = None,
|
|
163
|
+
figsize: tuple[float, float] | None = None,
|
|
164
|
+
overlay_series: dict[str, str] | None = None,
|
|
165
|
+
plot_size: tuple[float, float] | None = None,
|
|
166
|
+
bar_width: float = 0.7,
|
|
167
|
+
x_rotation: float = 0,
|
|
144
168
|
) -> None:
|
|
145
169
|
if out_path.suffix != ".png":
|
|
146
170
|
raise ValueError(f"out_path must be .png: {out_path}")
|
|
@@ -153,6 +177,11 @@ class StackedBarPlot:
|
|
|
153
177
|
self.baseline = baseline
|
|
154
178
|
self.format = format or Format()
|
|
155
179
|
self.legend = legend
|
|
180
|
+
self.plot_size = plot_size
|
|
181
|
+
self.figsize = figsize
|
|
182
|
+
self.overlay_series = overlay_series or {}
|
|
183
|
+
self.bar_width = bar_width
|
|
184
|
+
self.x_rotation = x_rotation
|
|
156
185
|
|
|
157
186
|
@classmethod
|
|
158
187
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -164,7 +193,13 @@ class StackedBarPlot:
|
|
|
164
193
|
cfg["data"] = pd.read_feather(cfg.pop("data_path"))
|
|
165
194
|
return cls(**cfg)
|
|
166
195
|
|
|
167
|
-
|
|
196
|
+
# -- Overridable hooks -----------------------------------
|
|
197
|
+
|
|
198
|
+
def _prepare_data(self) -> pd.DataFrame:
|
|
199
|
+
"""Slice, scale, and return plot-ready DataFrame.
|
|
200
|
+
|
|
201
|
+
Override to resample, change the index type, etc.
|
|
202
|
+
"""
|
|
168
203
|
start = (
|
|
169
204
|
pd.Timestamp(self.start_date)
|
|
170
205
|
if self.start_date
|
|
@@ -175,12 +210,41 @@ class StackedBarPlot:
|
|
|
175
210
|
if self.end_date
|
|
176
211
|
else self.data.index.max()
|
|
177
212
|
)
|
|
213
|
+
all_cols = list(self.series.keys()) + list(self.overlay_series.keys())
|
|
214
|
+
plot_data = self.data.loc[start:end, all_cols].dropna()
|
|
215
|
+
return plot_data * self.scale
|
|
178
216
|
|
|
179
|
-
|
|
180
|
-
|
|
217
|
+
def _format_xticks(
|
|
218
|
+
self,
|
|
219
|
+
ax: Axes,
|
|
220
|
+
plot_data: pd.DataFrame,
|
|
221
|
+
x: np.ndarray[tuple[int], np.dtype[np.intp]],
|
|
222
|
+
) -> None:
|
|
223
|
+
"""Set tick positions, labels, and rotation.
|
|
224
|
+
|
|
225
|
+
Override for custom labels (e.g. string index).
|
|
226
|
+
"""
|
|
227
|
+
dates = plot_data.index
|
|
228
|
+
step = max(1, len(dates) // 12)
|
|
229
|
+
tick_pos = list(range(0, len(dates), step))
|
|
230
|
+
tick_labels = [dates[i].strftime("%Y") for i in tick_pos]
|
|
231
|
+
ax.set_xticks( # type: ignore[reportUnknownMemberType]
|
|
232
|
+
tick_pos
|
|
233
|
+
)
|
|
234
|
+
ax.set_xticklabels( # type: ignore[reportUnknownMemberType]
|
|
235
|
+
tick_labels, rotation=self.x_rotation
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# -- Main render -----------------------------------------
|
|
181
239
|
|
|
240
|
+
def plot(self) -> Axes:
|
|
241
|
+
plot_data = self._prepare_data()
|
|
242
|
+
|
|
243
|
+
fig_kw = dict(FIG_CONFIG)
|
|
244
|
+
if self.figsize is not None:
|
|
245
|
+
fig_kw["figsize"] = self.figsize
|
|
182
246
|
fig: Figure = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
183
|
-
|
|
247
|
+
**fig_kw
|
|
184
248
|
)
|
|
185
249
|
ax: Axes = fig.add_subplot()
|
|
186
250
|
|
|
@@ -188,7 +252,6 @@ class StackedBarPlot:
|
|
|
188
252
|
labels = list(self.series.values())
|
|
189
253
|
|
|
190
254
|
x = np.arange(len(plot_data))
|
|
191
|
-
bar_width = 0.7
|
|
192
255
|
|
|
193
256
|
pos_bottom: np.ndarray[tuple[int], np.dtype[np.float64]] = np.zeros(
|
|
194
257
|
len(plot_data)
|
|
@@ -213,7 +276,7 @@ class StackedBarPlot:
|
|
|
213
276
|
x,
|
|
214
277
|
pos,
|
|
215
278
|
bottom=pos_bottom,
|
|
216
|
-
width=bar_width,
|
|
279
|
+
width=self.bar_width,
|
|
217
280
|
label=label,
|
|
218
281
|
)
|
|
219
282
|
.patches[0]
|
|
@@ -223,22 +286,24 @@ class StackedBarPlot:
|
|
|
223
286
|
x,
|
|
224
287
|
neg,
|
|
225
288
|
bottom=neg_bottom,
|
|
226
|
-
width=bar_width,
|
|
289
|
+
width=self.bar_width,
|
|
227
290
|
color=color,
|
|
228
291
|
)
|
|
229
292
|
pos_bottom = pos_bottom + pos
|
|
230
293
|
neg_bottom = neg_bottom + neg
|
|
231
294
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
295
|
+
for o_col, o_label in self.overlay_series.items():
|
|
296
|
+
o_vals = plot_data[o_col].to_numpy(dtype=np.float64)
|
|
297
|
+
ax.plot( # type: ignore[reportUnknownMemberType]
|
|
298
|
+
x,
|
|
299
|
+
o_vals,
|
|
300
|
+
color="black",
|
|
301
|
+
linewidth=1.5,
|
|
302
|
+
label=o_label,
|
|
303
|
+
zorder=10,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
self._format_xticks(ax, plot_data, x)
|
|
242
307
|
|
|
243
308
|
style_spines(
|
|
244
309
|
ax,
|
|
@@ -249,7 +314,15 @@ class StackedBarPlot:
|
|
|
249
314
|
if self.baseline:
|
|
250
315
|
style_baseline(ax, 0, **AX_CONFIG["baseline"])
|
|
251
316
|
|
|
252
|
-
|
|
317
|
+
all_labels = list(self.series.values()) + list(
|
|
318
|
+
self.overlay_series.values()
|
|
319
|
+
)
|
|
320
|
+
legend_ncol = self.legend.ncol if self.legend else None
|
|
321
|
+
ncol = (
|
|
322
|
+
legend_ncol
|
|
323
|
+
if legend_ncol is not None
|
|
324
|
+
else auto_ncol(ax, all_labels)
|
|
325
|
+
)
|
|
253
326
|
sep = self.legend.sep if self.legend else _DEFAULT_SEP
|
|
254
327
|
ax.legend( # type: ignore[reportUnknownMemberType]
|
|
255
328
|
loc="upper center",
|
|
@@ -257,6 +330,9 @@ class StackedBarPlot:
|
|
|
257
330
|
ncol=ncol,
|
|
258
331
|
)
|
|
259
332
|
|
|
333
|
+
if self.plot_size is not None:
|
|
334
|
+
adjust_figure_for_plot_size(fig, ax, self.plot_size)
|
|
335
|
+
|
|
260
336
|
fig.savefig( # type: ignore[reportUnknownMemberType]
|
|
261
337
|
self.out_path
|
|
262
338
|
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Diagnostic utilities for pipeline configuration.
|
|
2
|
+
|
|
3
|
+
Helps identify unused series in a catalog — series that
|
|
4
|
+
are downloaded but never appear as a dependency of any
|
|
5
|
+
rule or in any chart configuration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from tesorotools.dependencies.resolution import (
|
|
13
|
+
collect_document_series,
|
|
14
|
+
)
|
|
15
|
+
from tesorotools.pipeline.engine import TransformationRule
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def unused_series(
|
|
19
|
+
catalog: dict[str, Any],
|
|
20
|
+
rules: list[TransformationRule],
|
|
21
|
+
charts: dict[str, Any] | list[dict[str, Any]] | None = None,
|
|
22
|
+
) -> set[str]:
|
|
23
|
+
"""Find catalog series not referenced by rules or charts.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
catalog
|
|
28
|
+
Instrument catalog (canonical_id -> metadata).
|
|
29
|
+
rules
|
|
30
|
+
All transformation rules for the project.
|
|
31
|
+
charts
|
|
32
|
+
Chart config dict(s). Can be a single dict or a
|
|
33
|
+
list of dicts (barh, line, type_curve, tables).
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
set[str]
|
|
38
|
+
Canonical IDs present in the catalog but not used
|
|
39
|
+
as a dependency of any rule nor referenced in any
|
|
40
|
+
chart config.
|
|
41
|
+
"""
|
|
42
|
+
all_ids = set(catalog.keys())
|
|
43
|
+
|
|
44
|
+
used: set[str] = set()
|
|
45
|
+
for rule in rules:
|
|
46
|
+
used.add(rule.output_name)
|
|
47
|
+
used.update(rule.dependencies)
|
|
48
|
+
|
|
49
|
+
if charts is not None:
|
|
50
|
+
chart_list = charts if isinstance(charts, list) else [charts]
|
|
51
|
+
for name in collect_document_series(chart_list):
|
|
52
|
+
used.add(name)
|
|
53
|
+
|
|
54
|
+
return all_ids - used
|
|
@@ -91,8 +91,10 @@ def inverse_rule(
|
|
|
91
91
|
)
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def
|
|
95
|
-
|
|
94
|
+
def pct_change_rule(
|
|
95
|
+
output: str, source: str, periods: int = 12
|
|
96
|
+
) -> TransformationRule:
|
|
97
|
+
"""Periodic percentage change: source_t / source_{t-N} - 1.
|
|
96
98
|
|
|
97
99
|
Drops NaN before shifting so that *periods* counts
|
|
98
100
|
actual observations, not DataFrame rows.
|
|
@@ -137,6 +139,46 @@ def rolling_sum_rule(
|
|
|
137
139
|
)
|
|
138
140
|
|
|
139
141
|
|
|
142
|
+
def delta_rule(
|
|
143
|
+
output: str, source: str, periods: int = 1
|
|
144
|
+
) -> TransformationRule:
|
|
145
|
+
"""Level change: source_t - source_{t-periods}.
|
|
146
|
+
|
|
147
|
+
Drops NaN before shifting (mixed-frequency safe).
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def _compute(
|
|
151
|
+
df: pd.DataFrame,
|
|
152
|
+
s: str = source,
|
|
153
|
+
p: int = periods,
|
|
154
|
+
) -> pd.Series[float]:
|
|
155
|
+
clean: pd.Series[float] = df[s].dropna()
|
|
156
|
+
return clean - clean.shift(p)
|
|
157
|
+
|
|
158
|
+
return TransformationRule(
|
|
159
|
+
output_name=output,
|
|
160
|
+
dependencies=[source],
|
|
161
|
+
compute=_compute,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def cumsum_rule(output: str, source: str) -> TransformationRule:
|
|
166
|
+
"""Cumulative sum of a column.
|
|
167
|
+
|
|
168
|
+
Drops NaN before cumsum (mixed-frequency safe).
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def _compute(df: pd.DataFrame, s: str = source) -> pd.Series[float]:
|
|
172
|
+
clean: pd.Series[float] = df[s].dropna()
|
|
173
|
+
return clean.cumsum()
|
|
174
|
+
|
|
175
|
+
return TransformationRule(
|
|
176
|
+
output_name=output,
|
|
177
|
+
dependencies=[source],
|
|
178
|
+
compute=_compute,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
140
182
|
#: Registry of factory functions, keyed by YAML function name.
|
|
141
183
|
#: Projects can add custom factories at runtime.
|
|
142
184
|
FACTORIES: dict[
|
|
@@ -148,6 +190,8 @@ FACTORIES: dict[
|
|
|
148
190
|
"ratio": ratio_rule,
|
|
149
191
|
"difference": difference_rule,
|
|
150
192
|
"inverse": inverse_rule,
|
|
151
|
-
"
|
|
193
|
+
"pct_change": pct_change_rule,
|
|
152
194
|
"rolling_sum": rolling_sum_rule,
|
|
195
|
+
"delta": delta_rule,
|
|
196
|
+
"cumsum": cumsum_rule,
|
|
153
197
|
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from tesorotools.utils.template import TemplateLoader
|
|
7
|
+
|
|
8
|
+
_tags_registered = False
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _register_all_tags() -> None:
|
|
12
|
+
"""Register every YAML tag on TemplateLoader.
|
|
13
|
+
|
|
14
|
+
Called once, lazily, the first time ``read_config`` is
|
|
15
|
+
invoked with ``loader=TemplateLoader``. This removes
|
|
16
|
+
the need for consumers to import ``tesorotools.render``
|
|
17
|
+
or ``tesorotools.artists`` just for their side effects.
|
|
18
|
+
"""
|
|
19
|
+
global _tags_registered # noqa: PLW0603
|
|
20
|
+
if _tags_registered:
|
|
21
|
+
return
|
|
22
|
+
_tags_registered = True
|
|
23
|
+
|
|
24
|
+
# -- artists tags --
|
|
25
|
+
from tesorotools.artists.barh_plot import (
|
|
26
|
+
HorizontalBarChart,
|
|
27
|
+
)
|
|
28
|
+
from tesorotools.artists.line_plot import (
|
|
29
|
+
Format,
|
|
30
|
+
Legend,
|
|
31
|
+
LinePlot,
|
|
32
|
+
)
|
|
33
|
+
from tesorotools.artists.stacked import (
|
|
34
|
+
StackedAreaPlot,
|
|
35
|
+
StackedBarPlot,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
TemplateLoader.add_constructor("!line_plot", LinePlot.from_yaml)
|
|
39
|
+
TemplateLoader.add_constructor("!format", Format.from_yaml)
|
|
40
|
+
TemplateLoader.add_constructor("!legend", Legend.from_yaml)
|
|
41
|
+
TemplateLoader.add_constructor("!stacked_area", StackedAreaPlot.from_yaml)
|
|
42
|
+
TemplateLoader.add_constructor("!stacked_bar", StackedBarPlot.from_yaml)
|
|
43
|
+
TemplateLoader.add_constructor("!barh", HorizontalBarChart.from_yaml)
|
|
44
|
+
|
|
45
|
+
# -- render tags --
|
|
46
|
+
from tesorotools.render.content.images import (
|
|
47
|
+
Image,
|
|
48
|
+
Images,
|
|
49
|
+
)
|
|
50
|
+
from tesorotools.render.content.section import Section
|
|
51
|
+
from tesorotools.render.content.subtitle import Subtitle
|
|
52
|
+
from tesorotools.render.content.table import Table
|
|
53
|
+
from tesorotools.render.content.text import Text
|
|
54
|
+
from tesorotools.render.content.title import Title
|
|
55
|
+
from tesorotools.render.report import Report
|
|
56
|
+
|
|
57
|
+
TemplateLoader.add_constructor("!report", Report.from_yaml)
|
|
58
|
+
TemplateLoader.add_constructor("!section", Section.from_yaml)
|
|
59
|
+
TemplateLoader.add_constructor("!image", Image.from_yaml)
|
|
60
|
+
TemplateLoader.add_constructor("!images", Images.from_yaml)
|
|
61
|
+
TemplateLoader.add_constructor("!table", Table.from_yaml)
|
|
62
|
+
TemplateLoader.add_constructor("!text", Text.from_yaml)
|
|
63
|
+
TemplateLoader.add_constructor("!title", Title.from_yaml)
|
|
64
|
+
TemplateLoader.add_constructor("!subtitle", Subtitle.from_yaml)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def clean_config_dicts(
|
|
68
|
+
config_dicts: dict[str, Any],
|
|
69
|
+
) -> dict[str, Any]:
|
|
70
|
+
return {k: v for k, v in config_dicts.items() if not k.startswith(".")}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def read_config(
|
|
74
|
+
config_file: Path,
|
|
75
|
+
loader: type[yaml.FullLoader] | None = None,
|
|
76
|
+
clean: bool = True,
|
|
77
|
+
) -> Any:
|
|
78
|
+
actual_loader: type[yaml.FullLoader] = (
|
|
79
|
+
yaml.FullLoader if loader is None else TemplateLoader
|
|
80
|
+
)
|
|
81
|
+
if actual_loader is TemplateLoader:
|
|
82
|
+
_register_all_tags()
|
|
83
|
+
with open(config_file, encoding="utf8") as file:
|
|
84
|
+
config_dict: Any = yaml.load(file, Loader=actual_loader)
|
|
85
|
+
if clean and isinstance(config_dict, dict):
|
|
86
|
+
config_dict = clean_config_dicts(config_dict) # type: ignore[reportUnknownArgumentType]
|
|
87
|
+
return config_dict
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def merge(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]:
|
|
91
|
+
# a overrides
|
|
92
|
+
for key in b:
|
|
93
|
+
if key in a:
|
|
94
|
+
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
|
95
|
+
merge(a[key], b[key])
|
|
96
|
+
else:
|
|
97
|
+
a[key] = b[key]
|
|
98
|
+
return a
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from tesorotools.artists.line_plot import Format, Legend, LinePlot
|
|
2
|
-
from tesorotools.artists.stacked import StackedAreaPlot, StackedBarPlot
|
|
3
|
-
from tesorotools.utils.config import TemplateLoader
|
|
4
|
-
|
|
5
|
-
TemplateLoader.add_constructor("!line_plot", LinePlot.from_yaml)
|
|
6
|
-
TemplateLoader.add_constructor("!format", Format.from_yaml)
|
|
7
|
-
TemplateLoader.add_constructor("!legend", Legend.from_yaml)
|
|
8
|
-
TemplateLoader.add_constructor("!stacked_area", StackedAreaPlot.from_yaml)
|
|
9
|
-
TemplateLoader.add_constructor("!stacked_bar", StackedBarPlot.from_yaml)
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import matplotlib.style
|
|
2
|
-
|
|
3
|
-
from tesorotools.artists.barh_plot import HorizontalBarChart
|
|
4
|
-
from tesorotools.utils.config import TemplateLoader
|
|
5
|
-
|
|
6
|
-
from ..utils.globals import STYLE_SHEET
|
|
7
|
-
|
|
8
|
-
matplotlib.style.use(STYLE_SHEET)
|
|
9
|
-
TemplateLoader.add_constructor("!barh", HorizontalBarChart.from_yaml)
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from tesorotools.render.content.images import Image, Images
|
|
2
|
-
from tesorotools.render.content.section import Section
|
|
3
|
-
from tesorotools.render.content.subtitle import Subtitle
|
|
4
|
-
from tesorotools.render.content.table import Table
|
|
5
|
-
from tesorotools.render.content.text import Text
|
|
6
|
-
from tesorotools.render.content.title import Title
|
|
7
|
-
from tesorotools.render.report import Report
|
|
8
|
-
from tesorotools.utils.template import TemplateLoader
|
|
9
|
-
|
|
10
|
-
TemplateLoader.add_constructor("!report", Report.from_yaml)
|
|
11
|
-
TemplateLoader.add_constructor("!section", Section.from_yaml)
|
|
12
|
-
TemplateLoader.add_constructor("!image", Image.from_yaml)
|
|
13
|
-
TemplateLoader.add_constructor("!images", Images.from_yaml)
|
|
14
|
-
TemplateLoader.add_constructor("!table", Table.from_yaml)
|
|
15
|
-
TemplateLoader.add_constructor("!text", Text.from_yaml)
|
|
16
|
-
TemplateLoader.add_constructor("!title", Title.from_yaml)
|
|
17
|
-
TemplateLoader.add_constructor("!subtitle", Subtitle.from_yaml)
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
import yaml
|
|
5
|
-
|
|
6
|
-
from tesorotools.utils.template import TemplateLoader
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def clean_config_dicts(
|
|
10
|
-
config_dicts: dict[str, Any],
|
|
11
|
-
) -> dict[str, Any]:
|
|
12
|
-
return {k: v for k, v in config_dicts.items() if not k.startswith(".")}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def read_config(
|
|
16
|
-
config_file: Path,
|
|
17
|
-
loader: type[yaml.FullLoader] | None = None,
|
|
18
|
-
clean: bool = True,
|
|
19
|
-
) -> Any:
|
|
20
|
-
actual_loader: type[yaml.FullLoader] = (
|
|
21
|
-
yaml.FullLoader if loader is None else TemplateLoader
|
|
22
|
-
)
|
|
23
|
-
with open(config_file, encoding="utf8") as file:
|
|
24
|
-
config_dict: Any = yaml.load(file, Loader=actual_loader)
|
|
25
|
-
if clean and isinstance(config_dict, dict):
|
|
26
|
-
config_dict = clean_config_dicts(config_dict) # type: ignore[reportUnknownArgumentType]
|
|
27
|
-
return config_dict
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def merge(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]:
|
|
31
|
-
# a overrides
|
|
32
|
-
for key in b:
|
|
33
|
-
if key in a:
|
|
34
|
-
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
|
35
|
-
merge(a[key], b[key])
|
|
36
|
-
else:
|
|
37
|
-
a[key] = b[key]
|
|
38
|
-
return a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/barh_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/artists/type_curve.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
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/fonts/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/assets/tesoro.mplstyle
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/debug.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/data_sources/lseg.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/database/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/dependencies/node.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/dependencies/resolution.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
|
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/content.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/images.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/section.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/subtitle.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/table.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/text.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.25 → tesorotools_python-0.0.27}/src/tesorotools/render/content/title.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
|