tesorotools-python 0.1.9__tar.gz → 0.1.10__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.1.9 → tesorotools_python-0.1.10}/.gitignore +2 -1
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/PKG-INFO +1 -1
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/palette.py +2 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/charts.py +15 -7
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/native.py +19 -2
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/skill/SKILL.md +3 -2
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/__init__.py +10 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/app.py +171 -1
- tesorotools_python-0.1.10/src/tesorotools/web/period.py +220 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/pyproject.toml +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/_build_context.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/_registry.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/_common.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/_dates.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/bar_line.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/box_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/compact.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/intraday_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/line_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/matrix.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/plotly_backend.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/sankey.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/scatter.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/sharing.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/shock_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/stacked.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/tikz_backend.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/vector_plot.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/waterfall.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/nota_template.dotx +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/template.docx +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/driver.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/cli.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/scaffold.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/article/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/article/sections/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/article/sections/02-analisis.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/article/sections/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/beamer/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/beamer/slides/01-contexto.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/beamer/slides/02-analisis.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/beamer/slides/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/beamer/style/beamerthemeTesoro.sty +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/book/appendices/a-fuentes.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/book/chapters/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/book/chapters/02-analisis.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/book/frontmatter/resumen.tex +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/book/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/README.md.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/dot-gitignore +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/dot-vscode/settings.json +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/figures/dot-gitkeep +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/fonts/README.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/latexmkrc +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/refs.bib +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/style/tesoro.sty +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/style/tesorobrand.sty.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/latex/templates/common/style/tesorolocal.sty.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/builder.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/charts.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/cli.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/inline.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/issues.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/lint.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/markdown.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/pdf.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/skeleton.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/skill/SKILL.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/skill/reglas.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/note/template.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/orchestration.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/builder.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/cli.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/fit.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/glossary.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/house.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/lint.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/markdown.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/pdf.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/sections.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/skeleton.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/skill/reglas.md +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/template.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/ecb.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/fred.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/imf_irfcl.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/providers/lseg.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/table.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/document.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/scaffold.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/testing/compare.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/utils/template.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/brand.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/cli.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/components.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/formatting.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/launcher.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/palette.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/plotting.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/scaffold.py +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/Dockerfile.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/README.md.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/dot-dockerignore.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/dot-gitignore.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/dot-streamlit/config.toml.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/pyproject.toml.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/src/$package/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/src/$package/cli.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/src/$package/main.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/src/$package/sections.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/tests/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/templates/tests/test_app.py.tmpl +0 -0
- {tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/web/theme.py +0 -0
|
@@ -31,6 +31,8 @@ BLUE_PALE = "AEBAE7"
|
|
|
31
31
|
BLUE_FAINT = "ECF0FD"
|
|
32
32
|
#: Secondary text: captions, metadata, stale rows.
|
|
33
33
|
GREY = "6D6D6D"
|
|
34
|
+
#: Plot area of the slide charts, under white grid lines.
|
|
35
|
+
GREY_PANEL = "EFEFEF"
|
|
34
36
|
#: Tesoro yellow: highlights of the presentations (bands, frames, the
|
|
35
37
|
#: section subtitles of the index) and its light shade for fills.
|
|
36
38
|
YELLOW = "FFBD4C"
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/charts.py
RENAMED
|
@@ -4,7 +4,8 @@ The same artists, palette and axis format as the notes
|
|
|
4
4
|
(:func:`tesorotools.note.note_palette`, :func:`~tesorotools.note.axis_format`);
|
|
5
5
|
only the size changes: each chart is drawn at the size of its frame in
|
|
6
6
|
the template, with 12-point text that reads on a projected slide, the
|
|
7
|
-
legend too, and on a white
|
|
7
|
+
legend too, and the data on a grey panel with white grid lines, framed
|
|
8
|
+
by the white of the slide::
|
|
8
9
|
|
|
9
10
|
with slide_style():
|
|
10
11
|
LinePlot(
|
|
@@ -26,6 +27,7 @@ import matplotlib as mpl
|
|
|
26
27
|
|
|
27
28
|
from tesorotools.artists._common import Legend
|
|
28
29
|
from tesorotools.note.charts import note_palette
|
|
30
|
+
from tesorotools.palette import GREY_PANEL
|
|
29
31
|
|
|
30
32
|
__all__ = [
|
|
31
33
|
"SLIDE_CHART_CM",
|
|
@@ -49,13 +51,19 @@ SLIDE_CHART_CM: dict[str, tuple[float, float]] = {
|
|
|
49
51
|
"pareja_estrecho": (10.4, 8.9),
|
|
50
52
|
}
|
|
51
53
|
_CM_TO_IN = 1 / 2.54
|
|
52
|
-
#: What a slide chart changes of the house style:
|
|
53
|
-
#: the one of the slide, whatever the style of the notes or the machine
|
|
54
|
+
#: What a slide chart changes of the house style: the figure on white,
|
|
55
|
+
#: the one of the slide, whatever the style of the notes or the machine,
|
|
56
|
+
#: and the plot area grey (:data:`~tesorotools.palette.GREY_PANEL`) with
|
|
57
|
+
#: solid white grid lines.
|
|
54
58
|
SLIDE_STYLE: dict[str, Any] = {
|
|
55
59
|
"figure.facecolor": "white",
|
|
56
|
-
"axes.facecolor": "
|
|
60
|
+
"axes.facecolor": f"#{GREY_PANEL}",
|
|
57
61
|
"savefig.facecolor": "white",
|
|
58
62
|
"savefig.transparent": False,
|
|
63
|
+
"grid.color": "white",
|
|
64
|
+
"grid.linestyle": "solid",
|
|
65
|
+
"grid.linewidth": 1.0,
|
|
66
|
+
"grid.alpha": 1.0,
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
_rc_context: Callable[[dict[str, Any]], Any] = mpl.rc_context # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
|
@@ -64,9 +72,9 @@ _rc_context: Callable[[dict[str, Any]], Any] = mpl.rc_context # pyright: ignore
|
|
|
64
72
|
@contextmanager
|
|
65
73
|
def slide_style() -> Generator[None]:
|
|
66
74
|
"""Draw the charts inside the block for a slide: the colours of the
|
|
67
|
-
guide (:func:`~tesorotools.note.note_palette`) on
|
|
68
|
-
(:data:`SLIDE_STYLE`). Only while the block runs: the
|
|
69
|
-
their own style."""
|
|
75
|
+
guide (:func:`~tesorotools.note.note_palette`) on the grey panel of
|
|
76
|
+
the slides (:data:`SLIDE_STYLE`). Only while the block runs: the
|
|
77
|
+
notes keep their own style."""
|
|
70
78
|
with note_palette(), _rc_context(SLIDE_STYLE):
|
|
71
79
|
yield
|
|
72
80
|
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/native.py
RENAMED
|
@@ -27,11 +27,13 @@ from pptx.enum.chart import (
|
|
|
27
27
|
XL_MARKER_STYLE,
|
|
28
28
|
)
|
|
29
29
|
from pptx.enum.chart import XL_TICK_LABEL_POSITION as TICKS
|
|
30
|
-
from pptx.oxml
|
|
30
|
+
from pptx.oxml import parse_xml
|
|
31
|
+
from pptx.oxml.ns import nsdecls, qn
|
|
31
32
|
from pptx.util import Emu, Pt
|
|
32
33
|
|
|
33
34
|
from tesorotools.note.builder import format_number
|
|
34
35
|
from tesorotools.note.charts import NOTE_COLORS, axis_decimals
|
|
36
|
+
from tesorotools.palette import GREY_PANEL
|
|
35
37
|
|
|
36
38
|
__all__ = [
|
|
37
39
|
"NATIVE_KINDS",
|
|
@@ -173,9 +175,10 @@ def add_native_chart(
|
|
|
173
175
|
else:
|
|
174
176
|
series.format.fill.solid()
|
|
175
177
|
series.format.fill.fore_color.rgb = color
|
|
178
|
+
_panel(chart)
|
|
176
179
|
values: Any = chart.value_axis
|
|
177
180
|
values.has_major_gridlines = True
|
|
178
|
-
values.major_gridlines.format.line.color.rgb = _rgb(
|
|
181
|
+
values.major_gridlines.format.line.color.rgb = _rgb("FFFFFF")
|
|
179
182
|
values.format.line.fill.background()
|
|
180
183
|
values.tick_labels.number_format = fmt
|
|
181
184
|
values.tick_labels.number_format_is_linked = False
|
|
@@ -198,6 +201,20 @@ def add_native_chart(
|
|
|
198
201
|
return chart
|
|
199
202
|
|
|
200
203
|
|
|
204
|
+
def _panel(chart: Any) -> None:
|
|
205
|
+
"""The plot area on the grey of the slide charts (:data:`SLIDE_STYLE`),
|
|
206
|
+
under white grid lines. python-pptx has no call for it: its
|
|
207
|
+
``c:spPr`` goes last, after the axes, where the schema wants it (the
|
|
208
|
+
chart python-pptx writes has no ``c:extLst`` to go before)."""
|
|
209
|
+
area: Any = chart._chartSpace.chart.plotArea # pyright: ignore[reportPrivateUsage]
|
|
210
|
+
area.append(
|
|
211
|
+
parse_xml(
|
|
212
|
+
f"<c:spPr {nsdecls('c', 'a')}><a:solidFill><a:srgbClr"
|
|
213
|
+
f' val="{GREY_PANEL}"/></a:solidFill></c:spPr>'
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
201
218
|
def _date_ticks(axis: Any, months: int) -> None:
|
|
202
219
|
"""Marks every year, half year or quarter, as the span asks."""
|
|
203
220
|
if months > 48:
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/presentation/skill/SKILL.md
RENAMED
|
@@ -403,7 +403,7 @@ from tesorotools.note import axis_format
|
|
|
403
403
|
from tesorotools.presentation import slide_chart_size, slide_style
|
|
404
404
|
|
|
405
405
|
series = {"ES10Y": "España", "DE10Y": "Alemania"} # columna: etiqueta
|
|
406
|
-
with slide_style(): # colores de la guía,
|
|
406
|
+
with slide_style(): # colores de la guía, área de datos gris
|
|
407
407
|
LinePlot(
|
|
408
408
|
out_path=Path("graficos/tipos.png"),
|
|
409
409
|
data=df, # DataFrame con DatetimeIndex
|
|
@@ -430,7 +430,8 @@ with slide_style(): # colores de la guía, fondo blanco
|
|
|
430
430
|
`#2957FF`, Azul 3 `#5DA6FF`, Naranja `#E6821E` y Mostaza `#FBA737`) y el
|
|
431
431
|
mismo formato del eje que en las notas: un gráfico de una nota y el de
|
|
432
432
|
una presentación solo cambian de tamaño. `slide_style()` dibuja además
|
|
433
|
-
|
|
433
|
+
el área de datos en gris claro (`#EFEFEF`) con la rejilla blanca, y el
|
|
434
|
+
resto del gráfico en blanco, el de la diapositiva.
|
|
434
435
|
- `legend=True` pone la leyenda debajo del eje, a 12 puntos como el resto
|
|
435
436
|
del gráfico, y solo si hay dos series o más (una sola serie no lleva
|
|
436
437
|
leyenda: la nombra el título). Úsalo solo con los artists que admiten
|
|
@@ -38,15 +38,18 @@ from tesorotools.web.components import (
|
|
|
38
38
|
)
|
|
39
39
|
from tesorotools.web.formatting import format_number, long_date, short_date
|
|
40
40
|
from tesorotools.web.launcher import launch
|
|
41
|
+
from tesorotools.web.period import Period, PeriodChoice, resolve_period
|
|
41
42
|
|
|
42
43
|
if TYPE_CHECKING:
|
|
43
44
|
from tesorotools.web.app import (
|
|
44
45
|
Dashboard,
|
|
46
|
+
PeriodSelector,
|
|
45
47
|
Section,
|
|
46
48
|
block,
|
|
47
49
|
chart,
|
|
48
50
|
download_xlsx,
|
|
49
51
|
html,
|
|
52
|
+
period_selector,
|
|
50
53
|
xlsx_bytes,
|
|
51
54
|
)
|
|
52
55
|
from tesorotools.web.plotting import (
|
|
@@ -63,6 +66,9 @@ __all__ = [
|
|
|
63
66
|
"FontFace",
|
|
64
67
|
"Kpi",
|
|
65
68
|
"NavLink",
|
|
69
|
+
"Period",
|
|
70
|
+
"PeriodChoice",
|
|
71
|
+
"PeriodSelector",
|
|
66
72
|
"Section",
|
|
67
73
|
"badge_html",
|
|
68
74
|
"block",
|
|
@@ -80,6 +86,8 @@ __all__ = [
|
|
|
80
86
|
"long_date",
|
|
81
87
|
"nav_links_html",
|
|
82
88
|
"notice_html",
|
|
89
|
+
"period_selector",
|
|
90
|
+
"resolve_period",
|
|
83
91
|
"short_date",
|
|
84
92
|
"sidebar_logo_html",
|
|
85
93
|
"style_figure",
|
|
@@ -94,7 +102,9 @@ _APP = {
|
|
|
94
102
|
"chart",
|
|
95
103
|
"default_brand",
|
|
96
104
|
"download_xlsx",
|
|
105
|
+
"PeriodSelector",
|
|
97
106
|
"html",
|
|
107
|
+
"period_selector",
|
|
98
108
|
"xlsx_bytes",
|
|
99
109
|
}
|
|
100
110
|
_PLOTTING = {"PLOTLY_CONFIG", "line_figure", "style_figure"}
|
|
@@ -30,10 +30,12 @@ Marks, fonts, signature and navigation come from a
|
|
|
30
30
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
|
+
import datetime
|
|
33
34
|
import io
|
|
34
35
|
import logging
|
|
35
|
-
from collections.abc import Callable, Sequence
|
|
36
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
36
37
|
from dataclasses import dataclass, field
|
|
38
|
+
from typing import Any
|
|
37
39
|
|
|
38
40
|
import pandas as pd
|
|
39
41
|
import plotly.graph_objects as go
|
|
@@ -52,6 +54,14 @@ from tesorotools.web.components import (
|
|
|
52
54
|
section_index_markdown,
|
|
53
55
|
sidebar_logo_html,
|
|
54
56
|
)
|
|
57
|
+
from tesorotools.web.period import (
|
|
58
|
+
CUSTOM,
|
|
59
|
+
Period,
|
|
60
|
+
PeriodChoice,
|
|
61
|
+
Window,
|
|
62
|
+
as_day,
|
|
63
|
+
resolve_period,
|
|
64
|
+
)
|
|
55
65
|
from tesorotools.web.plotting import PLOTLY_CONFIG
|
|
56
66
|
from tesorotools.web.theme import DEFAULT_FAMILY, stylesheet
|
|
57
67
|
|
|
@@ -103,6 +113,166 @@ def chart(fig: go.Figure, *, key: str | None = None) -> None:
|
|
|
103
113
|
)
|
|
104
114
|
|
|
105
115
|
|
|
116
|
+
#: Width of each date field, in pixels: fits "31/12/2026" and its icon.
|
|
117
|
+
_DATE_WIDTH = 140
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _start_key(key: str) -> str:
|
|
121
|
+
return f"{key}-desde"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _end_key(key: str) -> str:
|
|
125
|
+
return f"{key}-hasta"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _as_date(day: pd.Timestamp | None) -> datetime.date | None:
|
|
129
|
+
return None if day is None else day.date()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass(frozen=True)
|
|
133
|
+
class PeriodSelector:
|
|
134
|
+
"""A period control, resolved but not yet drawn.
|
|
135
|
+
|
|
136
|
+
:attr:`period` is the window to draw with; :meth:`render` draws the
|
|
137
|
+
control. They are separate because the chart needs the window
|
|
138
|
+
**before** it is drawn and the control usually goes **under** it::
|
|
139
|
+
|
|
140
|
+
selector = period_selector("curva", WINDOWS, default="5 años",
|
|
141
|
+
dates=frame.index)
|
|
142
|
+
chart(figure(selector.period.clip(frame)))
|
|
143
|
+
selector.render()
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
key: str
|
|
147
|
+
choice: PeriodChoice
|
|
148
|
+
start_label: str = "Desde"
|
|
149
|
+
end_label: str = "Hasta"
|
|
150
|
+
|
|
151
|
+
@property
|
|
152
|
+
def period(self) -> Period:
|
|
153
|
+
return self.choice.period
|
|
154
|
+
|
|
155
|
+
def render(self) -> None:
|
|
156
|
+
"""The row of buttons and, to its right, the two date fields.
|
|
157
|
+
|
|
158
|
+
The date fields get no ``value``: theirs is written in the session
|
|
159
|
+
before drawing them (the window of the selected button, or what
|
|
160
|
+
was typed, clamped to the data), because Streamlit warns when a
|
|
161
|
+
widget gets both, and a day outside ``min_value``/``max_value`` is
|
|
162
|
+
an exception and not a warning. On a narrow screen the row wraps
|
|
163
|
+
and the dates go under the buttons.
|
|
164
|
+
"""
|
|
165
|
+
choice, state = self.choice, st.session_state
|
|
166
|
+
if choice.first is None or choice.last is None:
|
|
167
|
+
st.segmented_control(
|
|
168
|
+
"Periodo",
|
|
169
|
+
choice.options[:-1],
|
|
170
|
+
default=choice.default,
|
|
171
|
+
required=True,
|
|
172
|
+
key=self.key,
|
|
173
|
+
label_visibility="collapsed",
|
|
174
|
+
)
|
|
175
|
+
return
|
|
176
|
+
start, end = _start_key(self.key), _end_key(self.key)
|
|
177
|
+
if choice.period.warning is None:
|
|
178
|
+
state[start] = _as_date(choice.shown_start)
|
|
179
|
+
state[end] = _as_date(choice.shown_end)
|
|
180
|
+
else:
|
|
181
|
+
# What was typed stays, only clamped to the data: nobody gets
|
|
182
|
+
# their input erased for having it the wrong way round.
|
|
183
|
+
for date_key in (start, end):
|
|
184
|
+
day = as_day(state.get(date_key))
|
|
185
|
+
state[date_key] = _as_date(
|
|
186
|
+
None
|
|
187
|
+
if day is None
|
|
188
|
+
else min(max(day, choice.first), choice.last)
|
|
189
|
+
)
|
|
190
|
+
# The button also goes through the session and without
|
|
191
|
+
# ``default``: typing a date writes it there (see
|
|
192
|
+
# ``_to_custom``), and Streamlit warns about a default and a
|
|
193
|
+
# stored value at once.
|
|
194
|
+
if state.get(self.key) not in choice.options:
|
|
195
|
+
state[self.key] = choice.default
|
|
196
|
+
with st.container(
|
|
197
|
+
horizontal=True, vertical_alignment="bottom", gap="small"
|
|
198
|
+
):
|
|
199
|
+
st.segmented_control(
|
|
200
|
+
"Periodo",
|
|
201
|
+
choice.options,
|
|
202
|
+
required=True,
|
|
203
|
+
key=self.key,
|
|
204
|
+
label_visibility="collapsed",
|
|
205
|
+
# Inside a row Streamlit keeps it on one line and scrolls
|
|
206
|
+
# it; on a phone the custom button fell off the screen.
|
|
207
|
+
width="content",
|
|
208
|
+
)
|
|
209
|
+
for label, date_key in (
|
|
210
|
+
(self.start_label, start),
|
|
211
|
+
(self.end_label, end),
|
|
212
|
+
):
|
|
213
|
+
st.date_input(
|
|
214
|
+
label,
|
|
215
|
+
min_value=_as_date(choice.first),
|
|
216
|
+
max_value=_as_date(choice.last),
|
|
217
|
+
format="DD/MM/YYYY",
|
|
218
|
+
key=date_key,
|
|
219
|
+
on_change=_to_custom,
|
|
220
|
+
args=(self.key, choice.options[-1]),
|
|
221
|
+
width=_DATE_WIDTH,
|
|
222
|
+
)
|
|
223
|
+
if choice.period.warning:
|
|
224
|
+
# No ``icon=``: a Material icon written by name is one more
|
|
225
|
+
# place where the house font rule can fail.
|
|
226
|
+
st.warning(choice.period.warning)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _to_custom(key: str, custom_label: str) -> None:
|
|
230
|
+
"""``on_change`` of the date fields: typing selects the custom button.
|
|
231
|
+
|
|
232
|
+
It runs before the rerun, so the next pass already reads the new
|
|
233
|
+
window. The dates themselves are already in the session.
|
|
234
|
+
"""
|
|
235
|
+
st.session_state[key] = custom_label
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def period_selector(
|
|
239
|
+
key: str,
|
|
240
|
+
windows: Mapping[str, Window],
|
|
241
|
+
*,
|
|
242
|
+
default: str,
|
|
243
|
+
dates: Sequence[Any] | pd.Index | pd.Series,
|
|
244
|
+
first: pd.Timestamp | None = None,
|
|
245
|
+
custom_label: str = CUSTOM,
|
|
246
|
+
start_label: str = "Desde",
|
|
247
|
+
end_label: str = "Hasta",
|
|
248
|
+
) -> PeriodSelector:
|
|
249
|
+
"""A row of period buttons with two date fields, read from the session.
|
|
250
|
+
|
|
251
|
+
Nothing is drawn here: this reads what the three widgets left in
|
|
252
|
+
``st.session_state`` (Streamlit writes it before rerunning the page,
|
|
253
|
+
so a click shows in that same pass) and resolves the window with
|
|
254
|
+
:func:`~tesorotools.web.period.resolve_period`. Draw the control
|
|
255
|
+
with :meth:`PeriodSelector.render`, wherever it goes.
|
|
256
|
+
|
|
257
|
+
Clip what is drawn **and** what is downloaded with the same
|
|
258
|
+
:attr:`PeriodSelector.period`, so the workbook is what is on screen.
|
|
259
|
+
To repaint only the chart on a click, put the chart and its control
|
|
260
|
+
inside an ``st.fragment``.
|
|
261
|
+
"""
|
|
262
|
+
state = st.session_state
|
|
263
|
+
choice = resolve_period(
|
|
264
|
+
windows,
|
|
265
|
+
default=default,
|
|
266
|
+
dates=dates,
|
|
267
|
+
chosen=state.get(key),
|
|
268
|
+
typed_start=state.get(_start_key(key)),
|
|
269
|
+
typed_end=state.get(_end_key(key)),
|
|
270
|
+
first=first,
|
|
271
|
+
custom_label=custom_label,
|
|
272
|
+
)
|
|
273
|
+
return PeriodSelector(key, choice, start_label, end_label)
|
|
274
|
+
|
|
275
|
+
|
|
106
276
|
def xlsx_bytes(frame: pd.DataFrame, sheet: str = "datos") -> bytes:
|
|
107
277
|
"""*frame* as an ``.xlsx`` workbook (real numbers, not text)."""
|
|
108
278
|
buffer = io.BytesIO()
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""The window of a time-series chart: preset buttons plus a typed range.
|
|
2
|
+
|
|
3
|
+
A row of preset buttons ("2 años", "5 años", "Todo"...) and, next to it,
|
|
4
|
+
two date fields ("Desde", "Hasta"). They are **one control**:
|
|
5
|
+
|
|
6
|
+
* a button sets the window and the two dates are rewritten with it, so
|
|
7
|
+
the dates always say what is being shown;
|
|
8
|
+
* typing a date selects the custom button and the window becomes the one
|
|
9
|
+
between the two dates, both days included;
|
|
10
|
+
* a date outside the data is clamped to it, and a start later than the
|
|
11
|
+
end is not drawn: the period falls back to the default button and
|
|
12
|
+
carries a warning saying so.
|
|
13
|
+
|
|
14
|
+
This module is the pure part: it decides the window from what the
|
|
15
|
+
widgets left in the session, and needs neither Streamlit nor Plotly. The
|
|
16
|
+
widgets live in :func:`tesorotools.web.app.period_selector`.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from collections.abc import Mapping, Sequence
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import numpy as np
|
|
26
|
+
import numpy.typing as npt
|
|
27
|
+
import pandas as pd
|
|
28
|
+
|
|
29
|
+
#: How far back a preset button looks: whole years before the last
|
|
30
|
+
#: datum, a fixed first day (for instance the last close of the previous
|
|
31
|
+
#: year, for a "year to date" button), or ``None`` for all the data.
|
|
32
|
+
Window = int | pd.Timestamp | None
|
|
33
|
+
|
|
34
|
+
#: Default label of the button that is selected when dates are typed.
|
|
35
|
+
CUSTOM = "Personalizado"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class Period:
|
|
40
|
+
"""The chosen window: from which day to which day, both included.
|
|
41
|
+
|
|
42
|
+
``None`` on either side means "no cut on that side". With a preset
|
|
43
|
+
button ``end`` is always ``None``: the window ends at the last datum.
|
|
44
|
+
``warning`` is what to say under the control when the typed dates
|
|
45
|
+
could not be used.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
label: str
|
|
49
|
+
start: pd.Timestamp | None = None
|
|
50
|
+
end: pd.Timestamp | None = None
|
|
51
|
+
custom: bool = False
|
|
52
|
+
warning: str | None = None
|
|
53
|
+
|
|
54
|
+
def describe(self) -> str:
|
|
55
|
+
"""How the window is named in a message: «"5 años"» or a range."""
|
|
56
|
+
if not self.custom:
|
|
57
|
+
return f"«{self.label}»"
|
|
58
|
+
start = "el principio" if self.start is None else _day(self.start)
|
|
59
|
+
end = "el último dato" if self.end is None else _day(self.end)
|
|
60
|
+
if self.start is None or self.end is None:
|
|
61
|
+
return f"el periodo desde {start} hasta {end}"
|
|
62
|
+
return f"el periodo del {start} al {end}"
|
|
63
|
+
|
|
64
|
+
def mask(
|
|
65
|
+
self, dates: pd.Index | pd.Series[Any] | Sequence[Any]
|
|
66
|
+
) -> npt.NDArray[np.bool_]:
|
|
67
|
+
"""Which of *dates* fall inside the window.
|
|
68
|
+
|
|
69
|
+
The end day is included whole: an intraday stamp of that day is
|
|
70
|
+
inside, not only its midnight.
|
|
71
|
+
"""
|
|
72
|
+
stamps = pd.DatetimeIndex(list(dates))
|
|
73
|
+
inside = np.ones(len(stamps), dtype=bool)
|
|
74
|
+
if self.start is not None:
|
|
75
|
+
inside &= np.asarray(stamps >= self.start, dtype=bool)
|
|
76
|
+
if self.end is not None:
|
|
77
|
+
limit = self.end.normalize() + pd.Timedelta(days=1)
|
|
78
|
+
inside &= np.asarray(stamps < limit, dtype=bool)
|
|
79
|
+
return inside
|
|
80
|
+
|
|
81
|
+
def clip(self, frame: pd.DataFrame) -> pd.DataFrame:
|
|
82
|
+
"""The rows of *frame* (indexed by date) inside the window."""
|
|
83
|
+
if frame.empty or (self.start is None and self.end is None):
|
|
84
|
+
return frame
|
|
85
|
+
return frame.loc[self.mask(frame.index)]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@dataclass(frozen=True)
|
|
89
|
+
class PeriodChoice:
|
|
90
|
+
"""A resolved control: the period, and what the widgets must show.
|
|
91
|
+
|
|
92
|
+
``first`` and ``last`` are the bounds of the date fields (``None``
|
|
93
|
+
when there is no data, and then only the buttons are drawn);
|
|
94
|
+
``shown_start`` and ``shown_end`` are the dates the fields display.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
period: Period
|
|
98
|
+
options: tuple[str, ...]
|
|
99
|
+
default: str
|
|
100
|
+
first: pd.Timestamp | None
|
|
101
|
+
last: pd.Timestamp | None
|
|
102
|
+
shown_start: pd.Timestamp | None
|
|
103
|
+
shown_end: pd.Timestamp | None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _day(stamp: pd.Timestamp) -> str:
|
|
107
|
+
return f"{stamp:%d/%m/%Y}"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def as_day(value: object) -> pd.Timestamp | None:
|
|
111
|
+
"""What a date widget left in the session, as a day; or ``None``."""
|
|
112
|
+
if value is None or value == "":
|
|
113
|
+
return None
|
|
114
|
+
try:
|
|
115
|
+
day = pd.Timestamp(value) # pyright: ignore[reportArgumentType]
|
|
116
|
+
except (TypeError, ValueError):
|
|
117
|
+
return None
|
|
118
|
+
return None if pd.isna(day) else day.normalize()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _clamp(
|
|
122
|
+
day: pd.Timestamp, first: pd.Timestamp, last: pd.Timestamp
|
|
123
|
+
) -> pd.Timestamp:
|
|
124
|
+
return min(max(day, first), last)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def resolve_period(
|
|
128
|
+
windows: Mapping[str, Window],
|
|
129
|
+
*,
|
|
130
|
+
default: str,
|
|
131
|
+
dates: Sequence[Any] | pd.Index | pd.Series,
|
|
132
|
+
chosen: object = None,
|
|
133
|
+
typed_start: object = None,
|
|
134
|
+
typed_end: object = None,
|
|
135
|
+
first: pd.Timestamp | None = None,
|
|
136
|
+
custom_label: str = CUSTOM,
|
|
137
|
+
) -> PeriodChoice:
|
|
138
|
+
"""Decide the window from what the widgets left in the session.
|
|
139
|
+
|
|
140
|
+
*windows* are the preset buttons, in order; *default* the one that
|
|
141
|
+
starts selected. *dates* are the dates of the data, and give the
|
|
142
|
+
bounds of the date fields: from the first one (or from *first*, if
|
|
143
|
+
given and later) to the last one. *chosen*, *typed_start* and
|
|
144
|
+
*typed_end* are the session values of the three widgets; anything
|
|
145
|
+
that is not one of the options, or not a date, counts as unset.
|
|
146
|
+
"""
|
|
147
|
+
if default not in windows:
|
|
148
|
+
raise ValueError(f"default {default!r} is not one of the windows")
|
|
149
|
+
if custom_label in windows:
|
|
150
|
+
raise ValueError(f"{custom_label!r} is already a window label")
|
|
151
|
+
index = pd.DatetimeIndex(pd.to_datetime(pd.Series(list(dates)))).dropna()
|
|
152
|
+
options = (*windows, custom_label)
|
|
153
|
+
label = str(chosen) if chosen in options else default
|
|
154
|
+
|
|
155
|
+
if index.empty:
|
|
156
|
+
period = _preset(windows, default if label == custom_label else label)
|
|
157
|
+
return PeriodChoice(period, options, default, None, None, None, None)
|
|
158
|
+
|
|
159
|
+
newest = pd.Timestamp(index.max())
|
|
160
|
+
last = newest.normalize()
|
|
161
|
+
lower = pd.Timestamp(index.min()).normalize()
|
|
162
|
+
if first is not None:
|
|
163
|
+
lower = min(max(first.normalize(), lower), last)
|
|
164
|
+
|
|
165
|
+
if label != custom_label:
|
|
166
|
+
period = _preset(windows, label, newest)
|
|
167
|
+
else:
|
|
168
|
+
start = as_day(typed_start)
|
|
169
|
+
end = as_day(typed_end)
|
|
170
|
+
start = lower if start is None else _clamp(start, lower, last)
|
|
171
|
+
end = last if end is None else _clamp(end, lower, last)
|
|
172
|
+
if start > end:
|
|
173
|
+
fallback = _preset(windows, default, newest)
|
|
174
|
+
period = Period(
|
|
175
|
+
custom_label,
|
|
176
|
+
fallback.start,
|
|
177
|
+
fallback.end,
|
|
178
|
+
custom=True,
|
|
179
|
+
warning=(
|
|
180
|
+
f"«Desde» ({_day(start)}) es posterior a «Hasta» "
|
|
181
|
+
f"({_day(end)}): corrige una de las dos. Mientras, el "
|
|
182
|
+
f"gráfico enseña «{default}»."
|
|
183
|
+
),
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
period = Period(
|
|
187
|
+
custom_label,
|
|
188
|
+
None if start == lower else start,
|
|
189
|
+
None if end == last else end,
|
|
190
|
+
custom=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
cut = lower if period.start is None else period.start.normalize()
|
|
194
|
+
return PeriodChoice(
|
|
195
|
+
period=period,
|
|
196
|
+
options=options,
|
|
197
|
+
default=default,
|
|
198
|
+
first=lower,
|
|
199
|
+
last=last,
|
|
200
|
+
shown_start=max(cut, lower),
|
|
201
|
+
shown_end=last if period.end is None else period.end,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _preset(
|
|
206
|
+
windows: Mapping[str, Window],
|
|
207
|
+
label: str,
|
|
208
|
+
newest: pd.Timestamp | None = None,
|
|
209
|
+
) -> Period:
|
|
210
|
+
"""The period of a preset button, counted back from *newest*."""
|
|
211
|
+
window = windows[label]
|
|
212
|
+
if window is None:
|
|
213
|
+
return Period(label)
|
|
214
|
+
if isinstance(window, pd.Timestamp):
|
|
215
|
+
return Period(label, window)
|
|
216
|
+
if newest is None:
|
|
217
|
+
return Period(label)
|
|
218
|
+
# From the last datum **with its time**, so a button cuts exactly
|
|
219
|
+
# where a "last N years" filter on the raw data would.
|
|
220
|
+
return Period(label, newest - pd.DateOffset(years=window))
|
|
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.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/intraday_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/plotly_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/shock_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/tikz_backend.py
RENAMED
|
File without changes
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/type_curve.py
RENAMED
|
File without changes
|
{tesorotools_python-0.1.9 → tesorotools_python-0.1.10}/src/tesorotools/artists/vector_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|