tesorotools-python 0.0.9__py2.py3-none-any.whl → 0.0.11__py2.py3-none-any.whl

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/__init__.py CHANGED
@@ -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)
@@ -1,10 +1,14 @@
1
+ import datetime
1
2
  import locale
2
3
  from pathlib import Path
3
- from typing import Any
4
+ from typing import Any, Self
4
5
 
5
6
  import matplotlib.pyplot as plt
6
7
  import pandas as pd
7
8
  from matplotlib.ticker import FuncFormatter
9
+ from yaml.nodes import MappingNode
10
+
11
+ from tesorotools.utils.config import TemplateLoader
8
12
 
9
13
  locale.setlocale(locale.LC_ALL, "")
10
14
 
@@ -112,3 +116,128 @@ def plot_line_charts(data: pd.DataFrame, config_dicts: dict[str, Any]):
112
116
  trimmed_data = trimmed_data.rename(columns=series)
113
117
  out_name: Path = DEBUG / "line" / f"{name}.png"
114
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(data_file, color_file, shade_file, **table_cfg)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tesorotools-python
3
- Version: 0.0.9
3
+ Version: 0.0.11
4
4
  Requires-Dist: babel
5
5
  Requires-Dist: eikon
6
6
  Requires-Dist: lseg-data
@@ -1,9 +1,9 @@
1
- tesorotools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1
+ tesorotools/__init__.py,sha256=rkjAQCbiNmcFq9qlYUZFEOhPGXd5ISqS2PSfu1V1khQ,311
2
2
  tesorotools/convert.py,sha256=qk4N-AZpKlmTD9L9SG0E43jqW27DJTHTu_Y1wehvETQ,3334
3
3
  tesorotools/main.py,sha256=ZxDyqBD_0zlfcdTZp84UPxlK3_O-9xfexDjLJVu4L5U,1357
4
4
  tesorotools/artists/__init__.py,sha256=exObsPqXdP6IaRn4QPkLRXUN67iOPNXtDgDAOoMxJoA,105
5
5
  tesorotools/artists/barh_plot.py,sha256=3KkoT0DLkctzYNMV3Pz1EcAT9i29YGYSkTLTFNb3dv8,9461
6
- tesorotools/artists/line_plot.py,sha256=BqOHnmVB6fL0ElN_WDpvCzP0L7O2-mp6ULfHo3qwiCI,3405
6
+ tesorotools/artists/line_plot.py,sha256=TlQ5AFeQ5u4eNbjLkjJBH6ztKK8xAmccdVXqQiT4vpk,7553
7
7
  tesorotools/artists/table.py,sha256=CjyjWivn5YrNP5m8_eWi-ZA7R1ts3aIhATrYMEgTuOU,7651
8
8
  tesorotools/artists/type_curve.py,sha256=VGFCCyMVNwjliLK9usyw_nLC6emKcJYOSV1pmWJQNXk,6463
9
9
  tesorotools/assets/README.md,sha256=pB77vD_MvE26ftVNcZuj3iS0y-6g-WzqHb5OScDkQ3I,289
@@ -31,7 +31,7 @@ tesorotools/dependencies/resolution.py,sha256=Ctzzsm37YaJoyJQrbo9UcR0Sw6gw4xBnJO
31
31
  tesorotools/offsets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  tesorotools/offsets/offsets.py,sha256=h8hEoQ6KEsANlMqbCmSImdymllsjFgrBseKcB9gyMLQ,14068
33
33
  tesorotools/offsets/outliers.py,sha256=37enrVwungs7KrNE7FYiPd-Eo3-5d9OHFeNXy6X7URo,415
34
- tesorotools/render/__init__.py,sha256=XiuKC7mwvsXMTpPcInPaVnVdv2BzWwM-dcVY8dXhkjA,575
34
+ tesorotools/render/__init__.py,sha256=EN1aJcJq6O1AAxCAbpKuRvNBv8UVfUzzaHpNjGh929s,682
35
35
  tesorotools/render/headline.py,sha256=haKi1g2BvjrbLDMfsn6BaHJjGqPDmwlqH9e3pb9nzCE,1264
36
36
  tesorotools/render/introduction.py,sha256=uSeuOWiauLiX_hZYULFUiyw7JQwMHBIexr15-sW1ayA,1802
37
37
  tesorotools/render/report.py,sha256=rdbgeO1Ol6TM2mUkJeaAiJht_8Fcc9ZT5K_qbOXgj2Q,885
@@ -39,13 +39,14 @@ tesorotools/render/content/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
39
39
  tesorotools/render/content/content.py,sha256=m3USJXle5NctlQJs-d0tgSCvb-aNoCy2v7sZFqHZ1B8,409
40
40
  tesorotools/render/content/images.py,sha256=C0Tkrud17MqH_doenn5H-917kT6XCUdCmNek9CZkEXc,5040
41
41
  tesorotools/render/content/section.py,sha256=EugPBsb5mAWKRONAHQU-qCbKFQ419XU2vDJHQ0s_i3U,1682
42
- tesorotools/render/content/table.py,sha256=JjzmFI4CAENnSj8S0YX7-Y2HJtjZXZ6ERer3pa0Ju1c,9436
42
+ tesorotools/render/content/table.py,sha256=zlnzDmDxnxAJQl3wF8mLXTX4ziTuG3d53PosQbR9lnE,9609
43
+ tesorotools/render/content/text.py,sha256=g3VdYxXe1GHF1IjmFrHdfjgC9CzWWMwWmgHxifToIqM,678
43
44
  tesorotools/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  tesorotools/utils/config.py,sha256=TMD_6OB2RS2sjM17tYwFnOtcXZgIxs-Ec8FYA3Qokuc,1056
45
46
  tesorotools/utils/globals.py,sha256=c5PBpZwpVO_nOetxyIk8j32Vx3GFlsgdYbwwfSSlv1M,374
46
47
  tesorotools/utils/matplotlib.py,sha256=tKidZIcDLKWgawIOJggWwVoaTMsrjw1WEJQouydSYbI,1197
47
48
  tesorotools/utils/series.py,sha256=eUEUOdzDfA7YbgO4o6KtxwJOL5cQYdqLjrdvuQ7Hqhc,1168
48
49
  tesorotools/utils/template.py,sha256=xpBVad28gG9ZLVwd9e0dTWsGsbujjmVcCPEW2mIKEJo,4917
49
- tesorotools_python-0.0.9.dist-info/METADATA,sha256=Ls3l-uHo06HBzNZ6GsfRZgEzJx1vvcyQdSdxnKDSrM4,323
50
- tesorotools_python-0.0.9.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
51
- tesorotools_python-0.0.9.dist-info/RECORD,,
50
+ tesorotools_python-0.0.11.dist-info/METADATA,sha256=qqF7kVms8kO2tV6B_TgJBpkjCthcqN-yvBL5v1oMSAQ,324
51
+ tesorotools_python-0.0.11.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
52
+ tesorotools_python-0.0.11.dist-info/RECORD,,