tesorotools-python 0.0.14__tar.gz → 0.0.16__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 (52) hide show
  1. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/PKG-INFO +1 -1
  2. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/pyproject.toml +1 -1
  3. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/artists/barh_plot.py +2 -2
  4. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/artists/line_plot.py +4 -2
  5. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/artists/table.py +5 -4
  6. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/artists/type_curve.py +4 -2
  7. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/convert.py +9 -3
  8. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/__init__.py +4 -0
  9. tesorotools_python-0.0.14/tesorotools/render/introduction.py → tesorotools_python-0.0.16/tesorotools/render/content/subtitle.py +8 -4
  10. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/table.py +21 -12
  11. tesorotools_python-0.0.14/tesorotools/render/headline.py → tesorotools_python-0.0.16/tesorotools/render/content/title.py +8 -8
  12. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/report.py +3 -1
  13. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/.gitignore +0 -0
  14. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/__init__.py +0 -0
  15. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/artists/__init__.py +0 -0
  16. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/README.md +0 -0
  17. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
  18. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
  19. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
  20. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
  21. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
  22. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
  23. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
  24. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
  25. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/fonts/README.md +0 -0
  26. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/plots.yaml +0 -0
  27. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/assets/tesoro.mplstyle +0 -0
  28. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/data_sources/README.md +0 -0
  29. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/data_sources/__init__.py +0 -0
  30. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/data_sources/debug.py +0 -0
  31. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/data_sources/lseg.py +0 -0
  32. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/database/__init__.py +0 -0
  33. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/database/push.py +0 -0
  34. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/dependencies/__init__.py +0 -0
  35. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/dependencies/functions.py +0 -0
  36. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/dependencies/node.py +0 -0
  37. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/dependencies/resolution.py +0 -0
  38. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/main.py +0 -0
  39. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/offsets/__init__.py +0 -0
  40. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/offsets/offsets.py +0 -0
  41. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/offsets/outliers.py +0 -0
  42. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/__init__.py +0 -0
  43. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/content.py +0 -0
  44. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/images.py +0 -0
  45. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/section.py +0 -0
  46. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/render/content/text.py +0 -0
  47. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/utils/__init__.py +0 -0
  48. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/utils/config.py +0 -0
  49. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/utils/globals.py +0 -0
  50. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/utils/matplotlib.py +0 -0
  51. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/tesorotools/utils/series.py +0 -0
  52. {tesorotools_python-0.0.14 → tesorotools_python-0.0.16}/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.14
3
+ Version: 0.0.16
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.14"
3
+ version = "0.0.16"
4
4
  dependencies = ["psycopg2", "SQLAlchemy", "pandas", "matplotlib", "pyarrow", "python-docx", "openpyxl", "PyYAML", "babel", "eikon", "lseg-data"]
5
5
 
6
6
  [build-system]
