tesorotools-python 0.0.28__tar.gz → 0.0.29__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 (64) hide show
  1. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/PKG-INFO +1 -1
  2. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/pyproject.toml +1 -1
  3. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/line_plot.py +107 -4
  4. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/.gitignore +0 -0
  5. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/__init__.py +0 -0
  6. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/__init__.py +0 -0
  7. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/barh.md +0 -0
  8. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/barh_plot.py +0 -0
  9. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/stacked.py +0 -0
  10. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/table.py +0 -0
  11. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/artists/type_curve.py +0 -0
  12. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/README.md +0 -0
  13. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
  14. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
  15. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
  16. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
  17. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
  18. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
  19. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
  20. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
  21. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/README.md +0 -0
  22. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/plots.yaml +0 -0
  23. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/assets/tesoro.mplstyle +0 -0
  24. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/convert.py +0 -0
  25. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/README.md +0 -0
  26. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/__init__.py +0 -0
  27. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/debug.py +0 -0
  28. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/lseg.py +0 -0
  29. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/database/__init__.py +0 -0
  30. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/database/local.py +0 -0
  31. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/database/push.py +0 -0
  32. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/__init__.py +0 -0
  33. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/node.py +0 -0
  34. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/resolution.py +0 -0
  35. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/main.py +0 -0
  36. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/offsets/__init__.py +0 -0
  37. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/offsets/offsets.py +0 -0
  38. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/offsets/outliers.py +0 -0
  39. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/__init__.py +0 -0
  40. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/diagnose.py +0 -0
  41. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/engine.py +0 -0
  42. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/rules.py +0 -0
  43. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/providers/__init__.py +0 -0
  44. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/providers/base.py +0 -0
  45. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/providers/bde.py +0 -0
  46. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/py.typed +0 -0
  47. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/__init__.py +0 -0
  48. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/__init__.py +0 -0
  49. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/content.py +0 -0
  50. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/images.py +0 -0
  51. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/section.py +0 -0
  52. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/subtitle.py +0 -0
  53. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/table.py +0 -0
  54. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/text.py +0 -0
  55. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/content/title.py +0 -0
  56. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/render/report.py +0 -0
  57. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/__init__.py +0 -0
  58. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/config.py +0 -0
  59. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/format.py +0 -0
  60. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/globals.py +0 -0
  61. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/matplotlib.py +0 -0
  62. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/series.py +0 -0
  63. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/tesorotools/utils/shortcuts.py +0 -0
  64. {tesorotools_python-0.0.28 → tesorotools_python-0.0.29}/src/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.28
3
+ Version: 0.0.29
4
4
  Requires-Python: >=3.13
5
5
  Requires-Dist: babel>=2.17
6
6
  Requires-Dist: eikon>=1.1
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "tesorotools-python"
3
3
  requires-python = ">=3.13"
