openseries 2.1.1__tar.gz → 2.1.3__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.
- {openseries-2.1.1 → openseries-2.1.3}/PKG-INFO +1 -1
- {openseries-2.1.1 → openseries-2.1.3}/openseries/report.py +427 -151
- {openseries-2.1.1 → openseries-2.1.3}/pyproject.toml +5 -6
- {openseries-2.1.1 → openseries-2.1.3}/LICENSE.md +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/README.md +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/__init__.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/_common_model.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/_risk.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/datefixer.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/frame.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/load_plotly.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/owntypes.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/plotly_captor_logo.json +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/plotly_layouts.json +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/portfoliotools.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/py.typed +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/series.py +0 -0
- {openseries-2.1.1 → openseries-2.1.3}/openseries/simulation.py +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
5
6
|
import webbrowser
|
|
6
7
|
from inspect import stack
|
|
7
8
|
from logging import getLogger
|
|
@@ -16,7 +17,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|
|
16
17
|
from plotly.graph_objs import Figure # type: ignore[import-untyped]
|
|
17
18
|
|
|
18
19
|
from .frame import OpenFrame
|
|
19
|
-
from .owntypes import
|
|
20
|
+
from .owntypes import LiteralPlotlyOutput
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
from pandas import DataFrame, Index, Series, Timestamp, concat
|
|
@@ -424,9 +425,6 @@ def _configure_figure_layout(
|
|
|
424
425
|
add_logo: bool,
|
|
425
426
|
vertical_legend: bool,
|
|
426
427
|
title: str | None,
|
|
427
|
-
mobile: bool = False,
|
|
428
|
-
total_min_height: int | None = None,
|
|
429
|
-
table_min_height: int | None = None,
|
|
430
428
|
) -> None:
|
|
431
429
|
"""Configure figure layout with logo, legend, and title.
|
|
432
430
|
|
|
@@ -436,14 +434,12 @@ def _configure_figure_layout(
|
|
|
436
434
|
add_logo: Whether to add logo.
|
|
437
435
|
vertical_legend: Whether to use vertical legend.
|
|
438
436
|
title: Optional title for the figure.
|
|
439
|
-
mobile: Whether this is a mobile layout. Defaults to False.
|
|
440
|
-
total_min_height: Minimum total height for mobile layout in pixels.
|
|
441
|
-
table_min_height: Minimum height for table subplot in pixels.
|
|
442
437
|
"""
|
|
443
438
|
fig, logo = load_plotly_dict()
|
|
444
439
|
|
|
445
440
|
if add_logo:
|
|
446
|
-
|
|
441
|
+
logo_copy = logo.copy()
|
|
442
|
+
figure.add_layout_image(logo_copy)
|
|
447
443
|
|
|
448
444
|
figure.update_layout(fig.get("layout"))
|
|
449
445
|
colorway: list[str] = cast("dict[str, list[str]]", fig["layout"]).get(
|
|
@@ -468,24 +464,6 @@ def _configure_figure_layout(
|
|
|
468
464
|
"orientation": "h",
|
|
469
465
|
}
|
|
470
466
|
|
|
471
|
-
if mobile:
|
|
472
|
-
if vertical_legend:
|
|
473
|
-
legend = {
|
|
474
|
-
"yanchor": "top",
|
|
475
|
-
"y": 1.02,
|
|
476
|
-
"xanchor": "left",
|
|
477
|
-
"x": 0,
|
|
478
|
-
"orientation": "h",
|
|
479
|
-
}
|
|
480
|
-
else:
|
|
481
|
-
legend = {
|
|
482
|
-
"yanchor": "top",
|
|
483
|
-
"y": 1.02,
|
|
484
|
-
"xanchor": "left",
|
|
485
|
-
"x": 0,
|
|
486
|
-
"orientation": "h",
|
|
487
|
-
}
|
|
488
|
-
|
|
489
467
|
layout_updates: dict[str, object] = {
|
|
490
468
|
"legend": legend,
|
|
491
469
|
"colorway": colorway[: copied.item_count],
|
|
@@ -493,59 +471,11 @@ def _configure_figure_layout(
|
|
|
493
471
|
"margin": {"l": 50, "r": 50, "t": 80, "b": 50, "pad": 10},
|
|
494
472
|
}
|
|
495
473
|
|
|
496
|
-
if mobile and total_min_height is not None:
|
|
497
|
-
layout_updates["height"] = total_min_height
|
|
498
|
-
layout_updates["autosize"] = False
|
|
499
|
-
|
|
500
474
|
figure.update_layout(**layout_updates)
|
|
501
475
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
automargin=True,
|
|
506
|
-
tickangle=-45,
|
|
507
|
-
row=1,
|
|
508
|
-
col=1,
|
|
509
|
-
)
|
|
510
|
-
figure.update_xaxes(
|
|
511
|
-
gridcolor="#EEEEEE",
|
|
512
|
-
automargin=True,
|
|
513
|
-
tickangle=-45,
|
|
514
|
-
row=2,
|
|
515
|
-
col=1,
|
|
516
|
-
)
|
|
517
|
-
figure.update_yaxes(
|
|
518
|
-
tickformat=".2%",
|
|
519
|
-
gridcolor="#EEEEEE",
|
|
520
|
-
automargin=True,
|
|
521
|
-
row=1,
|
|
522
|
-
col=1,
|
|
523
|
-
)
|
|
524
|
-
figure.update_yaxes(
|
|
525
|
-
tickformat=".2%",
|
|
526
|
-
gridcolor="#EEEEEE",
|
|
527
|
-
automargin=True,
|
|
528
|
-
row=2,
|
|
529
|
-
col=1,
|
|
530
|
-
)
|
|
531
|
-
if table_min_height is not None and total_min_height is not None:
|
|
532
|
-
plot_height = 400
|
|
533
|
-
bar_height = 350
|
|
534
|
-
spacing = 0.08
|
|
535
|
-
plot_domain_top = 1.0
|
|
536
|
-
plot_domain_bottom = 1.0 - (plot_height / total_min_height)
|
|
537
|
-
bar_domain_top = plot_domain_bottom - spacing
|
|
538
|
-
bar_domain_bottom = bar_domain_top - (bar_height / total_min_height)
|
|
539
|
-
|
|
540
|
-
figure.update_layout(
|
|
541
|
-
yaxis_domain=[plot_domain_bottom, plot_domain_top],
|
|
542
|
-
yaxis2_domain=[bar_domain_bottom, bar_domain_top],
|
|
543
|
-
)
|
|
544
|
-
title_size = 24
|
|
545
|
-
else:
|
|
546
|
-
figure.update_xaxes(gridcolor="#EEEEEE", automargin=True, tickangle=-45)
|
|
547
|
-
figure.update_yaxes(tickformat=".2%", gridcolor="#EEEEEE", automargin=True)
|
|
548
|
-
title_size = 36
|
|
476
|
+
figure.update_xaxes(gridcolor="#EEEEEE", automargin=True, tickangle=-45)
|
|
477
|
+
figure.update_yaxes(tickformat=".2%", gridcolor="#EEEEEE", automargin=True)
|
|
478
|
+
title_size = 36
|
|
549
479
|
|
|
550
480
|
if title:
|
|
551
481
|
figure.update_layout(
|
|
@@ -713,16 +643,95 @@ def _build_mobile_figure(
|
|
|
713
643
|
col=1,
|
|
714
644
|
)
|
|
715
645
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
646
|
+
fig, logo = load_plotly_dict()
|
|
647
|
+
|
|
648
|
+
if add_logo:
|
|
649
|
+
logo_copy = logo.copy()
|
|
650
|
+
logo_copy["x"] = 0.99
|
|
651
|
+
logo_copy["xanchor"] = "right"
|
|
652
|
+
figure_mobile.add_layout_image(logo_copy)
|
|
653
|
+
|
|
654
|
+
figure_mobile.update_layout(fig.get("layout"))
|
|
655
|
+
colorway: list[str] = cast("dict[str, list[str]]", fig["layout"]).get(
|
|
656
|
+
"colorway",
|
|
657
|
+
[],
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
if vertical_legend:
|
|
661
|
+
legend = {
|
|
662
|
+
"yanchor": "bottom",
|
|
663
|
+
"y": -0.04,
|
|
664
|
+
"xanchor": "right",
|
|
665
|
+
"x": 0.98,
|
|
666
|
+
"orientation": "v",
|
|
667
|
+
}
|
|
668
|
+
else:
|
|
669
|
+
legend = {
|
|
670
|
+
"yanchor": "bottom",
|
|
671
|
+
"y": -0.2,
|
|
672
|
+
"xanchor": "right",
|
|
673
|
+
"x": 0.98,
|
|
674
|
+
"orientation": "h",
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
layout_updates: dict[str, object] = {
|
|
678
|
+
"legend": legend,
|
|
679
|
+
"colorway": colorway[: copied.item_count],
|
|
680
|
+
"autosize": False,
|
|
681
|
+
"height": total_min_height,
|
|
682
|
+
"margin": {"l": 50, "r": 50, "t": 80, "b": 50, "pad": 10},
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
figure_mobile.update_layout(**layout_updates)
|
|
686
|
+
|
|
687
|
+
figure_mobile.update_xaxes(
|
|
688
|
+
gridcolor="#EEEEEE",
|
|
689
|
+
automargin=True,
|
|
690
|
+
tickangle=-45,
|
|
691
|
+
row=1,
|
|
692
|
+
col=1,
|
|
693
|
+
)
|
|
694
|
+
figure_mobile.update_xaxes(
|
|
695
|
+
gridcolor="#EEEEEE",
|
|
696
|
+
automargin=True,
|
|
697
|
+
tickangle=-45,
|
|
698
|
+
row=2,
|
|
699
|
+
col=1,
|
|
700
|
+
)
|
|
701
|
+
figure_mobile.update_yaxes(
|
|
702
|
+
tickformat=".2%",
|
|
703
|
+
gridcolor="#EEEEEE",
|
|
704
|
+
automargin=True,
|
|
705
|
+
row=1,
|
|
706
|
+
col=1,
|
|
707
|
+
)
|
|
708
|
+
figure_mobile.update_yaxes(
|
|
709
|
+
tickformat=".2%",
|
|
710
|
+
gridcolor="#EEEEEE",
|
|
711
|
+
automargin=True,
|
|
712
|
+
row=2,
|
|
713
|
+
col=1,
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
plot_height = 400
|
|
717
|
+
bar_height = 350
|
|
718
|
+
spacing = 0.08
|
|
719
|
+
plot_domain_top = 1.0
|
|
720
|
+
plot_domain_bottom = 1.0 - (plot_height / total_min_height)
|
|
721
|
+
bar_domain_top = plot_domain_bottom - spacing
|
|
722
|
+
bar_domain_bottom = bar_domain_top - (bar_height / total_min_height)
|
|
723
|
+
|
|
724
|
+
figure_mobile.update_layout(
|
|
725
|
+
yaxis_domain=[plot_domain_bottom, plot_domain_top],
|
|
726
|
+
yaxis2_domain=[bar_domain_bottom, bar_domain_top],
|
|
724
727
|
)
|
|
725
728
|
|
|
729
|
+
title_size = 24
|
|
730
|
+
if title:
|
|
731
|
+
figure_mobile.update_layout(
|
|
732
|
+
{"title": {"text": f"<b>{title}</b><br>", "font": {"size": title_size}}},
|
|
733
|
+
)
|
|
734
|
+
|
|
726
735
|
return figure_mobile
|
|
727
736
|
|
|
728
737
|
|
|
@@ -830,7 +839,10 @@ def _generate_responsive_html_string(
|
|
|
830
839
|
"""
|
|
831
840
|
desktop_style = "width:100%;"
|
|
832
841
|
mobile_style = "width:100%; display:none;"
|
|
833
|
-
match_media =
|
|
842
|
+
match_media = (
|
|
843
|
+
'(window.matchMedia("(max-width: 960px)").matches || '
|
|
844
|
+
'"ontouchstart" in window || navigator.maxTouchPoints > 0)'
|
|
845
|
+
)
|
|
834
846
|
desktop_get = f'document.getElementById("{div_id_desktop}_container")'
|
|
835
847
|
mobile_get = f'document.getElementById("{div_id_mobile}_container")'
|
|
836
848
|
|
|
@@ -846,8 +858,10 @@ def _generate_responsive_html_string(
|
|
|
846
858
|
f"{mobile_content}\n"
|
|
847
859
|
f"</div>\n"
|
|
848
860
|
"<style>\n"
|
|
861
|
+
"html, body { height: 100%; margin: 0; padding: 0; }\n"
|
|
849
862
|
"body { overflow-y: auto; }\n"
|
|
850
|
-
"
|
|
863
|
+
".plotly-desktop { min-height: 100vh; }\n"
|
|
864
|
+
"@media (max-width: 960px), (pointer: coarse), (hover: none) {\n"
|
|
851
865
|
" .plotly-desktop { display: none !important; }\n"
|
|
852
866
|
" .plotly-mobile { display: block !important; "
|
|
853
867
|
"overflow: visible !important; }\n"
|
|
@@ -871,13 +885,46 @@ def _generate_responsive_html_string(
|
|
|
871
885
|
" .plotly-mobile [style*='overflow'] { "
|
|
872
886
|
"overflow: visible !important; overflow-y: visible !important; }\n"
|
|
873
887
|
"}\n"
|
|
874
|
-
"@media (min-width: 961px) {\n"
|
|
888
|
+
"@media (min-width: 961px) and (pointer: fine) and (hover: hover) {\n"
|
|
875
889
|
" .plotly-desktop { display: block !important; }\n"
|
|
876
890
|
" .plotly-mobile { display: none !important; }\n"
|
|
891
|
+
" .plotly-desktop .js-plotly-plot { "
|
|
892
|
+
"overflow: hidden !important; overflow-y: hidden !important; "
|
|
893
|
+
"overflow-x: hidden !important; }\n"
|
|
894
|
+
" .plotly-desktop .js-plotly-plot > div { "
|
|
895
|
+
"overflow: hidden !important; overflow-y: hidden !important; "
|
|
896
|
+
"overflow-x: hidden !important; }\n"
|
|
897
|
+
" .plotly-desktop .scrollbar-slider { "
|
|
898
|
+
"display: none !important; visibility: hidden !important; "
|
|
899
|
+
"opacity: 0 !important; }\n"
|
|
900
|
+
" .plotly-desktop .scrollbar-glyph { "
|
|
901
|
+
"display: none !important; visibility: hidden !important; "
|
|
902
|
+
"opacity: 0 !important; }\n"
|
|
877
903
|
"}\n"
|
|
878
904
|
"</style>\n"
|
|
879
905
|
"<script>\n"
|
|
880
906
|
"(function() {\n"
|
|
907
|
+
" function adjustLogoPosition(container) {\n"
|
|
908
|
+
" if (!container) return;\n"
|
|
909
|
+
" var plots = container.querySelectorAll('.js-plotly-plot');\n"
|
|
910
|
+
" plots.forEach(function(plot) {\n"
|
|
911
|
+
" var svg = plot.querySelector('svg');\n"
|
|
912
|
+
" if (!svg) return;\n"
|
|
913
|
+
" var images = svg.querySelectorAll('image[xref=\"paper\"]');\n"
|
|
914
|
+
" images.forEach(function(img) {\n"
|
|
915
|
+
" var isNarrow = window.matchMedia("
|
|
916
|
+
"'(max-width: 960px)').matches;\n"
|
|
917
|
+
" if (isNarrow) {\n"
|
|
918
|
+
" img.setAttribute('x', '0.99');\n"
|
|
919
|
+
" img.setAttribute('xanchor', 'right');\n"
|
|
920
|
+
" } else {\n"
|
|
921
|
+
" img.setAttribute('x', '0.01');\n"
|
|
922
|
+
" img.removeAttribute('xanchor');\n"
|
|
923
|
+
" }\n"
|
|
924
|
+
" });\n"
|
|
925
|
+
" });\n"
|
|
926
|
+
" }\n"
|
|
927
|
+
"\n"
|
|
881
928
|
" function updateLayout() {\n"
|
|
882
929
|
f" var isMobile = {match_media};\n"
|
|
883
930
|
f" var desktopContainer = {desktop_get};\n"
|
|
@@ -900,12 +947,71 @@ def _generate_responsive_html_string(
|
|
|
900
947
|
'mobileContainer.style.display = "none";\n'
|
|
901
948
|
" disableTableScrolling(desktopContainer);\n"
|
|
902
949
|
" setTimeout(function() { "
|
|
903
|
-
"adjustTableSize(desktopContainer);
|
|
950
|
+
"adjustTableSize(desktopContainer); "
|
|
951
|
+
"adjustLogoPosition(desktopContainer); }, 100);\n"
|
|
904
952
|
" }\n"
|
|
953
|
+
" adjustLogoPosition(desktopContainer);\n"
|
|
905
954
|
" }\n"
|
|
906
955
|
"\n"
|
|
907
956
|
" function adjustTableSize(container) {\n"
|
|
908
957
|
" if (!container) return;\n"
|
|
958
|
+
" \n"
|
|
959
|
+
" function forceNoScroll() {\n"
|
|
960
|
+
" var scrollbarKits = container.querySelectorAll("
|
|
961
|
+
"'.scrollbar-kit');\n"
|
|
962
|
+
" scrollbarKits.forEach(function(el) {\n"
|
|
963
|
+
" el.style.setProperty('display', 'none', "
|
|
964
|
+
"'important');\n"
|
|
965
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
966
|
+
"'important');\n"
|
|
967
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
968
|
+
" });\n"
|
|
969
|
+
" \n"
|
|
970
|
+
" var scrollbarSliders = container.querySelectorAll("
|
|
971
|
+
"'.scrollbar-slider');\n"
|
|
972
|
+
" scrollbarSliders.forEach(function(el) {\n"
|
|
973
|
+
" el.style.setProperty('display', 'none', "
|
|
974
|
+
"'important');\n"
|
|
975
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
976
|
+
"'important');\n"
|
|
977
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
978
|
+
" });\n"
|
|
979
|
+
" \n"
|
|
980
|
+
" var scrollbarGlyphs = container.querySelectorAll("
|
|
981
|
+
"'.scrollbar-glyph');\n"
|
|
982
|
+
" scrollbarGlyphs.forEach(function(el) {\n"
|
|
983
|
+
" el.style.setProperty('display', 'none', "
|
|
984
|
+
"'important');\n"
|
|
985
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
986
|
+
"'important');\n"
|
|
987
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
988
|
+
" });\n"
|
|
989
|
+
" \n"
|
|
990
|
+
" var allElements = container.querySelectorAll('*');\n"
|
|
991
|
+
" allElements.forEach(function(el) {\n"
|
|
992
|
+
" var computed = window.getComputedStyle(el);\n"
|
|
993
|
+
" if (computed.overflowY === 'auto' || "
|
|
994
|
+
"computed.overflowY === 'scroll' || computed.overflow === 'auto' || "
|
|
995
|
+
"computed.overflow === 'scroll') {\n"
|
|
996
|
+
" el.style.setProperty('overflow', 'hidden', "
|
|
997
|
+
"'important');\n"
|
|
998
|
+
" el.style.setProperty('overflow-y', 'hidden', "
|
|
999
|
+
"'important');\n"
|
|
1000
|
+
" el.style.setProperty('overflow-x', 'hidden', "
|
|
1001
|
+
"'important');\n"
|
|
1002
|
+
" }\n"
|
|
1003
|
+
" if (el.scrollHeight > el.clientHeight && "
|
|
1004
|
+
"el.clientHeight > 0) {\n"
|
|
1005
|
+
" el.style.setProperty('max-height', "
|
|
1006
|
+
"el.clientHeight + 'px', 'important');\n"
|
|
1007
|
+
" el.style.setProperty('overflow', 'hidden', "
|
|
1008
|
+
"'important');\n"
|
|
1009
|
+
" el.style.setProperty('overflow-y', 'hidden', "
|
|
1010
|
+
"'important');\n"
|
|
1011
|
+
" }\n"
|
|
1012
|
+
" });\n"
|
|
1013
|
+
" }\n"
|
|
1014
|
+
" \n"
|
|
909
1015
|
" var plots = container.querySelectorAll('.js-plotly-plot');\n"
|
|
910
1016
|
" plots.forEach(function(plot) {\n"
|
|
911
1017
|
" var svg = plot.querySelector('svg');\n"
|
|
@@ -914,89 +1020,197 @@ def _generate_responsive_html_string(
|
|
|
914
1020
|
" if (!tableGroup) return;\n"
|
|
915
1021
|
" var plotRect = plot.getBoundingClientRect();\n"
|
|
916
1022
|
" if (plotRect.height === 0) return;\n"
|
|
917
|
-
"
|
|
1023
|
+
" \n"
|
|
1024
|
+
" function adjustTable() {\n"
|
|
918
1025
|
" try {\n"
|
|
919
|
-
"
|
|
920
|
-
"
|
|
921
|
-
" var availableHeight = plotRect.height -
|
|
922
|
-
"
|
|
923
|
-
"
|
|
924
|
-
"
|
|
925
|
-
"
|
|
926
|
-
"
|
|
927
|
-
"
|
|
928
|
-
"
|
|
929
|
-
"
|
|
930
|
-
"
|
|
931
|
-
"
|
|
932
|
-
"
|
|
933
|
-
"
|
|
934
|
-
"
|
|
935
|
-
"
|
|
936
|
-
"
|
|
937
|
-
"
|
|
938
|
-
"
|
|
939
|
-
"
|
|
940
|
-
"
|
|
941
|
-
"
|
|
942
|
-
"
|
|
1026
|
+
" forceNoScroll();\n"
|
|
1027
|
+
" \n"
|
|
1028
|
+
" var availableHeight = plotRect.height - 100;\n"
|
|
1029
|
+
" if (availableHeight <= 0) return;\n"
|
|
1030
|
+
" \n"
|
|
1031
|
+
" var rows = tableGroup.querySelectorAll("
|
|
1032
|
+
"'g[class*=\"row\"]');\n"
|
|
1033
|
+
" if (rows.length === 0) return;\n"
|
|
1034
|
+
" \n"
|
|
1035
|
+
" var rowCount = rows.length;\n"
|
|
1036
|
+
" var calculatedRowHeight = availableHeight / rowCount;\n"
|
|
1037
|
+
" var minRowHeight = 18;\n"
|
|
1038
|
+
" var maxRowHeight = 35;\n"
|
|
1039
|
+
" var rowHeight = Math.max(minRowHeight, "
|
|
1040
|
+
"Math.min(maxRowHeight, calculatedRowHeight));\n"
|
|
1041
|
+
" \n"
|
|
1042
|
+
" var baseFontSize = 12;\n"
|
|
1043
|
+
" var fontScale = rowHeight / 25;\n"
|
|
1044
|
+
" var fontSize = baseFontSize * fontScale;\n"
|
|
1045
|
+
" var minFontSize = 9;\n"
|
|
1046
|
+
" var maxFontSize = 14;\n"
|
|
1047
|
+
" fontSize = Math.max(minFontSize, "
|
|
1048
|
+
"Math.min(maxFontSize, fontSize));\n"
|
|
1049
|
+
" \n"
|
|
1050
|
+
" rows.forEach(function(row, rowIndex) {\n"
|
|
1051
|
+
" var yPos = rowIndex * rowHeight;\n"
|
|
1052
|
+
" var rects = row.querySelectorAll('rect');\n"
|
|
1053
|
+
" var texts = row.querySelectorAll('text');\n"
|
|
1054
|
+
" \n"
|
|
1055
|
+
" rects.forEach(function(rect) {\n"
|
|
1056
|
+
" var currentHeight = "
|
|
1057
|
+
"parseFloat(rect.getAttribute('height') || '25');\n"
|
|
1058
|
+
" if (currentHeight > 0) {\n"
|
|
1059
|
+
" rect.setAttribute('height', "
|
|
1060
|
+
"rowHeight.toString());\n"
|
|
1061
|
+
" var currentY = "
|
|
1062
|
+
"parseFloat(rect.getAttribute('y') || '0');\n"
|
|
1063
|
+
" var rowBaseY = Math.floor(currentY / "
|
|
1064
|
+
"currentHeight) * rowHeight;\n"
|
|
1065
|
+
" rect.setAttribute('y', "
|
|
1066
|
+
"rowBaseY.toString());\n"
|
|
1067
|
+
" }\n"
|
|
1068
|
+
" });\n"
|
|
1069
|
+
" \n"
|
|
1070
|
+
" texts.forEach(function(text) {\n"
|
|
1071
|
+
" text.setAttribute('font-size', "
|
|
1072
|
+
"fontSize.toString());\n"
|
|
1073
|
+
" });\n"
|
|
1074
|
+
" });\n"
|
|
1075
|
+
" \n"
|
|
1076
|
+
" var existingTransform = "
|
|
943
1077
|
"tableGroup.getAttribute('transform') || '';\n"
|
|
944
|
-
"
|
|
1078
|
+
" var translateMatch = "
|
|
945
1079
|
"existingTransform.match(/translate\\(([^)]+)\\)/);\n"
|
|
946
|
-
"
|
|
1080
|
+
" var translate = translateMatch ? "
|
|
947
1081
|
"translateMatch[0] : 'translate(0,0)';\n"
|
|
948
|
-
"
|
|
949
|
-
"
|
|
950
|
-
"
|
|
1082
|
+
" tableGroup.setAttribute('transform', translate);\n"
|
|
1083
|
+
" \n"
|
|
1084
|
+
" forceNoScroll();\n"
|
|
951
1085
|
" } catch (e) {\n"
|
|
952
1086
|
" console.log('Table adjustment error:', e);\n"
|
|
953
1087
|
" }\n"
|
|
954
|
-
" }
|
|
1088
|
+
" }\n"
|
|
1089
|
+
" \n"
|
|
1090
|
+
" setTimeout(adjustTable, 200);\n"
|
|
1091
|
+
" setTimeout(adjustTable, 600);\n"
|
|
1092
|
+
" setTimeout(adjustTable, 1200);\n"
|
|
1093
|
+
" setTimeout(adjustTable, 2000);\n"
|
|
955
1094
|
" });\n"
|
|
956
1095
|
" }\n"
|
|
957
1096
|
"\n"
|
|
958
1097
|
" function disableTableScrolling(container) {\n"
|
|
959
1098
|
" if (!container) return;\n"
|
|
1099
|
+
" var isDesktop = container.classList && "
|
|
1100
|
+
"container.classList.contains('plotly-desktop');\n"
|
|
1101
|
+
" var overflowValue = isDesktop ? 'hidden' : 'visible';\n"
|
|
960
1102
|
" var plots = container.querySelectorAll('.js-plotly-plot');\n"
|
|
961
1103
|
" plots.forEach(function(plot) {\n"
|
|
962
1104
|
" var allElements = plot.querySelectorAll('*');\n"
|
|
963
1105
|
" allElements.forEach(function(el) {\n"
|
|
964
|
-
"
|
|
965
|
-
"
|
|
966
|
-
"
|
|
967
|
-
"
|
|
1106
|
+
" if (el.tagName === 'DIV' || el.tagName === 'SVG') {\n"
|
|
1107
|
+
" var style = window.getComputedStyle(el);\n"
|
|
1108
|
+
" var overflow = style.overflow;\n"
|
|
1109
|
+
" var overflowY = style.overflowY;\n"
|
|
1110
|
+
" if (overflow === 'auto' || overflow === 'scroll' || "
|
|
968
1111
|
"overflowY === 'auto' || overflowY === 'scroll') {\n"
|
|
969
|
-
"
|
|
970
|
-
"'important');\n"
|
|
971
|
-
"
|
|
972
|
-
"'important');\n"
|
|
973
|
-
"
|
|
974
|
-
"'important');\n"
|
|
1112
|
+
" el.style.setProperty('overflow', "
|
|
1113
|
+
"overflowValue, 'important');\n"
|
|
1114
|
+
" el.style.setProperty('overflow-y', "
|
|
1115
|
+
"overflowValue, 'important');\n"
|
|
1116
|
+
" el.style.setProperty('overflow-x', "
|
|
1117
|
+
"overflowValue, 'important');\n"
|
|
1118
|
+
" }\n"
|
|
975
1119
|
" }\n"
|
|
976
1120
|
" });\n"
|
|
977
|
-
" plot.style.setProperty('overflow',
|
|
978
|
-
"
|
|
1121
|
+
" plot.style.setProperty('overflow', overflowValue, "
|
|
1122
|
+
"'important');\n"
|
|
1123
|
+
" plot.style.setProperty('overflow-y', overflowValue, "
|
|
979
1124
|
"'important');\n"
|
|
980
1125
|
" var plotDivs = plot.querySelectorAll('div');\n"
|
|
981
1126
|
" plotDivs.forEach(function(div) {\n"
|
|
982
|
-
" div.style.setProperty('overflow',
|
|
1127
|
+
" div.style.setProperty('overflow', overflowValue, "
|
|
983
1128
|
"'important');\n"
|
|
984
|
-
" div.style.setProperty('overflow-y',
|
|
1129
|
+
" div.style.setProperty('overflow-y', overflowValue, "
|
|
985
1130
|
"'important');\n"
|
|
986
1131
|
" });\n"
|
|
987
1132
|
" var svgs = plot.querySelectorAll('svg');\n"
|
|
988
1133
|
" svgs.forEach(function(svg) {\n"
|
|
989
|
-
" svg.style.setProperty('overflow',
|
|
1134
|
+
" svg.style.setProperty('overflow', overflowValue, "
|
|
990
1135
|
"'important');\n"
|
|
991
|
-
" svg.setAttribute('overflow',
|
|
1136
|
+
" svg.setAttribute('overflow', overflowValue);\n"
|
|
992
1137
|
" });\n"
|
|
993
1138
|
" });\n"
|
|
994
1139
|
" }\n"
|
|
995
1140
|
"\n"
|
|
996
1141
|
" function setupScrollObserver(container) {\n"
|
|
997
1142
|
" if (!container) return;\n"
|
|
1143
|
+
" var isDesktop = container.classList && "
|
|
1144
|
+
"container.classList.contains('plotly-desktop');\n"
|
|
1145
|
+
" \n"
|
|
1146
|
+
" function preventScrollbars() {\n"
|
|
1147
|
+
" if (isDesktop) {\n"
|
|
1148
|
+
" var scrollbarKits = container.querySelectorAll("
|
|
1149
|
+
"'.scrollbar-kit');\n"
|
|
1150
|
+
" scrollbarKits.forEach(function(el) {\n"
|
|
1151
|
+
" el.style.setProperty('display', 'none', "
|
|
1152
|
+
"'important');\n"
|
|
1153
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
1154
|
+
"'important');\n"
|
|
1155
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
1156
|
+
" });\n"
|
|
1157
|
+
" \n"
|
|
1158
|
+
" var scrollbarSliders = container.querySelectorAll("
|
|
1159
|
+
"'.scrollbar-slider');\n"
|
|
1160
|
+
" scrollbarSliders.forEach(function(el) {\n"
|
|
1161
|
+
" el.style.setProperty('display', 'none', "
|
|
1162
|
+
"'important');\n"
|
|
1163
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
1164
|
+
"'important');\n"
|
|
1165
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
1166
|
+
" });\n"
|
|
1167
|
+
" \n"
|
|
1168
|
+
" var scrollbarGlyphs = container.querySelectorAll("
|
|
1169
|
+
"'.scrollbar-glyph');\n"
|
|
1170
|
+
" scrollbarGlyphs.forEach(function(el) {\n"
|
|
1171
|
+
" el.style.setProperty('display', 'none', "
|
|
1172
|
+
"'important');\n"
|
|
1173
|
+
" el.style.setProperty('visibility', 'hidden', "
|
|
1174
|
+
"'important');\n"
|
|
1175
|
+
" el.style.setProperty('opacity', '0', 'important');\n"
|
|
1176
|
+
" });\n"
|
|
1177
|
+
" \n"
|
|
1178
|
+
" var plots = container.querySelectorAll('.js-plotly-plot');\n"
|
|
1179
|
+
" plots.forEach(function(plot) {\n"
|
|
1180
|
+
" var allElements = plot.querySelectorAll('*');\n"
|
|
1181
|
+
" allElements.forEach(function(el) {\n"
|
|
1182
|
+
" if (el.tagName === 'DIV' || "
|
|
1183
|
+
"el.tagName === 'SVG') {\n"
|
|
1184
|
+
" var style = window.getComputedStyle(el);\n"
|
|
1185
|
+
" if (style.overflow === 'auto' || "
|
|
1186
|
+
"style.overflow === 'scroll' || style.overflowY === 'auto' || "
|
|
1187
|
+
"style.overflowY === 'scroll') {\n"
|
|
1188
|
+
" el.style.setProperty('overflow', "
|
|
1189
|
+
"'hidden', 'important');\n"
|
|
1190
|
+
" el.style.setProperty('overflow-y', "
|
|
1191
|
+
"'hidden', 'important');\n"
|
|
1192
|
+
" el.style.setProperty('overflow-x', "
|
|
1193
|
+
"'hidden', 'important');\n"
|
|
1194
|
+
" }\n"
|
|
1195
|
+
" }\n"
|
|
1196
|
+
" });\n"
|
|
1197
|
+
" plot.style.setProperty('overflow', 'hidden', "
|
|
1198
|
+
"'important');\n"
|
|
1199
|
+
" var svg = plot.querySelector('svg');\n"
|
|
1200
|
+
" if (svg) {\n"
|
|
1201
|
+
" svg.style.setProperty('overflow', 'hidden', "
|
|
1202
|
+
"'important');\n"
|
|
1203
|
+
" svg.setAttribute('overflow', 'hidden');\n"
|
|
1204
|
+
" }\n"
|
|
1205
|
+
" });\n"
|
|
1206
|
+
" adjustTableSize(container);\n"
|
|
1207
|
+
" } else {\n"
|
|
1208
|
+
" disableTableScrolling(container);\n"
|
|
1209
|
+
" }\n"
|
|
1210
|
+
" }\n"
|
|
1211
|
+
" \n"
|
|
998
1212
|
" var observer = new MutationObserver(function(mutations) {\n"
|
|
999
|
-
"
|
|
1213
|
+
" preventScrollbars();\n"
|
|
1000
1214
|
" });\n"
|
|
1001
1215
|
" observer.observe(container, {\n"
|
|
1002
1216
|
" childList: true,\n"
|
|
@@ -1004,6 +1218,7 @@ def _generate_responsive_html_string(
|
|
|
1004
1218
|
" attributes: true,\n"
|
|
1005
1219
|
" attributeFilter: ['style', 'class']\n"
|
|
1006
1220
|
" });\n"
|
|
1221
|
+
" preventScrollbars();\n"
|
|
1007
1222
|
" return observer;\n"
|
|
1008
1223
|
" }\n"
|
|
1009
1224
|
"\n"
|
|
@@ -1014,10 +1229,28 @@ def _generate_responsive_html_string(
|
|
|
1014
1229
|
" if (desktopContainer && "
|
|
1015
1230
|
'desktopContainer.style.display !== "none") {\n'
|
|
1016
1231
|
" adjustTableSize(desktopContainer);\n"
|
|
1232
|
+
" adjustLogoPosition(desktopContainer);\n"
|
|
1233
|
+
" var plotDiv = desktopContainer.querySelector("
|
|
1234
|
+
"'.js-plotly-plot');\n"
|
|
1235
|
+
" if (plotDiv && typeof Plotly !== 'undefined') {\n"
|
|
1236
|
+
" Plotly.Plots.resize(plotDiv);\n"
|
|
1237
|
+
" }\n"
|
|
1017
1238
|
" }\n"
|
|
1018
1239
|
" }, 100);\n"
|
|
1019
1240
|
" });\n"
|
|
1020
1241
|
" updateLayout();\n"
|
|
1242
|
+
" // Set desktop plot container to full viewport height on load\n"
|
|
1243
|
+
" setTimeout(function() {\n"
|
|
1244
|
+
f" var desktopContainer = {desktop_get};\n"
|
|
1245
|
+
" if (desktopContainer && typeof Plotly !== 'undefined') {\n"
|
|
1246
|
+
" var plotDiv = desktopContainer.querySelector("
|
|
1247
|
+
"'.js-plotly-plot');\n"
|
|
1248
|
+
" if (plotDiv) {\n"
|
|
1249
|
+
" plotDiv.style.height = window.innerHeight + 'px';\n"
|
|
1250
|
+
" Plotly.Plots.resize(plotDiv);\n"
|
|
1251
|
+
" }\n"
|
|
1252
|
+
" }\n"
|
|
1253
|
+
" }, 100);\n"
|
|
1021
1254
|
" var checkInterval = setInterval(function() {\n"
|
|
1022
1255
|
f" var mobileContainer = {mobile_get};\n"
|
|
1023
1256
|
f" var desktopContainer = {desktop_get};\n"
|
|
@@ -1031,8 +1264,8 @@ def _generate_responsive_html_string(
|
|
|
1031
1264
|
" }\n"
|
|
1032
1265
|
" if (desktopContainer && "
|
|
1033
1266
|
'desktopContainer.style.display !== "none") {\n'
|
|
1034
|
-
" disableTableScrolling(desktopContainer);\n"
|
|
1035
1267
|
" adjustTableSize(desktopContainer);\n"
|
|
1268
|
+
" adjustLogoPosition(desktopContainer);\n"
|
|
1036
1269
|
" if (!desktopContainer._scrollObserver) {\n"
|
|
1037
1270
|
" desktopContainer._scrollObserver = "
|
|
1038
1271
|
"setupScrollObserver(desktopContainer);\n"
|
|
@@ -1045,6 +1278,36 @@ def _generate_responsive_html_string(
|
|
|
1045
1278
|
)
|
|
1046
1279
|
|
|
1047
1280
|
|
|
1281
|
+
def _wrap_in_full_html(
|
|
1282
|
+
responsive_content: str,
|
|
1283
|
+
) -> str:
|
|
1284
|
+
"""Wrap responsive HTML content in a full HTML document.
|
|
1285
|
+
|
|
1286
|
+
Args:
|
|
1287
|
+
responsive_content: The responsive HTML content (divs, CSS, JS).
|
|
1288
|
+
|
|
1289
|
+
Returns:
|
|
1290
|
+
Full HTML document string.
|
|
1291
|
+
"""
|
|
1292
|
+
plotly_js_script = (
|
|
1293
|
+
'<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>'
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
return (
|
|
1297
|
+
"<!DOCTYPE html>\n"
|
|
1298
|
+
'<html lang="en">\n'
|
|
1299
|
+
"<head>\n"
|
|
1300
|
+
' <meta charset="utf-8" />\n'
|
|
1301
|
+
" <title>Plotly Report</title>\n"
|
|
1302
|
+
f" {plotly_js_script}\n"
|
|
1303
|
+
"</head>\n"
|
|
1304
|
+
"<body>\n"
|
|
1305
|
+
f"{responsive_content}\n"
|
|
1306
|
+
"</body>\n"
|
|
1307
|
+
"</html>"
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
|
|
1048
1311
|
def report_html(
|
|
1049
1312
|
data: OpenFrame,
|
|
1050
1313
|
bar_freq: LiteralBizDayFreq = "BYE",
|
|
@@ -1052,7 +1315,6 @@ def report_html(
|
|
|
1052
1315
|
title: str | None = None,
|
|
1053
1316
|
directory: Path | None = None,
|
|
1054
1317
|
output_type: LiteralPlotlyOutput = "file",
|
|
1055
|
-
include_plotlyjs: LiteralPlotlyJSlib = "cdn",
|
|
1056
1318
|
*,
|
|
1057
1319
|
auto_open: bool = False,
|
|
1058
1320
|
add_logo: bool = True,
|
|
@@ -1067,9 +1329,6 @@ def report_html(
|
|
|
1067
1329
|
title: The report page title.
|
|
1068
1330
|
directory: Directory where Plotly HTML file is saved.
|
|
1069
1331
|
output_type: Determines output type. Defaults to "file".
|
|
1070
|
-
include_plotlyjs: Determines how the plotly.js library is included in
|
|
1071
|
-
the output.
|
|
1072
|
-
Defaults to "cdn".
|
|
1073
1332
|
auto_open: Determines whether to open a browser window with the plot.
|
|
1074
1333
|
Defaults to False.
|
|
1075
1334
|
add_logo: If True a Captor logo is added to the plot. Defaults to True.
|
|
@@ -1077,7 +1336,13 @@ def report_html(
|
|
|
1077
1336
|
labels. Defaults to False.
|
|
1078
1337
|
|
|
1079
1338
|
Returns:
|
|
1080
|
-
|
|
1339
|
+
A tuple containing:
|
|
1340
|
+
- Plotly Figure object (the desktop version of the figure)
|
|
1341
|
+
- When ``output_type="file"``: A string containing the file path to the
|
|
1342
|
+
saved HTML file
|
|
1343
|
+
- When ``output_type="div"``: A string containing the responsive HTML div
|
|
1344
|
+
section (includes both desktop and mobile layouts with CSS and
|
|
1345
|
+
JavaScript)
|
|
1081
1346
|
|
|
1082
1347
|
"""
|
|
1083
1348
|
copied = data.from_deepcopy()
|
|
@@ -1139,12 +1404,11 @@ def report_html(
|
|
|
1139
1404
|
plotfile = dirpath / filename
|
|
1140
1405
|
|
|
1141
1406
|
_configure_figure_layout(
|
|
1142
|
-
figure,
|
|
1143
|
-
copied,
|
|
1407
|
+
figure=figure,
|
|
1408
|
+
copied=copied,
|
|
1144
1409
|
add_logo=add_logo,
|
|
1145
1410
|
vertical_legend=vertical_legend,
|
|
1146
1411
|
title=title,
|
|
1147
|
-
mobile=False,
|
|
1148
1412
|
)
|
|
1149
1413
|
|
|
1150
1414
|
figure_mobile = _build_mobile_figure(
|
|
@@ -1168,12 +1432,14 @@ def report_html(
|
|
|
1168
1432
|
div_id_desktop = filename.split(sep=".")[0] + "_desktop"
|
|
1169
1433
|
div_id_mobile = filename.split(sep=".")[0] + "_mobile"
|
|
1170
1434
|
|
|
1171
|
-
|
|
1435
|
+
figure.update_layout(height=None)
|
|
1436
|
+
|
|
1437
|
+
html_desktop_raw = to_html(
|
|
1172
1438
|
fig=figure,
|
|
1173
1439
|
div_id=div_id_desktop,
|
|
1174
1440
|
auto_play=False,
|
|
1175
1441
|
full_html=False,
|
|
1176
|
-
include_plotlyjs=
|
|
1442
|
+
include_plotlyjs="cdn",
|
|
1177
1443
|
config=fig["config"],
|
|
1178
1444
|
)
|
|
1179
1445
|
|
|
@@ -1186,16 +1452,23 @@ def report_html(
|
|
|
1186
1452
|
config=fig["config"],
|
|
1187
1453
|
)
|
|
1188
1454
|
|
|
1455
|
+
script_pattern = r'<script src="https://cdn\.plot\.ly/plotly[^"]*"></script>'
|
|
1456
|
+
html_desktop_content = re.sub(script_pattern, "", html_desktop_raw)
|
|
1457
|
+
|
|
1189
1458
|
responsive_html = _generate_responsive_html_string(
|
|
1190
|
-
html_desktop=
|
|
1459
|
+
html_desktop=html_desktop_content,
|
|
1191
1460
|
html_mobile=html_mobile,
|
|
1192
1461
|
div_id_desktop=div_id_desktop,
|
|
1193
1462
|
div_id_mobile=div_id_mobile,
|
|
1194
1463
|
table_html=table_html,
|
|
1195
1464
|
)
|
|
1196
1465
|
|
|
1466
|
+
full_html = _wrap_in_full_html(
|
|
1467
|
+
responsive_content=responsive_html,
|
|
1468
|
+
)
|
|
1469
|
+
|
|
1197
1470
|
with plotfile.open(mode="w", encoding="utf-8") as f:
|
|
1198
|
-
f.write(
|
|
1471
|
+
f.write(full_html)
|
|
1199
1472
|
|
|
1200
1473
|
if auto_open:
|
|
1201
1474
|
webbrowser.open(f"file://{plotfile.resolve()}")
|
|
@@ -1204,12 +1477,15 @@ def report_html(
|
|
|
1204
1477
|
else:
|
|
1205
1478
|
div_id = filename.split(sep=".")[0]
|
|
1206
1479
|
div_id_mobile = div_id + "_mobile"
|
|
1480
|
+
|
|
1481
|
+
figure.update_layout(height=None)
|
|
1482
|
+
|
|
1207
1483
|
html_desktop = to_html(
|
|
1208
1484
|
fig=figure,
|
|
1209
1485
|
div_id=div_id,
|
|
1210
1486
|
auto_play=False,
|
|
1211
1487
|
full_html=False,
|
|
1212
|
-
include_plotlyjs=
|
|
1488
|
+
include_plotlyjs="cdn",
|
|
1213
1489
|
config=fig["config"],
|
|
1214
1490
|
)
|
|
1215
1491
|
html_mobile = to_html(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "openseries"
|
|
3
|
-
version = "2.1.
|
|
3
|
+
version = "2.1.3"
|
|
4
4
|
description = "Tools for analyzing financial timeseries."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Martin Karrin", email = "martin.karrin@captor.se" },
|
|
@@ -67,18 +67,17 @@ pre-commit = ">=4.5.0"
|
|
|
67
67
|
pytest = ">=9.0.2"
|
|
68
68
|
pytest-cov = ">=7.0.0"
|
|
69
69
|
pytest-xdist = ">=3.8.0"
|
|
70
|
-
ruff = "0.14.
|
|
70
|
+
ruff = "0.14.9"
|
|
71
71
|
types-openpyxl = ">=3.1.2"
|
|
72
72
|
scipy-stubs = ">=1.14.1.0"
|
|
73
73
|
types-python-dateutil = ">=2.8.2"
|
|
74
74
|
types-requests = ">=2.20.0"
|
|
75
75
|
|
|
76
76
|
[tool.poetry.group.docs.dependencies]
|
|
77
|
-
|
|
78
|
-
sphinx = ">=8.2.3"
|
|
77
|
+
sphinx = ">=9.0.4"
|
|
79
78
|
sphinx-autobuild = ">=2025.8.25"
|
|
80
|
-
sphinx-autodoc-typehints = ">=3.
|
|
81
|
-
sphinx-rtd-theme = ">=3.
|
|
79
|
+
sphinx-autodoc-typehints = ">=3.6.0"
|
|
80
|
+
sphinx-rtd-theme = ">=3.1.0rc1"
|
|
82
81
|
|
|
83
82
|
[build-system]
|
|
84
83
|
requires = ["poetry-core>=2.2.1"]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|