@@ -289,7 +289,7 @@ def _normalize_from_flash(
289
289
 
290
290
 
291
291
  def plot_barh_charts_from_flash(
292
- flash: pd.DataFrame, config_dicts: dict[str, dict]
292
+ out_path: Path, flash: pd.DataFrame, config_dicts: dict[str, dict]
293
293
  ):
294
294
  for name, config in config_dicts.items():
295
295
  blocks: dict[str, Any] = config.get("blocks", None)
@@ -306,5 +306,5 @@ def plot_barh_charts_from_flash(
306
306
  units_bar=config["format"]["units"],
307
307
  units_axis=config.get("axis_format", {"units": ""})["units"],
308
308
  )
309
- out_file = Path(DEBUG / "barh" / f"{name}.png")
309
+ out_file = out_path / f"{name}.png"
310
310
  _plot_barh_chart(out_file, standard_dict, alias, **config)
@@ -100,7 +100,9 @@ def plot_line_chart(
100
100
  fig.savefig(out_name)
101
101
 
102
102
 
103
- def plot_line_charts(data: pd.DataFrame, config_dicts: dict[str, Any]):
103
+ def plot_line_charts(
104
+ out_path: Path, data: pd.DataFrame, config_dicts: dict[str, Any]
105
+ ):
104
106
  for name, config in config_dicts.items():
105
107
  start_date: pd.Timestamp = pd.to_datetime(config["start_date"])
106
108
  end_date_str: str | None = config["end_date"]
@@ -114,7 +116,7 @@ def plot_line_charts(data: pd.DataFrame, config_dicts: dict[str, Any]):
114
116
  slice(start_date, end_date), series.keys()
115
117
  ]
116
118
  trimmed_data = trimmed_data.rename(columns=series)
117
- out_name: Path = DEBUG / "line" / f"{name}.png"
119
+ out_name: Path = out_path / f"{name}.png"
118
120
  plot_line_chart(out_name, trimmed_data, **config)
119
121
 
120
122
 
@@ -1,4 +1,5 @@
1
1
  from math import floor
2
+ from pathlib import Path
2
3
  from typing import Any
3
4
 
4
5
  import pandas as pd
@@ -186,7 +187,7 @@ def generate_table(table_data: pd.DataFrame, table_cfg: dict[str, Any]):
186
187
 
187
188
 
188
189
  def generate_tables_from_flash(
189
- flash: pd.DataFrame, config_dicts: dict[str, dict]
190
+ out_path: Path, flash: pd.DataFrame, config_dicts: dict[str, dict]
190
191
  ):
191
192
  for table_name, table_cfg in config_dicts.items():
192
193
  series: list[str] = list(collect_series(table_cfg))
@@ -194,6 +195,6 @@ def generate_tables_from_flash(
194
195
  formatted_table, color_table, shade_table = generate_table(
195
196
  table_data, table_cfg
196
197
  )
197
- formatted_table.to_feather(DEBUG / "table" / f"{table_name}.feather")
198
- color_table.to_feather(DEBUG / "table" / f"{table_name}_color.feather")
199
- shade_table.to_feather(DEBUG / "table" / f"{table_name}_shade.feather")
198
+ formatted_table.to_feather(out_path / f"{table_name}.feather")
199
+ color_table.to_feather(out_path / f"{table_name}_color.feather")
200
+ shade_table.to_feather(out_path / f"{table_name}_shade.feather")
@@ -199,7 +199,9 @@ def plot_type_curve(
199
199
 
200
200
 
201
201
  # data is expected to be a simple time series data, columns are series and rows represents dates
202
- def plot_type_curves(data: pd.DataFrame, config_dicts: dict[str, Any]):
202
+ def plot_type_curves(
203
+ out_path: Path, data: pd.DataFrame, config_dicts: dict[str, Any]
204
+ ):
203
205
  for name, config in config_dicts.items():
204
206
  if not name.startswith("."): # aux entries
205
207
  series: dict[str, str] = config["series"]
@@ -211,6 +213,6 @@ def plot_type_curves(data: pd.DataFrame, config_dicts: dict[str, Any]):
211
213
  trimmed_data: pd.DataFrame = trimmed_data.rename(columns=series)
212
214
  plot_type_curve(
213
215
  data=trimmed_data,
214
- out_file=DEBUG / "type_curve" / f"{name}.png",
216
+ out_file=out_path / f"{name}.png",
215
217
  **config,
216
218
  )
@@ -37,13 +37,19 @@ def cheap_convert(old_file: Path) -> pd.DataFrame:
37
37
  trimmed = old.loc[(slice(None), "no", "absolute", "value"), :].copy()
38
38
 
39
39
  old = old.loc[
40
- (slice(None), ["day", "mtd", "week", "year"], slice(None), slice(None)),
40
+ (
41
+ slice(None),
42
+ ["day", "mtd", "week", "year", "tariff_crisis"],
43
+ slice(None),
44
+ slice(None),
45
+ ),
41
46
  :,
42
47
  ]
43
48
  old.index = index_replace("day", "bday", old.index)
44
49
  old.index = index_replace("week", "ftd", old.index)
45
50
  old.index = index_replace("year", "ytd", old.index)
46
51
  old.index = index_replace("roll_var", "roll_std", old.index)
52
+ old.index = index_replace("tariff_crisis", "2025-04-02", old.index)
47
53
 
48
54
  trimmed.index = index_replace("absolute", "no", trimmed.index)
49
55
 
@@ -52,7 +58,7 @@ def cheap_convert(old_file: Path) -> pd.DataFrame:
52
58
 
53
59
 
54
60
  if __name__ == "__main__":
55
- preprocess = False
61
+ preprocess = True
56
62
  barh_config_dicts = read_config(Path("examples") / "barh_plots.yaml")
57
63
  line_config_dicts = read_config(Path("examples") / "line_plots.yaml")
58
64
  type_config_dicts = read_config(Path("examples") / "type_curves.yaml")
@@ -81,7 +87,7 @@ if __name__ == "__main__":
81
87
  independent_full_df,
82
88
  dependent_trimmed_df,
83
89
  offsets_config,
84
- force_trim=True,
90
+ # force_trim=True,
85
91
  )
86
92
  full_df.to_feather("derivates.feather")
87
93
 
@@ -1,7 +1,9 @@
1
1
  from tesorotools.render.content.images import Image, Images
2
2
  from tesorotools.render.content.section import Section
3
+ from tesorotools.render.content.subtitle import Subtitle
3
4
  from tesorotools.render.content.table import Table
4
5
  from tesorotools.render.content.text import Text
6
+ from tesorotools.render.content.title import Title
5
7
  from tesorotools.render.report import Report
6
8
  from tesorotools.utils.template import TemplateLoader
7
9
 
@@ -11,3 +13,5 @@ TemplateLoader.add_constructor("!image", Image.from_yaml)
11
13
  TemplateLoader.add_constructor("!images", Images.from_yaml)
12
14
  TemplateLoader.add_constructor("!table", Table.from_yaml)
13
15
  TemplateLoader.add_constructor("!text", Text.from_yaml)
16
+ TemplateLoader.add_constructor("!title", Title.from_yaml)
17
+ TemplateLoader.add_constructor("!subtitle", Subtitle.from_yaml)
@@ -3,18 +3,22 @@ from typing import Any, Self
3
3
 
4
4
  from babel.dates import format_datetime
5
5
  from docx.document import Document
6
+ from yaml import MappingNode
6
7
 
8
+ from tesorotools.utils.config import TemplateLoader
7
9
 
8
- class Introduction:
10
+
11
+ class Subtitle:
9
12
  def __init__(self, date_time: datetime | None = None) -> None:
10
13
  if date_time is None:
11
14
  date_time: datetime = datetime.now()
12
15
  self._date_time: datetime = date_time
13
16
 
14
17
  @classmethod
15
- def from_dict(cls, introduction_cfg: dict[str, Any]) -> Self:
18
+ def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
19
+ subtitle_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
16
20
  # parse date
17
- date_cfg: str | date | None = introduction_cfg.get("date", None)
21
+ date_cfg: str | date | None = subtitle_cfg.get("date", None)
18
22
  if date_cfg is None:
19
23
  date_time: datetime = datetime.now()
20
24
  elif isinstance(date_cfg, date):
@@ -23,7 +27,7 @@ class Introduction:
23
27
  date_time: datetime = datetime.strptime(date_cfg, "%Y-%m-%d")
24
28
 
25
29
  # parse hour
26
- hour_cfg: str | None = introduction_cfg.get("hour", None)
30
+ hour_cfg: str | None = subtitle_cfg.get("hour", None)
27
31
  if hour_cfg is not None:
28
32
  hour_str, minute_str = hour_cfg.split(sep=":")
29
33
  date_time = date_time.replace(
@@ -4,12 +4,12 @@ from typing import Any, Self
4
4
  import numpy as np
5
5
  import pandas as pd
6
6
  from docx.document import Document
7
- from docx.enum.table import WD_ALIGN_VERTICAL
7
+ from docx.enum.table import WD_ALIGN_VERTICAL, WD_TABLE_ALIGNMENT
8
8
  from docx.enum.text import WD_ALIGN_PARAGRAPH
9
9
  from docx.oxml import OxmlElement, parse_xml
10
10
  from docx.oxml.ns import nsdecls, qn
11
11
  from docx.shared import Inches, Pt, RGBColor
12
- from docx.table import Table
12
+ from docx.table import Table as TableDocx
13
13
  from docx.table import _Cell as TableCell
14
14
  from yaml import MappingNode
15
15
 
@@ -69,7 +69,7 @@ def _style_horizontal_blocks_header(cell: TableCell):
69
69
  cell.paragraphs[0].runs[0].font.size = Pt(12)
70
70
 
71
71
 
72
- def _horizontal_blocks_header(columns: pd.MultiIndex, table_docx: Table):
72
+ def _horizontal_blocks_header(columns: pd.MultiIndex, table_docx: TableDocx):
73
73
  column_counter: int = 1
74
74
  blocks: list[str] = list(columns.get_level_values(level=0).unique())
75
75
  for block in blocks:
@@ -92,7 +92,7 @@ def _style_column_names(cell: TableCell):
92
92
 
93
93
 
94
94
  def _fill_column_names(
95
- table: pd.DataFrame, table_docx: Table, horizontal: bool
95
+ table: pd.DataFrame, table_docx: TableDocx, horizontal: bool
96
96
  ):
97
97
  if horizontal:
98
98
  start_row: int = 1
@@ -115,13 +115,13 @@ def _style_index_names(cell: TableCell):
115
115
 
116
116
 
117
117
  def _fill_index_names(
118
- index: pd.Index | pd.MultiIndex, table_docx: Table, horizontal: bool
118
+ index: pd.Index | pd.MultiIndex, table_docx: TableDocx, horizontal: bool
119
119
  ):
120
120
  start_row: int = 2 if horizontal else 1
121
121
 
122
122
  index_names: pd.Index = (
123
123
  index
124
- if (horizontal or isinstance(index, pd.Index))
124
+ if (horizontal or (not isinstance(index, pd.MultiIndex)))
125
125
  else index.get_level_values(level=1)
126
126
  )
127
127
 
@@ -132,7 +132,7 @@ def _fill_index_names(
132
132
 
133
133
 
134
134
  # we only separate blocks in vertically stacked tables
135
- def _separate_blocks(index: pd.MultiIndex, table_docx: Table):
135
+ def _separate_blocks(index: pd.MultiIndex, table_docx: TableDocx):
136
136
  blocks: list[str] = list(index.get_level_values(level=0).unique())
137
137
  previous_rows = 0
138
138
  for block in blocks[:-1]:
@@ -186,7 +186,7 @@ def _fill_content(
186
186
  table: pd.DataFrame,
187
187
  color_table: pd.DataFrame,
188
188
  shade_table: pd.DataFrame,
189
- table_docx: Table,
189
+ table_docx: TableDocx,
190
190
  horizontal: bool,
191
191
  ):
192
192
  start_row: int = 2 if horizontal else 1
@@ -209,7 +209,7 @@ def _fill_content(
209
209
  _style_content(cell)
210
210
 
211
211
 
212
- def _style_table(table_docx: Table):
212
+ def _style_table(table_docx: TableDocx):
213
213
  table_docx.style = RENDER_CONFIG.get("style", None)
214
214
  table_docx.autofit = RENDER_CONFIG["autofit"]
215
215
 
@@ -221,10 +221,10 @@ def render_table(
221
221
  document: Document,
222
222
  block_sep: bool,
223
223
  **kwargs,
224
- ) -> Table:
224
+ ) -> TableDocx:
225
225
 
226
226
  horizontal: bool = isinstance(table.columns, pd.MultiIndex)
227
- table_docx: Table = document.add_table(
227
+ table_docx: TableDocx = document.add_table(
228
228
  rows=len(table.index) + table.columns.nlevels,
229
229
  cols=len(table.columns) + 1,
230
230
  )
@@ -237,6 +237,7 @@ def render_table(
237
237
  if block_sep:
238
238
  _separate_blocks(table.index, table_docx)
239
239
  _fill_content(table, color_table, shade_table, table_docx, horizontal)
240
+ table_docx.alignment = WD_TABLE_ALIGNMENT.CENTER
240
241
  return document
241
242
 
242
243
 
@@ -274,7 +275,15 @@ class Table:
274
275
  table_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
275
276
  root_path: Path = loader.imports["table"]
276
277
  file_prefix: str = table_cfg.pop("id")
277
- data_file: Path = root_path / f"{file_prefix}_data.feather"
278
+
279
+ # the data file has an optional "_data" suffix
280
+ # if a file with the suffix exists, it will be preferred
281
+ data_file_suffix: Path = root_path / f"{file_prefix}_data.feather"
282
+ if data_file_suffix.exists():
283
+ data_file: Path = data_file_suffix
284
+ else:
285
+ data_file: Path = root_path / f"{file_prefix}.feather"
286
+
278
287
  color_file: Path = root_path / f"{file_prefix}_color.feather"
279
288
  shade_file: Path = root_path / f"{file_prefix}_shade.feather"
280
289
  return cls(
@@ -1,9 +1,12 @@
1
1
  from typing import Any, Self
2
2
 
3
3
  from docx.document import Document
4
+ from yaml import MappingNode
4
5
 
6
+ from tesorotools.utils.config import TemplateLoader
5
7
 
6
- class HeadLine:
8
+
9
+ class Title:
7
10
  def __init__(
8
11
  self, title: str | None = None, comment: str | None = None
9
12
  ) -> None:
@@ -11,9 +14,10 @@ class HeadLine:
11
14
  self._comment: str = "" if comment is None else comment
12
15
 
13
16
  @classmethod
14
- def from_dict(cls, headline_cfg: dict[str, Any]) -> Self:
15
- title: str | None = headline_cfg.get("title", None)
16
- comment: str | None = headline_cfg.get("comment", None)
17
+ def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
18
+ title_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
19
+ title: str | None = title_cfg.get("title", None)
20
+ comment: str | None = title_cfg.get("comment", None)
17
21
  return cls(title, comment)
18
22
 
19
23
  def render(self, document: Document) -> Document:
@@ -31,10 +35,6 @@ class HeadLine:
31
35
  def title(self) -> str:
32
36
  return self._title
33
37
 
34
- @title.setter
35
- def title(self, title) -> None:
36
- self._title = title
37
-
38
38
  @property
39
39
  def comment(self) -> str:
40
40
  return self._comment
@@ -4,6 +4,7 @@ from docx.document import Document
4
4
  from yaml import Loader, MappingNode
5
5
 
6
6
  from tesorotools.render.content.content import Content
7
+ from tesorotools.render.content.title import Title
7
8
 
8
9
 
9
10
  class Report:
@@ -25,5 +26,6 @@ class Report:
25
26
  document._body.clear_content()
26
27
  for _, content in self.contents.items():
27
28
  content.render(document)
28
- document.add_paragraph()
29
+ if not isinstance(content, Title):
30
+ document.add_paragraph()
29
31
  return document