4
- version = "0.0.28"
4
+ version = "0.0.29"
5
5
  dependencies = [
6
6
  # database and ORM
7
7
  "psycopg[binary]>=3.1",
@@ -96,6 +96,100 @@ def auto_ncol(ax: Axes, labels: list[str]) -> int:
96
96
  return min(ncol, len(labels))
97
97
 
98
98
 
99
+ def annotate_last_values(
100
+ ax: Axes,
101
+ plot_data: pd.DataFrame,
102
+ *,
103
+ decimals: int,
104
+ units: str,
105
+ series_styles: dict[str, dict[str, Any]] | None = None,
106
+ annotate_color: str | None = None,
107
+ ) -> None:
108
+ """Label the last non-NaN value of each column on the right.
109
+
110
+ Colour priority (highest first): ``annotate_color``
111
+ (global override), ``series_styles[col]['color']``,
112
+ the Matplotlib line colour.
113
+
114
+ Labels are packed vertically in display space so that
115
+ series ending at nearly the same value do not overlap.
116
+ The x-axis limit is extended by the widest label so
117
+ the text is not clipped by the axes frame.
118
+ """
119
+ fig = ax.get_figure()
120
+ if fig is None:
121
+ return
122
+ styles = series_styles or {}
123
+
124
+ lines_by_label = {str(line.get_label()): line for line in ax.lines}
125
+ entries: list[tuple[Any, float, str, Any]] = []
126
+ for col in plot_data.columns:
127
+ col_series: pd.Series[float] = plot_data[col].dropna()
128
+ if col_series.empty:
129
+ continue
130
+ last_date = col_series.index[-1]
131
+ last_val = float(col_series.iloc[-1])
132
+ if annotate_color is not None:
133
+ color: Any = annotate_color
134
+ else:
135
+ override = styles.get(col, {}).get("color")
136
+ if override is not None:
137
+ color = override
138
+ else:
139
+ line = lines_by_label.get(col)
140
+ color = line.get_color() if line is not None else "black"
141
+ entries.append((last_date, last_val, col, color))
142
+
143
+ if not entries:
144
+ return
145
+
146
+ fig.canvas.draw() # type: ignore[reportUnknownMemberType]
147
+ renderer = fig.canvas.get_renderer() # type: ignore[reportUnknownMemberType]
148
+ trans = ax.transData
149
+
150
+ sample = ax.text( # type: ignore[reportUnknownMemberType]
151
+ 0, 0, "0"
152
+ )
153
+ text_height = (
154
+ sample.get_window_extent(renderer).height # type: ignore[reportUnknownArgumentType]
155
+ * 1.15
156
+ )
157
+ sample.remove()
158
+
159
+ entries.sort(key=lambda e: e[1])
160
+ placements: list[float] = []
161
+ prev_y = -float("inf")
162
+ for _, val, _, _ in entries:
163
+ y_display: float = trans.transform((0, val))[1] # type: ignore[reportUnknownArgumentType]
164
+ if y_display - prev_y < text_height:
165
+ y_display = prev_y + text_height
166
+ placements.append(y_display)
167
+ prev_y = y_display
168
+
169
+ max_label_width = 0.0
170
+ for (date, val, _, color), packed_y in zip(entries, placements):
171
+ orig_y: float = trans.transform((0, val))[1] # type: ignore[reportUnknownArgumentType]
172
+ dy_pts = (packed_y - orig_y) * 72.0 / fig.dpi
173
+ text = format_annotation(val, decimals, units)
174
+ t = ax.annotate( # type: ignore[reportUnknownMemberType]
175
+ text,
176
+ xy=(date, val),
177
+ xytext=(5, dy_pts),
178
+ textcoords="offset points",
179
+ color=color,
180
+ va="center",
181
+ ha="left",
182
+ )
183
+ width = t.get_window_extent(renderer).width # type: ignore[reportUnknownArgumentType]
184
+ max_label_width = max(max_label_width, width)
185
+
186
+ inv = trans.inverted()
187
+ x0: float = inv.transform((0, 0))[0] # type: ignore[reportUnknownArgumentType]
188
+ x1: float = inv.transform((max_label_width + 10, 0))[0] # type: ignore[reportUnknownArgumentType]
189
+ xmin, xmax = ax.get_xlim()
190
+ ax.set_xlim(xmin, xmax + (x1 - x0))
191
+
192
+
99
193
  def style_spines(
100
194
  ax: Axes,
101
195
  decimals: int,
@@ -290,6 +384,7 @@ class LinePlot:
290
384
  end_date: datetime.datetime | None = None,
291
385
  base_100: bool = False,
292
386
  annotate: bool = False,
387
+ annotate_color: str | None = None,
293
388
  baseline: bool = False,
294
389
  format: Format | None = None,
295
390
  legend: Legend | None = None,
@@ -320,7 +415,8 @@ class LinePlot:
320
415
  raise ValueError("series is required")
321
416
 
322
417
  self.base_100 = base_100
323
- self.annotate = annotate # unused for the moment
418
+ self.annotate = annotate
419
+ self.annotate_color = annotate_color
324
420
  self.format = format
325
421
  self.start_date = start_date
326
422
  self.end_date = end_date
@@ -379,10 +475,17 @@ class LinePlot:
379
475
  else:
380
476
  plot_data.plot(ax=ax)
381
477
 
382
- if self.annotate: # not implemented yet
383
- pass
384
-
385
478
  assert self.format is not None
479
+ if self.annotate:
480
+ annotate_last_values(
481
+ ax,
482
+ plot_data,
483
+ decimals=self.format.decimals,
484
+ units=self.format.units,
485
+ series_styles=self.series_styles,
486
+ annotate_color=self.annotate_color,
487
+ )
488
+
386
489
  style_spines( # maybe make this function accept a Format object
387
490
  ax,
388
491
  decimals=self.format.decimals,