maidr 1.8.1__tar.gz → 1.10.0__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.
- {maidr-1.8.1 → maidr-1.10.0}/CHANGELOG.md +17 -0
- {maidr-1.8.1 → maidr-1.10.0}/PKG-INFO +1 -1
- {maidr-1.8.1 → maidr-1.10.0}/docs/examples.qmd +1 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/__init__.py +1 -1
- {maidr-1.8.1 → maidr-1.10.0}/maidr/api.py +17 -12
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/maidr.py +88 -37
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/candlestick.py +59 -90
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/datetime_conversion.py +14 -27
- maidr-1.10.0/maidr/util/mplfinance_utils.py +117 -0
- {maidr-1.8.1 → maidr-1.10.0}/pyproject.toml +1 -1
- {maidr-1.8.1 → maidr-1.10.0}/uv.lock +7 -1
- maidr-1.8.1/maidr/util/mplfinance_utils.py +0 -415
- {maidr-1.8.1 → maidr-1.10.0}/.commitlintrc.cjs +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.editorconfig +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/copilot-instructions.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/workflows/ci.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/workflows/docs.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.github/workflows/release.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.gitignore +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.pre-commit-config.yaml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.vscode/extensions.json +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/.vscode/settings.json +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/CONDUCT.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/CONTRIBUTING.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/LICENSE +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/README.md +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/.gitignore +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/CNAME +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_environment +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/machow/interlinks/.gitignore +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/_extension.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/line-highlight.lua +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/css/line-highlight.css +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_extensions/shafayetShafee/line-highlight/resources/js/line-highlight.js +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/_quarto.yml +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/index.qmd +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/docs/styles.css +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/bar/example_bar_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/bar/matplotlib/example_mpl_bar_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/bar/seaborn/example_sns_bar_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/box/example_box_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/box/matplotlib/example_mpl_box.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/box/seaborn/example_sns_box.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/legacy_candlestick_example.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/mplfinance_candlestick_example.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/test_data_daily_current_year.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/test_data_daily_mixed_years.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/test_data_hourly.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/test_data_minute.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/test_data_weekly.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/candle_stick/volcandat.csv +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/count/example_count_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/count/seaborn/example_sns_count_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/dodged/matplotlib/example_mpl_dodged.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/dodged/seaborn/example_sns_dodged.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_bar_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/facet-subplots/seaborn/example_sns_facet_combined_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/flask/test_flask_app.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/heatmap/example_heatmap_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/heatmap/matplotlib/example_mpl_heatmap.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/heatmap/seaborn/example_sns_heatmap.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/histogram/example_histogram_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/histogram/matplotlib/example_mpl_hist.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/histogram/matplotlib/histogram_with_kde_matplotlib.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/histogram/seaborn/example_sns_hist.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/histogram/seaborn/histogram_with_kde_seaborn.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/kde/example_kde_plots.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/kde/matplotlib/multiple_kde_matplotlib.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/kde/matplotlib/single_kde_matplotlib.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/kde/seaborn/multiple_kde_seaborn.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/kde/seaborn/single_kde_seaborn.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/line/example_line_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/line/matplotlib/example_mpl_line.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/line/seaborn/example_sns_line.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multilayer/example_mpl_multilayer.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multilayer/example_multilayer_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multiline/example_multiline_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multiline/matplotlib/example_mpl_multiline.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multiline/seaborn/example_sns_multiline.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multipanel/example_multipanel_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multipanel/matplotlib/example_mpl_multipanel.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/multipanel/seaborn/example_sns_multipanel.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/quarto/demo.qmd +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/reg/example_reg_plots.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/reg/matplotlib/example_matplotlib_smooth_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/reg/seaborn/example_sns_reg.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/scatter/example_scatter_plot.ipynb +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/scatter/matplotlib/example_mpl_scatter.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/scatter/seaborn/example_sns_scatter.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/shiny/example_shiny_scatter.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/stacked/matplotlib/example_mpl_stacked.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/stacked/seaborn/example_sns_stacked.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/example/streamlit/example_streamlit_app.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/context_manager.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/enum/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/enum/library.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/enum/maidr_key.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/enum/plot_type.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/enum/smooth_keywords.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/figure_manager.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/barplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/boxplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/grouped_barplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/heatmap.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/histogram.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/lineplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/maidr_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/maidr_plot_factory.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/mplfinance_barplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/mplfinance_lineplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/regplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/core/plot/scatterplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/exception/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/exception/extraction_error.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/barplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/boxplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/candlestick.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/clear.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/common.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/heatmap.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/highlight.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/histogram.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/kdeplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/lineplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/mplfinance.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/regplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/patch/scatterplot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/dedup_utils.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/environment.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/mixin/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/mixin/extractor_mixin.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/mixin/merger_mixin.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/plot_detection.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/regression_line_utils.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/util/svg_utils.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/widget/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/maidr/widget/shiny.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/conftest.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/enum/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/plot/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/test_figure_manager.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/test_maidr_plot.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/core/test_maidr_plot_factory.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/fixture/__init__.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/fixture/library_factory.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/fixture/matplotlib_factory.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tests/fixture/seaborn_factory.py +0 -0
- {maidr-1.8.1 → maidr-1.10.0}/tox.ini +0 -0
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v1.10.0 (2026-01-31)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Remove candlestick formatting and add ylabel to dodged plots
|
|
9
|
+
([#260](https://github.com/xability/py-maidr/pull/260),
|
|
10
|
+
[`70b2626`](https://github.com/xability/py-maidr/commit/70b2626bb31e109e8cdb29051f809fe03bfb6275))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v1.9.0 (2025-10-31)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- Add data_in_svg parameter for save_html ([#257](https://github.com/xability/py-maidr/pull/257),
|
|
18
|
+
[`17d65ee`](https://github.com/xability/py-maidr/commit/17d65eee06ac3209ef3a91ebd623a5a6d0b16e79))
|
|
19
|
+
|
|
20
|
+
|
|
4
21
|
## v1.8.1 (2025-10-16)
|
|
5
22
|
|
|
6
23
|
### Bug Fixes
|
|
@@ -152,6 +152,7 @@ for offset, (category, counts) in zip(offsets, weight_counts.items()):
|
|
|
152
152
|
ax.set_xticks(x)
|
|
153
153
|
ax.set_xticklabels(species)
|
|
154
154
|
ax.set_xlabel("Species")
|
|
155
|
+
ax.set_ylabel("Weight")
|
|
155
156
|
ax.set_title("Dodged Bar Plot: Penguin Weight Counts")
|
|
156
157
|
ax.legend(loc="upper right")
|
|
157
158
|
|
|
@@ -14,12 +14,12 @@ from maidr.core.figure_manager import FigureManager
|
|
|
14
14
|
def _get_plot_or_current(plot: Any | None) -> Any:
|
|
15
15
|
"""
|
|
16
16
|
Get the plot object or current matplotlib figure if plot is None.
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
Parameters
|
|
19
19
|
----------
|
|
20
20
|
plot : Any or None
|
|
21
21
|
The plot object. If None, returns the current matplotlib figure.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
Returns
|
|
24
24
|
-------
|
|
25
25
|
Any
|
|
@@ -28,7 +28,7 @@ def _get_plot_or_current(plot: Any | None) -> Any:
|
|
|
28
28
|
if plot is None:
|
|
29
29
|
# Lazy import matplotlib.pyplot when needed
|
|
30
30
|
import matplotlib.pyplot as plt
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
return plt.gcf()
|
|
33
33
|
return plot
|
|
34
34
|
|
|
@@ -48,7 +48,7 @@ def render(plot: Any | None = None) -> Tag:
|
|
|
48
48
|
The rendered HTML representation of the plot.
|
|
49
49
|
"""
|
|
50
50
|
plot = _get_plot_or_current(plot)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
ax = FigureManager.get_axes(plot)
|
|
53
53
|
if isinstance(ax, list):
|
|
54
54
|
for axes in ax:
|
|
@@ -82,7 +82,7 @@ def show(
|
|
|
82
82
|
The display result.
|
|
83
83
|
"""
|
|
84
84
|
plot = _get_plot_or_current(plot)
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
ax = FigureManager.get_axes(plot)
|
|
87
87
|
if isinstance(ax, list):
|
|
88
88
|
for axes in ax:
|
|
@@ -95,10 +95,11 @@ def show(
|
|
|
95
95
|
|
|
96
96
|
def save_html(
|
|
97
97
|
plot: Any | None = None,
|
|
98
|
-
*,
|
|
98
|
+
*,
|
|
99
99
|
file: str,
|
|
100
|
-
lib_dir: str | None = "lib",
|
|
101
|
-
include_version: bool = True
|
|
100
|
+
lib_dir: str | None = "lib",
|
|
101
|
+
include_version: bool = True,
|
|
102
|
+
data_in_svg: bool = True,
|
|
102
103
|
) -> str:
|
|
103
104
|
"""
|
|
104
105
|
Save a MAIDR plot as HTML file.
|
|
@@ -113,6 +114,8 @@ def save_html(
|
|
|
113
114
|
Directory name for libraries.
|
|
114
115
|
include_version : bool, default True
|
|
115
116
|
Whether to include version information.
|
|
117
|
+
data_in_svg : bool, default True
|
|
118
|
+
Controls where the MAIDR JSON payload is placed in the HTML or SVG.
|
|
116
119
|
|
|
117
120
|
Returns
|
|
118
121
|
-------
|
|
@@ -120,19 +123,21 @@ def save_html(
|
|
|
120
123
|
The path to the saved HTML file.
|
|
121
124
|
"""
|
|
122
125
|
plot = _get_plot_or_current(plot)
|
|
123
|
-
|
|
126
|
+
|
|
124
127
|
ax = FigureManager.get_axes(plot)
|
|
125
128
|
htmls = []
|
|
126
129
|
if isinstance(ax, list):
|
|
127
130
|
for axes in ax:
|
|
128
131
|
maidr = FigureManager.get_maidr(axes.get_figure())
|
|
129
|
-
htmls.append(maidr._create_html_doc(use_iframe=False))
|
|
132
|
+
htmls.append(maidr._create_html_doc(use_iframe=False, data_in_svg=data_in_svg))
|
|
130
133
|
return htmls[-1].save_html(
|
|
131
134
|
file, libdir=lib_dir, include_version=include_version
|
|
132
135
|
)
|
|
133
136
|
else:
|
|
134
137
|
maidr = FigureManager.get_maidr(ax.get_figure())
|
|
135
|
-
return maidr.save_html(
|
|
138
|
+
return maidr.save_html(
|
|
139
|
+
file, lib_dir=lib_dir, include_version=include_version, data_in_svg=data_in_svg
|
|
140
|
+
)
|
|
136
141
|
|
|
137
142
|
|
|
138
143
|
def stacked(plot: Axes | BarContainer) -> Maidr:
|
|
@@ -150,6 +155,6 @@ def close(plot: Any | None = None) -> None:
|
|
|
150
155
|
The plot object to close. If None, uses the current matplotlib figure.
|
|
151
156
|
"""
|
|
152
157
|
plot = _get_plot_or_current(plot)
|
|
153
|
-
|
|
158
|
+
|
|
154
159
|
ax = FigureManager.get_axes(plot)
|
|
155
160
|
FigureManager.destroy(ax.get_figure())
|
|
@@ -72,7 +72,12 @@ class Maidr:
|
|
|
72
72
|
return self._create_html_tag(use_iframe=True)
|
|
73
73
|
|
|
74
74
|
def save_html(
|
|
75
|
-
self,
|
|
75
|
+
self,
|
|
76
|
+
file: str,
|
|
77
|
+
*,
|
|
78
|
+
lib_dir: str | None = "lib",
|
|
79
|
+
include_version: bool = True,
|
|
80
|
+
data_in_svg: bool = True,
|
|
76
81
|
) -> str:
|
|
77
82
|
"""
|
|
78
83
|
Save the HTML representation of the figure with MAIDR to a file.
|
|
@@ -86,9 +91,11 @@ class Maidr:
|
|
|
86
91
|
(relative to the file's directory).
|
|
87
92
|
include_version : bool, default=True
|
|
88
93
|
Whether to include the version number in the dependency folder name.
|
|
94
|
+
data_in_svg : bool, default=True
|
|
95
|
+
Controls where the MAIDR JSON payload is placed in the output HTML or SVG.
|
|
89
96
|
"""
|
|
90
97
|
html = self._create_html_doc(
|
|
91
|
-
use_iframe=False
|
|
98
|
+
use_iframe=False, data_in_svg=data_in_svg
|
|
92
99
|
) # Always use direct HTML for saving
|
|
93
100
|
return html.save_html(file, libdir=lib_dir, include_version=include_version)
|
|
94
101
|
|
|
@@ -180,8 +187,17 @@ class Maidr:
|
|
|
180
187
|
else:
|
|
181
188
|
webbrowser.open(f"file://{html_file_path}")
|
|
182
189
|
|
|
183
|
-
def _create_html_tag(self, use_iframe: bool = True) -> Tag:
|
|
184
|
-
"""Create the MAIDR HTML using HTML tags.
|
|
190
|
+
def _create_html_tag(self, use_iframe: bool = True, data_in_svg: bool = True) -> Tag:
|
|
191
|
+
"""Create the MAIDR HTML using HTML tags.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
use_iframe : bool, default=True
|
|
196
|
+
Whether to render the plot inside an iframe (for notebooks and similar envs).
|
|
197
|
+
data_in_svg : bool, default=True
|
|
198
|
+
If True, the MAIDR JSON is embedded in the root <svg> under attribute 'maidr'.
|
|
199
|
+
If False, a <script>var maidr = {...}</script> tag is injected instead.
|
|
200
|
+
"""
|
|
185
201
|
tagged_elements: list[Any] = [
|
|
186
202
|
element for plot in self._plots for element in plot.elements
|
|
187
203
|
]
|
|
@@ -191,16 +207,31 @@ class Maidr:
|
|
|
191
207
|
for _ in plot.elements:
|
|
192
208
|
selector_ids.append(self.selector_ids[i])
|
|
193
209
|
|
|
210
|
+
# Build schema once so id stays consistent across SVG and global var
|
|
211
|
+
schema = self._flatten_maidr()
|
|
212
|
+
|
|
194
213
|
with HighlightContextManager.set_maidr_elements(tagged_elements, selector_ids):
|
|
195
|
-
svg = self._get_svg()
|
|
196
|
-
|
|
214
|
+
svg = self._get_svg(embed_data=data_in_svg, schema=schema)
|
|
215
|
+
|
|
216
|
+
# Generate external payload if data is not embedded in SVG
|
|
217
|
+
maidr = None
|
|
218
|
+
if not data_in_svg:
|
|
219
|
+
maidr = f"\nvar maidr = {json.dumps(schema, indent=2)}\n"
|
|
197
220
|
|
|
198
221
|
# Inject plot's svg and MAIDR structure into html tag.
|
|
199
222
|
return Maidr._inject_plot(svg, maidr, self.maidr_id, use_iframe)
|
|
200
223
|
|
|
201
|
-
def _create_html_doc(self, use_iframe: bool = True) -> HTMLDocument:
|
|
202
|
-
"""Create an HTML document from Tag objects.
|
|
203
|
-
|
|
224
|
+
def _create_html_doc(self, use_iframe: bool = True, data_in_svg: bool = True) -> HTMLDocument:
|
|
225
|
+
"""Create an HTML document from Tag objects.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
use_iframe : bool, default=True
|
|
230
|
+
Whether to render the plot inside an iframe (for notebooks and similar envs).
|
|
231
|
+
data_in_svg : bool, default=True
|
|
232
|
+
See _create_html_tag for details on payload placement strategy.
|
|
233
|
+
"""
|
|
234
|
+
return HTMLDocument(self._create_html_tag(use_iframe, data_in_svg), lang="en")
|
|
204
235
|
|
|
205
236
|
def _merge_plots_by_subplot_position(self) -> list[MaidrPlot]:
|
|
206
237
|
"""
|
|
@@ -312,10 +343,23 @@ class Maidr:
|
|
|
312
343
|
for cell in subplot_grid[i]
|
|
313
344
|
]
|
|
314
345
|
|
|
315
|
-
return {
|
|
346
|
+
return {
|
|
347
|
+
"id": Maidr._unique_id(),
|
|
348
|
+
"subplots": subplot_grid,
|
|
349
|
+
}
|
|
316
350
|
|
|
317
|
-
def _get_svg(self) -> HTML:
|
|
318
|
-
"""Extract the chart SVG from ``matplotlib.figure.Figure``.
|
|
351
|
+
def _get_svg(self, embed_data: bool = True, schema: dict | None = None) -> HTML:
|
|
352
|
+
"""Extract the chart SVG from ``matplotlib.figure.Figure``.
|
|
353
|
+
|
|
354
|
+
Parameters
|
|
355
|
+
----------
|
|
356
|
+
embed_data : bool, default=True
|
|
357
|
+
If True, embed the MAIDR JSON schema as an attribute named 'maidr' on
|
|
358
|
+
the root <svg> element. If False, do not embed JSON in the SVG.
|
|
359
|
+
schema : dict | None, default=None
|
|
360
|
+
If provided, this schema will be used (ensuring a consistent id across
|
|
361
|
+
the page). If None, a new schema will be generated.
|
|
362
|
+
"""
|
|
319
363
|
svg_buffer = io.StringIO()
|
|
320
364
|
self._fig.savefig(svg_buffer, format="svg")
|
|
321
365
|
str_svg = svg_buffer.getvalue()
|
|
@@ -323,12 +367,14 @@ class Maidr:
|
|
|
323
367
|
etree.register_namespace("svg", "http://www.w3.org/2000/svg")
|
|
324
368
|
tree_svg = etree.fromstring(str_svg.encode(), parser=None)
|
|
325
369
|
root_svg = None
|
|
326
|
-
# Find the `svg` tag and
|
|
370
|
+
# Find the `svg` tag and optionally embed MAIDR data.
|
|
327
371
|
for element in tree_svg.iter(tag="{http://www.w3.org/2000/svg}svg"):
|
|
328
|
-
if
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
372
|
+
current_schema = schema if schema is not None else self._flatten_maidr()
|
|
373
|
+
# Ensure SVG id matches schema id in both modes
|
|
374
|
+
if isinstance(current_schema, dict) and "id" in current_schema:
|
|
375
|
+
element.attrib["id"] = str(current_schema["id"]) # ensure match
|
|
376
|
+
if embed_data:
|
|
377
|
+
element.attrib["maidr"] = json.dumps(current_schema, indent=2)
|
|
332
378
|
root_svg = element
|
|
333
379
|
break
|
|
334
380
|
|
|
@@ -355,36 +401,41 @@ class Maidr:
|
|
|
355
401
|
return str(uuid.uuid4())
|
|
356
402
|
|
|
357
403
|
@staticmethod
|
|
358
|
-
def _inject_plot(plot: HTML, maidr: str, maidr_id, use_iframe: bool = True) -> Tag:
|
|
404
|
+
def _inject_plot(plot: HTML, maidr: str | None, maidr_id, use_iframe: bool = True) -> Tag:
|
|
359
405
|
"""Embed the plot and associated MAIDR scripts into the HTML structure."""
|
|
360
406
|
# Get the latest version from npm registry
|
|
361
407
|
MAIDR_TS_CDN_URL = "https://cdn.jsdelivr.net/npm/maidr@latest/dist/maidr.js"
|
|
362
408
|
|
|
363
409
|
script = f"""
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
410
|
+
(function() {{
|
|
411
|
+
var existing = document.querySelector('script[src="{MAIDR_TS_CDN_URL}"]');
|
|
412
|
+
if (!existing) {{
|
|
413
|
+
var s = document.createElement('script');
|
|
414
|
+
s.src = '{MAIDR_TS_CDN_URL}';
|
|
415
|
+
s.onload = function() {{ if (window.main) window.main(); }};
|
|
416
|
+
document.head.appendChild(s);
|
|
417
|
+
}} else {{
|
|
418
|
+
if (document.readyState === 'loading') {{
|
|
419
|
+
document.addEventListener('DOMContentLoaded', function() {{ if (window.main) window.main(); }});
|
|
420
|
+
}} else {{
|
|
421
|
+
if (window.main) window.main();
|
|
422
|
+
}}
|
|
423
|
+
}}
|
|
424
|
+
}})();
|
|
378
425
|
"""
|
|
379
426
|
|
|
380
|
-
|
|
427
|
+
children = [
|
|
381
428
|
tags.link(
|
|
382
429
|
rel="stylesheet",
|
|
383
430
|
href="https://cdn.jsdelivr.net/npm/maidr@latest/dist/maidr_style.css",
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
431
|
+
)
|
|
432
|
+
]
|
|
433
|
+
if maidr is not None:
|
|
434
|
+
children.append(tags.script(maidr, type="text/javascript"))
|
|
435
|
+
children.append(tags.script(script, type="text/javascript"))
|
|
436
|
+
children.append(tags.div(plot))
|
|
437
|
+
|
|
438
|
+
base_html = tags.div(*children)
|
|
388
439
|
|
|
389
440
|
# is_quarto = os.getenv("IS_QUARTO") == "True"
|
|
390
441
|
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import uuid
|
|
4
3
|
from typing import Union, Dict
|
|
5
4
|
from matplotlib.axes import Axes
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
8
6
|
|
|
9
7
|
from maidr.core.enum import PlotType
|
|
10
8
|
from maidr.core.plot import MaidrPlot
|
|
11
9
|
from maidr.core.enum.maidr_key import MaidrKey
|
|
12
10
|
from maidr.exception import ExtractionError
|
|
13
|
-
from maidr.util.mplfinance_utils import MplfinanceDataExtractor
|
|
14
11
|
|
|
15
12
|
|
|
16
13
|
class CandlestickPlot(MaidrPlot):
|
|
17
14
|
"""
|
|
18
15
|
Specialized candlestick plot class for mplfinance OHLC data.
|
|
19
16
|
|
|
20
|
-
This class
|
|
21
|
-
|
|
17
|
+
This class extracts candlestick data directly from the original DataFrame
|
|
18
|
+
without any formatting or transformation.
|
|
22
19
|
"""
|
|
23
20
|
|
|
24
21
|
def __init__(self, axes: list[Axes], **kwargs) -> None:
|
|
@@ -34,23 +31,17 @@ class CandlestickPlot(MaidrPlot):
|
|
|
34
31
|
Additional keyword arguments.
|
|
35
32
|
"""
|
|
36
33
|
self.axes = axes
|
|
37
|
-
# Ensure there's at least one axis for the superclass init
|
|
38
34
|
if not axes:
|
|
39
35
|
raise ValueError("Axes list cannot be empty.")
|
|
40
36
|
super().__init__(axes[0], PlotType.CANDLESTICK)
|
|
41
37
|
|
|
42
|
-
# Store
|
|
38
|
+
# Store collections passed from mplfinance patch
|
|
43
39
|
self._maidr_wick_collection = kwargs.get("_maidr_wick_collection", None)
|
|
44
40
|
self._maidr_body_collection = kwargs.get("_maidr_body_collection", None)
|
|
45
|
-
self.
|
|
46
|
-
self._maidr_original_data = kwargs.get(
|
|
47
|
-
"_maidr_original_data", None
|
|
48
|
-
) # Store original data
|
|
49
|
-
self._maidr_datetime_converter = kwargs.get("_maidr_datetime_converter", None)
|
|
41
|
+
self._maidr_original_data = kwargs.get("_maidr_original_data", None)
|
|
50
42
|
|
|
51
|
-
# Store the GID for
|
|
43
|
+
# Store the GID for selector generation
|
|
52
44
|
self._maidr_gid = None
|
|
53
|
-
# Modern-path separate gids for body and wick
|
|
54
45
|
self._maidr_body_gid = None
|
|
55
46
|
self._maidr_wick_gid = None
|
|
56
47
|
if self._maidr_body_collection:
|
|
@@ -62,105 +53,83 @@ class CandlestickPlot(MaidrPlot):
|
|
|
62
53
|
|
|
63
54
|
def _extract_plot_data(self) -> list[dict]:
|
|
64
55
|
"""
|
|
65
|
-
Extract candlestick data from the
|
|
66
|
-
|
|
67
|
-
This method processes candlestick plots from both modern (mplfinance.plot) and
|
|
68
|
-
legacy (original_flavor) pipelines, extracting OHLC data and setting up
|
|
69
|
-
highlighting elements and GIDs.
|
|
56
|
+
Extract candlestick data directly from the original DataFrame.
|
|
70
57
|
|
|
71
58
|
Returns
|
|
72
59
|
-------
|
|
73
60
|
list[dict]
|
|
74
61
|
List of dictionaries containing candlestick data with keys:
|
|
75
|
-
- 'value': Date string
|
|
62
|
+
- 'value': Date string (raw from DataFrame index)
|
|
76
63
|
- 'open': Opening price (float)
|
|
77
64
|
- 'high': High price (float)
|
|
78
65
|
- 'low': Low price (float)
|
|
79
66
|
- 'close': Closing price (float)
|
|
80
|
-
- 'volume': Volume (float
|
|
67
|
+
- 'volume': Volume (float)
|
|
81
68
|
"""
|
|
82
|
-
|
|
83
|
-
# Get the custom collections from kwargs
|
|
84
69
|
body_collection = self._maidr_body_collection
|
|
85
70
|
wick_collection = self._maidr_wick_collection
|
|
86
71
|
|
|
87
72
|
if body_collection and wick_collection:
|
|
88
|
-
# Store the GIDs from the collections
|
|
73
|
+
# Store the GIDs from the collections
|
|
89
74
|
self._maidr_body_gid = body_collection.get_gid()
|
|
90
75
|
self._maidr_wick_gid = wick_collection.get_gid()
|
|
91
|
-
# Keep legacy gid filled for backward compatibility
|
|
92
76
|
self._maidr_gid = self._maidr_body_gid or self._maidr_wick_gid
|
|
93
77
|
|
|
94
78
|
# Use the original collections for highlighting
|
|
95
79
|
self._elements = [body_collection, wick_collection]
|
|
96
80
|
|
|
97
|
-
#
|
|
98
|
-
if self.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
return data
|
|
103
|
-
|
|
104
|
-
# Fallback to original detection method
|
|
105
|
-
if not self.axes:
|
|
106
|
-
return []
|
|
107
|
-
|
|
108
|
-
ax_ohlc = self.axes[0]
|
|
109
|
-
|
|
110
|
-
# Look for Rectangle patches (original_flavor candlestick)
|
|
111
|
-
body_rectangles = []
|
|
112
|
-
for patch in ax_ohlc.patches:
|
|
113
|
-
if isinstance(patch, Rectangle):
|
|
114
|
-
body_rectangles.append(patch)
|
|
115
|
-
|
|
116
|
-
if body_rectangles:
|
|
117
|
-
# Set elements for highlighting
|
|
118
|
-
self._elements = body_rectangles
|
|
119
|
-
|
|
120
|
-
# Generate a GID for highlighting if none exists
|
|
121
|
-
if not self._maidr_gid:
|
|
122
|
-
self._maidr_gid = f"maidr-{uuid.uuid4()}"
|
|
123
|
-
# Set GID on all rectangles
|
|
124
|
-
for rect in body_rectangles:
|
|
125
|
-
rect.set_gid(self._maidr_gid)
|
|
126
|
-
# Keep a dedicated body gid for legacy dict selectors
|
|
127
|
-
self._maidr_body_gid = (
|
|
128
|
-
getattr(self, "_maidr_body_gid", None) or self._maidr_gid
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
# Assign a shared gid to wick Line2D (vertical 2-point lines) on the same axis
|
|
132
|
-
wick_lines = []
|
|
133
|
-
for line in ax_ohlc.get_lines():
|
|
134
|
-
try:
|
|
135
|
-
xydata = line.get_xydata()
|
|
136
|
-
if xydata is None:
|
|
137
|
-
continue
|
|
138
|
-
xy_arr = np.asarray(xydata)
|
|
139
|
-
if (
|
|
140
|
-
xy_arr.ndim == 2
|
|
141
|
-
and xy_arr.shape[0] == 2
|
|
142
|
-
and xy_arr.shape[1] >= 2
|
|
143
|
-
):
|
|
144
|
-
x0 = float(xy_arr[0, 0])
|
|
145
|
-
x1 = float(xy_arr[1, 0])
|
|
146
|
-
if abs(x0 - x1) < 1e-10:
|
|
147
|
-
wick_lines.append(line)
|
|
148
|
-
except Exception:
|
|
149
|
-
continue
|
|
150
|
-
if wick_lines:
|
|
151
|
-
if not getattr(self, "_maidr_wick_gid", None):
|
|
152
|
-
self._maidr_wick_gid = f"maidr-{uuid.uuid4()}"
|
|
153
|
-
for line in wick_lines:
|
|
154
|
-
line.set_gid(self._maidr_wick_gid)
|
|
155
|
-
|
|
156
|
-
# Use the utility class to extract data
|
|
157
|
-
data = MplfinanceDataExtractor.extract_rectangle_candlestick_data(
|
|
158
|
-
body_rectangles, self._maidr_date_nums, self._maidr_original_data
|
|
159
|
-
)
|
|
160
|
-
return data
|
|
81
|
+
# Extract data directly from DataFrame
|
|
82
|
+
if self._maidr_original_data is not None and isinstance(
|
|
83
|
+
self._maidr_original_data, pd.DataFrame
|
|
84
|
+
):
|
|
85
|
+
return self._extract_from_dataframe(self._maidr_original_data)
|
|
161
86
|
|
|
162
87
|
return []
|
|
163
88
|
|
|
89
|
+
def _extract_from_dataframe(self, df: pd.DataFrame) -> list[dict]:
|
|
90
|
+
"""
|
|
91
|
+
Extract candlestick data directly from DataFrame without any formatting.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
df : pd.DataFrame
|
|
96
|
+
DataFrame with OHLC data and DatetimeIndex.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
list[dict]
|
|
101
|
+
List of candlestick data dictionaries with raw values.
|
|
102
|
+
"""
|
|
103
|
+
candles = []
|
|
104
|
+
|
|
105
|
+
for i in range(len(df)):
|
|
106
|
+
try:
|
|
107
|
+
# Get date directly from index - raw representation
|
|
108
|
+
date_value = str(df.index[i])
|
|
109
|
+
|
|
110
|
+
# Get OHLC values directly from DataFrame columns
|
|
111
|
+
open_price = float(df.iloc[i]["Open"])
|
|
112
|
+
high_price = float(df.iloc[i]["High"])
|
|
113
|
+
low_price = float(df.iloc[i]["Low"])
|
|
114
|
+
close_price = float(df.iloc[i]["Close"])
|
|
115
|
+
|
|
116
|
+
# Get volume if available, otherwise 0
|
|
117
|
+
volume = float(df.iloc[i].get("Volume", 0.0))
|
|
118
|
+
|
|
119
|
+
candle_data = {
|
|
120
|
+
"value": date_value,
|
|
121
|
+
"open": open_price,
|
|
122
|
+
"high": high_price,
|
|
123
|
+
"low": low_price,
|
|
124
|
+
"close": close_price,
|
|
125
|
+
"volume": volume,
|
|
126
|
+
}
|
|
127
|
+
candles.append(candle_data)
|
|
128
|
+
except (KeyError, IndexError, ValueError, TypeError):
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
return candles
|
|
132
|
+
|
|
164
133
|
def _extract_axes_data(self) -> dict:
|
|
165
134
|
"""
|
|
166
135
|
Extract the plot's axes data including labels.
|
|
@@ -6,18 +6,16 @@ from datetime import datetime
|
|
|
6
6
|
|
|
7
7
|
class DatetimeConverter:
|
|
8
8
|
"""
|
|
9
|
-
|
|
10
|
-
and provides intelligent date/time formatting for mplfinance plots.
|
|
9
|
+
Datetime converter for mplfinance plots.
|
|
11
10
|
|
|
12
|
-
This utility
|
|
13
|
-
datetime values consistently for screen reader accessibility and visual clarity.
|
|
11
|
+
This utility provides datetime value conversion for financial data visualization.
|
|
14
12
|
|
|
15
13
|
Parameters
|
|
16
14
|
----------
|
|
17
15
|
data : pd.DataFrame
|
|
18
16
|
DataFrame with DatetimeIndex containing financial data.
|
|
19
17
|
datetime_format : str, optional
|
|
20
|
-
Custom datetime format string
|
|
18
|
+
Custom datetime format string (currently unused, kept for compatibility).
|
|
21
19
|
|
|
22
20
|
Attributes
|
|
23
21
|
----------
|
|
@@ -49,14 +47,14 @@ class DatetimeConverter:
|
|
|
49
47
|
>>>
|
|
50
48
|
>>> # Get formatted datetime
|
|
51
49
|
>>> formatted = converter.get_formatted_datetime(0)
|
|
52
|
-
>>> print(formatted) # Output: "
|
|
50
|
+
>>> print(formatted) # Output: "2024-01-15 00:00:00"
|
|
53
51
|
>>>
|
|
54
52
|
>>> # For time-based data
|
|
55
53
|
>>> hourly_dates = pd.date_range('2024-01-15 09:00:00', periods=3, freq='H')
|
|
56
54
|
>>> df_hourly = pd.DataFrame({'Open': [3050, 3078, 3080]}, index=hourly_dates)
|
|
57
55
|
>>> converter_hourly = create_datetime_converter(df_hourly)
|
|
58
56
|
>>> formatted_hourly = converter_hourly.get_formatted_datetime(0)
|
|
59
|
-
>>> print(formatted_hourly) # Output: "
|
|
57
|
+
>>> print(formatted_hourly) # Output: "2024-01-15 09:00:00"
|
|
60
58
|
"""
|
|
61
59
|
|
|
62
60
|
def __init__(
|
|
@@ -164,9 +162,7 @@ class DatetimeConverter:
|
|
|
164
162
|
|
|
165
163
|
def get_formatted_datetime(self, index: int) -> Optional[str]:
|
|
166
164
|
"""
|
|
167
|
-
Get
|
|
168
|
-
|
|
169
|
-
Always includes year for screen reader accessibility.
|
|
165
|
+
Get datetime string for given index.
|
|
170
166
|
|
|
171
167
|
Parameters
|
|
172
168
|
----------
|
|
@@ -176,13 +172,13 @@ class DatetimeConverter:
|
|
|
176
172
|
Returns
|
|
177
173
|
-------
|
|
178
174
|
str or None
|
|
179
|
-
|
|
175
|
+
Datetime string or None if index is invalid.
|
|
180
176
|
|
|
181
177
|
Examples
|
|
182
178
|
--------
|
|
183
179
|
>>> converter = create_datetime_converter(df)
|
|
184
180
|
>>> formatted = converter.get_formatted_datetime(0)
|
|
185
|
-
>>> print(formatted) # "
|
|
181
|
+
>>> print(formatted) # "2024-01-15 00:00:00"
|
|
186
182
|
"""
|
|
187
183
|
if index not in self.date_mapping:
|
|
188
184
|
return None
|
|
@@ -192,7 +188,7 @@ class DatetimeConverter:
|
|
|
192
188
|
|
|
193
189
|
def _format_datetime_custom(self, dt: datetime) -> str:
|
|
194
190
|
"""
|
|
195
|
-
|
|
191
|
+
Format datetime as-is using ISO format.
|
|
196
192
|
|
|
197
193
|
Parameters
|
|
198
194
|
----------
|
|
@@ -202,24 +198,15 @@ class DatetimeConverter:
|
|
|
202
198
|
Returns
|
|
203
199
|
-------
|
|
204
200
|
str
|
|
205
|
-
Formatted datetime string
|
|
201
|
+
Formatted datetime string in ISO format.
|
|
206
202
|
|
|
207
203
|
Notes
|
|
208
204
|
-----
|
|
209
|
-
|
|
210
|
-
-
|
|
211
|
-
- Time-based data: "Jan 15 2024 09:00" or "Jan 15 2024 09:00:30"
|
|
212
|
-
- Seconds are only shown when they are non-zero for cleaner display.
|
|
205
|
+
Returns the datetime as a string without smart formatting.
|
|
206
|
+
Output format is "YYYY-MM-DD HH:MM:SS" (e.g., "2024-01-15 00:00:00").
|
|
213
207
|
"""
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if dt.second == 0:
|
|
217
|
-
return dt.strftime("%b %d %Y %H:%M")
|
|
218
|
-
else:
|
|
219
|
-
return dt.strftime("%b %d %Y %H:%M:%S")
|
|
220
|
-
else:
|
|
221
|
-
# Daily/weekly/monthly data: just date
|
|
222
|
-
return dt.strftime("%b %d %Y")
|
|
208
|
+
# Return string representation of datetime
|
|
209
|
+
return str(dt)
|
|
223
210
|
|
|
224
211
|
@property
|
|
225
212
|
def date_nums(self) -> List[float]:
|