tts-html-utils 0.5.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. tts_html_utils/__init__.py +0 -0
  2. tts_html_utils/core/__init__.py +0 -0
  3. tts_html_utils/core/compiler.py +215 -0
  4. tts_html_utils/core/components/__init__.py +6 -0
  5. tts_html_utils/core/components/base.py +300 -0
  6. tts_html_utils/core/components/favicon.py +17 -0
  7. tts_html_utils/core/components/flexbox.py +133 -0
  8. tts_html_utils/core/components/list.py +168 -0
  9. tts_html_utils/core/components/misc.py +237 -0
  10. tts_html_utils/core/components/navigation.py +55 -0
  11. tts_html_utils/core/components/plot.py +166 -0
  12. tts_html_utils/core/components/structure.py +159 -0
  13. tts_html_utils/core/components/table.py +392 -0
  14. tts_html_utils/core/components/text.py +105 -0
  15. tts_html_utils/css/gantt_chart/gantt_chart.css +330 -0
  16. tts_html_utils/css/pane_container/pane_container.css +8 -0
  17. tts_html_utils/django/__init__.py +0 -0
  18. tts_html_utils/django/forms/__init__.py +0 -0
  19. tts_html_utils/django/forms/time_range_form.py +56 -0
  20. tts_html_utils/django/tables/__init__.py +0 -0
  21. tts_html_utils/django/tables/filter_table.py +201 -0
  22. tts_html_utils/django/templates/filter_table/filter_table.html +39 -0
  23. tts_html_utils/django/templates/filter_table/filter_table_rows.html +22 -0
  24. tts_html_utils/django/templates/filter_table/javascript.html +108 -0
  25. tts_html_utils/django/templates/filter_table/pagination.html +18 -0
  26. tts_html_utils/gantt_chart/gantt_chart.py +167 -0
  27. tts_html_utils/javascript/filter_table/django_html_filter_table.js +85 -0
  28. tts_html_utils/javascript/filter_table/django_html_table_sort.js +78 -0
  29. tts_html_utils/javascript/filter_table/show_hide_details.js +24 -0
  30. tts_html_utils/javascript/filter_table/static_html_filter_table.js +72 -0
  31. tts_html_utils/javascript/filter_table/static_html_table_sort.js +122 -0
  32. tts_html_utils/javascript/gantt_chart/gantt_chart.js +908 -0
  33. tts_html_utils/javascript/pane_container/pane_container.js +141 -0
  34. tts_html_utils/javascript/pane_container.js +43 -0
  35. tts_html_utils/javascript/plotlyv1.58.5.js +62 -0
  36. tts_html_utils/javascript/visdiff_tables/hide_columns_without_mismatch.js +35 -0
  37. tts_html_utils/javascript/visdiff_tables/row_height_match.js +55 -0
  38. tts_html_utils/javascript/visdiff_tables/scroll_sync.js +29 -0
  39. tts_html_utils/jupyter/__init__.py +0 -0
  40. tts_html_utils/jupyter/grid.py +465 -0
  41. tts_html_utils/resource.py +225 -0
  42. tts_html_utils/resources/css/navbar.css +39 -0
  43. tts_html_utils/resources/html_templates/full_report.html +56 -0
  44. tts_html_utils/resources/html_templates/generic.html +2 -0
  45. tts_html_utils/resources/html_templates/generic_singleton.html +2 -0
  46. tts_html_utils/resources/html_templates/helpers.html +31 -0
  47. tts_html_utils/resources/html_templates/plotly_config.html +6 -0
  48. tts_html_utils/resources/html_templates/script_general.html +3 -0
  49. tts_html_utils/resources/html_templates/table.html +21 -0
  50. tts_html_utils/resources/html_templates/test.html +4 -0
  51. tts_html_utils/resources/img/favicon.png +0 -0
  52. tts_html_utils/resources/report_base.css +474 -0
  53. tts_html_utils/resources/rule_result_table.css +40 -0
  54. tts_html_utils/resources/rule_summary.css +79 -0
  55. tts_html_utils/resources/scripts/collapsible.js +40 -0
  56. tts_html_utils/resources/scripts/link_functions.js +13 -0
  57. tts_html_utils/resources/scripts/open_tab_function.js +29 -0
  58. tts_html_utils/resources/scripts/resize_bodyheight.js +7 -0
  59. tts_html_utils/resources/scripts/sorttable.js +489 -0
  60. tts_html_utils/resources/scripts/sticky_headers.js +11 -0
  61. tts_html_utils/test/__init__.py +0 -0
  62. tts_html_utils/test/test_base.py +110 -0
  63. tts_html_utils/test/test_compiler.py +227 -0
  64. tts_html_utils/test/test_list.py +127 -0
  65. tts_html_utils/test/test_misc.py +145 -0
  66. tts_html_utils/test/test_navigation.py +59 -0
  67. tts_html_utils/test/test_plot.py +150 -0
  68. tts_html_utils/test/test_resource.py +133 -0
  69. tts_html_utils/test/test_structure.py +176 -0
  70. tts_html_utils/test/test_table.py +256 -0
  71. tts_html_utils/test/test_text.py +62 -0
  72. tts_html_utils/visdiff/visdiff.py +98 -0
  73. tts_html_utils-0.5.4.dist-info/METADATA +72 -0
  74. tts_html_utils-0.5.4.dist-info/RECORD +76 -0
  75. tts_html_utils-0.5.4.dist-info/WHEEL +5 -0
  76. tts_html_utils-0.5.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,168 @@
1
+ #Standard Library Imports
2
+ import pdb
3
+
4
+ #Installed dependency imports
5
+ #None yet
6
+
7
+ #Teamtools Studio Imports
8
+ from tts_utilities.logger import create_logger
9
+
10
+ #This Library Imports
11
+ from tts_html_utils.core.components.base import HtmlComponent, HtmlComponentSimple
12
+
13
+ logger = create_logger(name='html_utils.core.components.base')
14
+
15
+ class UnorderedList(HtmlComponentSimple):
16
+ """
17
+ A standard bulleted list (<ul>).
18
+
19
+ **Concept:**
20
+ Used for items where the order does not matter. Browsers typically
21
+ render these with bullet points. This is also a common structural
22
+ element that is used without looking like a literal list.
23
+
24
+ **Alias:** `UL`
25
+ """
26
+ TAG = 'ul'
27
+
28
+ UL = UnorderedList
29
+
30
+ class OrderedList(HtmlComponentSimple):
31
+ """
32
+ A numbered list (<ol>).
33
+
34
+ **Concept:**
35
+ Used for items where the sequence is important. Browsers typically render
36
+ these with numbers (1, 2, 3...) or letters (a, b, c...). Much less common
37
+ than an unordered list.
38
+
39
+
40
+ **Alias:** `OL`
41
+ """
42
+ TAG = 'ol'
43
+ OL = OrderedList
44
+
45
+ class ListItem(HtmlComponentSimple):
46
+ """
47
+ A single row within a list (<li>).
48
+
49
+ **Concept:**
50
+ Neither `<ul>` nor `<ol>` can directly contain text. They can only contain `<li>` tags.
51
+ This component wraps the actual content of the list entry.
52
+
53
+ **Alias:** `LI`
54
+ """
55
+ TAG = 'li'
56
+ LI = ListItem
57
+
58
+ class PowerList(HtmlComponent):
59
+ """
60
+ A smart builder for creating complex, nested lists without the headache.
61
+
62
+ **The Problem:**
63
+ Creating nested HTML lists is very repetative. You have to remember to open a `<ul>`,
64
+ add an `<li>`, open a new `<ul>` *inside* that `<li>` for the sub-items, and then
65
+ close everything in the exact reverse order.
66
+
67
+ **The Solution:**
68
+ `PowerList` works like a text editor's outline mode. You simply add a line, and
69
+ command it to "indent" (`up_level`) or "outdent" (`down_level`). It tracks
70
+ where you are in the hierarchy and handles all the tag nesting for you automatically.
71
+
72
+ :param children: Initial list items to populate. (Usually better to use `.line()` after init).
73
+ :type children: List, optional
74
+ :param class_name: CSS classes for the root list element.
75
+ :type class_name: str or list of str, optional
76
+ :param extra_class_name: Additional CSS classes to append.
77
+ :type extra_class_name: str or list of str, optional
78
+ :param style: Inline CSS styles for the root list element.
79
+ :type style: dict, optional
80
+ :param ordered: If True, renders as a numbered list (`<ol>`). If False, renders as bullets (`<ul>`). Defaults to False.
81
+ :type ordered: bool
82
+ """
83
+ def __init__(self, children=None, class_name=None, extra_class_name=None, style=None, ordered=False):
84
+ super().__init__(children=children, class_name=class_name, extra_class_name=extra_class_name, style=style)
85
+ self.ordered = ordered
86
+
87
+ # 'target' acts as a cursor. It points to the specific list (root or nested)
88
+ # where the next item should be added.
89
+ self.target = [self]
90
+
91
+ def line(self, line, class_name=None, extra_class_name=None, style=None):
92
+ """
93
+ Appends a new item to the current level of the list.
94
+
95
+ :param line: The content of the item (text or another HtmlComponent).
96
+ :type line: str or HtmlComponent
97
+ :param class_name: CSS classes for this specific list item (`<li>`).
98
+ :type class_name: str or list of str, optional
99
+ :param extra_class_name: Additional CSS classes to append.
100
+ :type extra_class_name: str or list of str, optional
101
+ :param style: Inline CSS styles for this specific list item.
102
+ :type style: dict, optional
103
+ """
104
+ if not isinstance(line, ListItem):
105
+ line = ListItem(children=line, class_name=class_name, extra_class_name=extra_class_name, style=style)
106
+
107
+ self.target[-1].add_child(line)
108
+
109
+
110
+ def up_level(self, label=None, new_list=None, class_name=None, extra_class_name=None, style=None, ordered=None):
111
+ """
112
+ Indents the list (creates a sub-list).
113
+
114
+ **How it works:**
115
+ This creates a new list (container) and nests it inside the *last item* you added.
116
+ The internal "cursor" moves to this new list, so subsequent `.line()` calls
117
+ will appear as sub-items.
118
+
119
+ :param label: Optional text to place on the parent item *before* the sub-list starts.
120
+ :type label: str or HtmlComponent, optional
121
+ :param new_list: If you have already built a `UL` or `OL` object, you can pass it here to insert it.
122
+ :type new_list: UL or OL, optional
123
+ :param class_name: CSS classes for the new sub-list container.
124
+ :type class_name: str or list of str, optional
125
+ :param extra_class_name: Additional CSS classes to append.
126
+ :type extra_class_name: str or list of str, optional
127
+ :param style: Inline CSS styles for the new sub-list container.
128
+ :type style: dict, optional
129
+ :param ordered: If True, the sub-list will be numbered. If False, bulleted. Defaults to the parent list's type.
130
+ :type ordered: bool, optional
131
+ """
132
+
133
+ if not new_list:
134
+ if ordered is None:
135
+ ordered = self.ordered
136
+
137
+ # FIX: Pass the styling arguments to the new PowerList
138
+ new_list = PowerList(
139
+ ordered=ordered,
140
+ class_name=class_name,
141
+ extra_class_name=extra_class_name,
142
+ style=style
143
+ )
144
+
145
+ if label is not None:
146
+ self.target[-1].add_child(ListItem(children=[label, new_list]))
147
+ else:
148
+ self.target[-1].add_child(ListItem(children=new_list))
149
+ self.target.append(new_list)
150
+
151
+ def down_level(self):
152
+ """
153
+ Outdents the list (returns to the parent level).
154
+
155
+ **How it works:**
156
+ Moves the internal "cursor" back up to the parent list.
157
+ """
158
+ if len(self.target) <= 1:
159
+ return # Do nothing if we're at the root level already
160
+ self.target.pop()
161
+
162
+ def render(self):
163
+ """
164
+ Converts the internal PowerList structure into standard HTML components
165
+ (`UL`/`OL` and `LI`) and renders them to a string.
166
+ """
167
+ top_list = OrderedList if self.ordered else UnorderedList
168
+ return top_list(children=self.children, class_name=self.class_name, style=self.style).rendered
@@ -0,0 +1,237 @@
1
+ #Standard Library Imports
2
+ import pdb
3
+
4
+ #Installed dependency imports
5
+ #None yet
6
+
7
+ #Teamtools Studio Imports
8
+ from tts_utilities.logger import create_logger
9
+
10
+ #This Library Imports
11
+ from tts_html_utils.core.components.base import HtmlComponentSimple, HtmlComponentSingleSimple
12
+ from tts_html_utils.core.components.favicon import favicon_data
13
+
14
+ logger = create_logger(name='html_utils.core.components.base')
15
+
16
+ class HTML(HtmlComponentSimple):
17
+ """
18
+ The root container for the entire document.
19
+
20
+ **Concept:**
21
+ This is the outermost shell of a webpage. Every other element (Head, Body, etc.)
22
+ lives inside this tag.
23
+
24
+ **Usage Note:**
25
+ You rarely need to instantiate this yourself. The `HtmlCompiler` typically
26
+ creates this automatically when you build a page.
27
+ """
28
+ TAG = 'html'
29
+
30
+ class Head(HtmlComponentSimple):
31
+ """
32
+ The "Settings" section of the document.
33
+
34
+ **Concept:**
35
+ The `<head>` contains metadata—information *about* the page rather than content
36
+ *on* the page. It holds the title, CSS links, scripts, and browser instructions.
37
+ Nothing put here is displayed in the main browser window.
38
+
39
+ **Usage Note:**
40
+ Like `HTML`, this is usually managed automatically by the `HtmlCompiler`.
41
+ """
42
+ TAG = 'head'
43
+
44
+ class Body(HtmlComponentSimple):
45
+ """
46
+ The "Canvas" of the document.
47
+
48
+ **Concept:**
49
+ The `<body>` contains everything the user actually sees: text, images, buttons,
50
+ and tables. If you are building a component to display data, it eventually
51
+ lives inside here.
52
+
53
+ **Usage Note:**
54
+ Managed automatically by the `HtmlCompiler`. You typically just add children
55
+ to the compiler, and it places them in the Body for you.
56
+ """
57
+ TAG = 'body'
58
+
59
+ class Header(HtmlComponentSimple):
60
+ """
61
+ A semantic container for introductory content.
62
+
63
+ **Concept:**
64
+ Not to be confused with `<head>`. The `<header>` is a visible section of the page,
65
+ usually containing the logo, navigation bar, and page title. It helps screen readers
66
+ and search engines understand the structure of your content.
67
+ """
68
+ TAG = 'header'
69
+
70
+ class Footer(HtmlComponentSimple):
71
+ """
72
+ A semantic container for the bottom of a page or section.
73
+
74
+ **Concept:**
75
+ Usually contains copyright notices, contact info, or links to privacy policies.
76
+ Like `<header>`, this is a "semantic" tag—it behaves exactly like a `Div`
77
+ visually, but it tells search engines "this is the end of the content."
78
+ """
79
+ TAG = 'footer'
80
+
81
+ class Style(HtmlComponentSimple):
82
+ """
83
+ A container for raw CSS code.
84
+
85
+ **Concept:**
86
+ Allows you to write CSS rules directly within the HTML file rather than linking
87
+ to an external `.css` file.
88
+
89
+ **Usage Note:**
90
+ The `HtmlCompiler` uses this to inject CSS dependencies. You generally shouldn't
91
+ need to use this manually; prefer creating `.css` files and linking them.
92
+ """
93
+ TAG = 'style'
94
+
95
+ class Script(HtmlComponentSimple):
96
+ """
97
+ A container for raw JavaScript code.
98
+
99
+ **Concept:**
100
+ Allows you to write JavaScript logic directly within the HTML file.
101
+
102
+ **Usage Note:**
103
+ The `HtmlCompiler` uses this to inject JS dependencies. For complex logic,
104
+ prefer creating `.js` files and letting the compiler link them.
105
+ """
106
+ TAG = 'script'
107
+
108
+ class Title(HtmlComponentSimple):
109
+ """
110
+ The browser tab name.
111
+
112
+ **Concept:**
113
+ Defines the text that appears in the browser tab, bookmarks, and search engine results.
114
+ It does not appear in the page content itself.
115
+ """
116
+ TAG = 'title'
117
+
118
+ class Div(HtmlComponentSimple):
119
+ """
120
+ The generic "Lego Brick" of the web.
121
+
122
+ **Concept:**
123
+ The `<div>` (Division) is a generic container used to group other elements together
124
+ for styling or layout purposes. It has no semantic meaning (unlike `<header>` or `<footer>`).
125
+
126
+ Most modern web layouts are essentially nested structures of Divs.
127
+ """
128
+ TAG = 'div'
129
+
130
+ class TtsFavicon(Script):
131
+ """
132
+ Injects the Teamtools Studio logo into the browser tab.
133
+
134
+ **How it works:**
135
+ Instead of requiring you to manage a physical `.ico` or `.png` file on the server,
136
+ this class contains the base64-encoded image data of the logo. It uses a small
137
+ JavaScript snippet to create the link tag dynamically and attach it to the document head.
138
+ """
139
+ def __init__(self):
140
+ super().__init__()
141
+ self.js_includes = [('\n'.join([
142
+ "const favicon = document.createElement('link');",
143
+ "favicon.rel = 'icon';",
144
+ "favicon.type = 'image/png';",
145
+ f"favicon.href = '{favicon_data}';",
146
+ "document.head.appendChild(favicon);",
147
+ ]), {})]
148
+
149
+
150
+ class HorizontalBreak(HtmlComponentSingleSimple):
151
+ """
152
+ A thematic break (Horizontal Rule).
153
+
154
+ **Concept:**
155
+ Renders a horizontal line across the container. Used to visually separate
156
+ different topics or sections of content.
157
+
158
+ **Alias:** `HR`
159
+ """
160
+
161
+ TAG = 'hr'
162
+ DEFAULT_CLASS=['faded']
163
+
164
+ HR = HorizontalBreak
165
+
166
+ class LineBreak(HtmlComponentSingleSimple):
167
+ """
168
+ A forced line break.
169
+
170
+ **Concept:**
171
+ Forces the text following it to start on a new line.
172
+
173
+ **Usage Note:**
174
+ Use sparingly. For spacing between paragraphs, it is usually better to use
175
+ CSS margins or separate `<p>` (Paragraph) tags rather than multiple `<br>` tags.
176
+
177
+ **Alias:** `BR`
178
+ """
179
+ TAG = 'br'
180
+
181
+ BR = LineBreak
182
+
183
+ class Link(HtmlComponentSimple):
184
+ """
185
+ A hyperlink (Anchor tag).
186
+
187
+ **Concept:**
188
+ The fundamental connector of the web. Links one page (or section) to another.
189
+
190
+ **Alias:** `A`
191
+
192
+ :param text: The clickable text the user sees.
193
+ :type text: str
194
+ :param href: The destination URL. If None, defaults to the `text` (useful if pasting raw URLs).
195
+ :type href: str, optional
196
+ :param new_tab: If True, clicking the link opens a new browser tab (`target="_blank"`).
197
+ If False, it navigates the current tab. Defaults to True.
198
+ :type new_tab: bool, optional
199
+ :param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
200
+ """
201
+
202
+ TAG='a'
203
+ def __init__(self, text, href=None, new_tab=True, **kwargs):
204
+ if href is None: href = text
205
+ attr = {'href': href, 'target': '_blank' if new_tab else '_self'}
206
+ if 'attr' in kwargs.keys():
207
+ attr = {**attr, **kwargs['attr']}
208
+ del kwargs['attr']
209
+ super().__init__(children=text, attr=attr, **kwargs)
210
+
211
+ A = Link
212
+
213
+ class Button(HtmlComponentSimple):
214
+ """
215
+ A clickable button element.
216
+
217
+ **Concept:**
218
+ Used for actions (like "Submit", "Calculate", "Toggle"), as opposed to Links
219
+ which are used for navigation.
220
+
221
+ :param text: The label displayed on the button.
222
+ :type text: str
223
+ :param button_type: The HTML behavior type.
224
+ - 'button': A generic clickable button (requires JS to do anything).
225
+ - 'submit': Submits a parent form.
226
+ - 'reset': Resets a parent form.
227
+ :type button_type: str, optional
228
+ :param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
229
+ """
230
+
231
+ TAG='button'
232
+ def __init__(self, text, button_type='button', **kwargs):
233
+ attr = {'type': button_type}
234
+ if 'attr' in kwargs.keys():
235
+ attr = {**attr, **kwargs['attr']}
236
+ del kwargs['attr']
237
+ super().__init__(children=text, attr=attr, **kwargs)
@@ -0,0 +1,55 @@
1
+ #Standard Library Imports
2
+ from pathlib import Path
3
+ import pdb
4
+
5
+ #Installed dependency imports
6
+ #None yet
7
+
8
+ #Teamtools Studio Imports
9
+ from tts_utilities.logger import create_logger
10
+
11
+ #This Library Imports
12
+ from tts_html_utils.core.components.list import PowerList, ListItem
13
+ from tts_html_utils.core.components.misc import Link
14
+
15
+ logger = create_logger(name='html_utils.core.components.base')
16
+
17
+
18
+ class Navbar(PowerList):
19
+ """
20
+ Simple class to streamline creation of a navbar, which is essentially an unordered list
21
+ of links.
22
+
23
+ Nothing special now, but eventually this will be part of stock templates and will include out of the box CSS
24
+
25
+ :param mapping: Dictionary where keys are labels and values are URLs
26
+ :type style: dict
27
+
28
+ :param new_tab: Should links open to a new tab or in the current one?
29
+ :type new_tab: bool
30
+
31
+ :param kwargs: Additional keyword arguments passed to the `HtmlComponent` constructor.
32
+ """
33
+ def __init__(self, mapping, new_tab=False, **kwargs):
34
+ super().__init__(class_name='navbar', **kwargs)
35
+ self.add_items(mapping, new_tab=new_tab)
36
+ self.css_includes = [(Path(__file__).parent.parent.parent.joinpath('resources/css/navbar.css'),{})]
37
+
38
+ def add_items(self, mapping, new_tab=False):
39
+ """
40
+ Add an item to an existing Navbar. This is equivalent to including the item in the mapping parameter of __init__()
41
+
42
+ :param mapping: Dictionary where keys are labels and values are URLs
43
+ :type style: dict
44
+
45
+ :param new_tab: Should links open to a new tab or in the current one?
46
+ :type new_tab: bool
47
+ """
48
+ for label, href in mapping.items():
49
+ if isinstance(href, dict):
50
+ self.up_level(label=label)
51
+ self.add_items(href)
52
+ self.down_level()
53
+ else:
54
+ self.line(Link(label, href=href, new_tab=new_tab), class_name='navbartab')
55
+
@@ -0,0 +1,166 @@
1
+ #Standard Library Imports
2
+ import pdb
3
+ import uuid
4
+
5
+ #Installed dependency imports
6
+ import plotly.graph_objects as go
7
+ import plotly.express as px
8
+ import plotly.io as pio
9
+
10
+ #Teamtools Studio Imports
11
+ from tts_utilities.logger import create_logger
12
+
13
+ #This Library Imports
14
+ from tts_html_utils.core.components.base import HtmlComponent
15
+ from tts_html_utils.core.components.misc import Div
16
+
17
+ logger = create_logger(name='html_utils.core.components.base')
18
+
19
+ class PlotBase(HtmlComponent):
20
+ """
21
+ The base wrapper for embedding Plotly figures into the HTML document.
22
+
23
+ **Concept:**
24
+ Plotly produces interactive, JavaScript-based charts. To put them in an HTML file,
25
+ we need to convert the Python `Figure` object into a specific block of HTML/JS code.
26
+
27
+ **Important Note on Performance:**
28
+ This component renders with `include_plotlyjs=False`. The global `plotly.js` library
29
+ is automatically requested via the component's `js_includes` and managed by the
30
+ `HtmlCompiler` to ensure it is loaded once in the `<head>`.
31
+
32
+ :param fig: The existing Plotly figure to render. If None, creates a blank figure.
33
+ :type fig: plotly.graph_objects.Figure, optional
34
+ :param title: Sets the title of the plot layout.
35
+ :type title: str, optional
36
+ :param template: The visual theme to use (e.g., 'plotly_dark', 'seaborn', 'ggplot2').
37
+ Defaults to 'seaborn'.
38
+ :type template: str, optional
39
+ :param class_name: Primary CSS class for the container Div.
40
+ :type class_name: str, optional
41
+ :param extra_class_name: Additional CSS classes.
42
+ :type extra_class_name: str, optional
43
+ :param id: Optional. A custom ID for this container. If not provided, a unique ID will be generated.
44
+ :type id: str, optional
45
+ :param style: Dictionary of inline CSS styles for the container Div.
46
+ :type style: dict, optional
47
+ """
48
+ DEFAULT_PLOTLY_TEMPLATE = 'seaborn'
49
+ def __init__(self, fig=None, title=None, template=None, class_name=None, extra_class_name=None, id=None, style=None, include_plotlyjs=False, full_html=False):
50
+ self.fig = fig or go.Figure()
51
+ self.fig.update_layout(template=(template or self.DEFAULT_PLOTLY_TEMPLATE))
52
+ self.include_plotlyjs = include_plotlyjs
53
+ self.full_html = full_html
54
+
55
+ if title:
56
+ self.fig.update_layout(title=title)
57
+
58
+ # Ensure every plot has a unique ID for JS scoping if one isn't provided
59
+ if id is None:
60
+ id = f"plotly-{uuid.uuid4().hex[:8]}"
61
+
62
+ super().__init__(children=None, class_name=class_name, extra_class_name=extra_class_name, id=id, style=style)
63
+
64
+ # Automatically include the Plotly global library via CDN
65
+ # The HtmlCompiler will deduplicate this if multiple plots are present.
66
+ self.js_includes.append(("https://cdn.plot.ly/plotly-3.3.1.min.js", {}))
67
+
68
+ def render(self):
69
+ """
70
+ Converts the Python Plotly figure into an HTML Div string.
71
+ """
72
+ # Generate the Plotly HTML snippet.
73
+ # This already includes a <div id="self.id"> and a <script>.
74
+ plotly_html = pio.to_html(
75
+ self.fig,
76
+ include_plotlyjs=self.include_plotlyjs,
77
+ full_html=self.full_html,
78
+ div_id=self.id
79
+ )
80
+
81
+ return plotly_html
82
+
83
+
84
+ class ScatterPlot(PlotBase):
85
+ """
86
+ A helper for creating basic line/scatter plots.
87
+
88
+ **Legacy Note:** This is a legacy wrapper. For complex visualization needs, it is generally
89
+ recommended to use `dtat-lib` or build `go.Figure` objects directly and pass
90
+ them to `PlotBase`.
91
+ """
92
+ def add_trace(self, y, x=None, name=None, lines=False, markers=True, trace_kwargs=None, scatter_kwargs=None):
93
+ """
94
+ Adds a new data series (trace) to the plot.
95
+
96
+ :param y: The Y-axis data points.
97
+ :type y: list or array-like
98
+ :param x: The X-axis data points.
99
+ :type x: list or array-like, optional
100
+ :param name: The name of this series (appears in the legend).
101
+ :type name: str, optional
102
+ :param lines: If True, connects points with lines.
103
+ :type lines: bool, default False
104
+ :param markers: If True, draws a dot at each data point.
105
+ :type markers: bool, default True
106
+ :param trace_kwargs: A passthrough dictionary of arguments sent to `self.fig.add_trace`.
107
+ Useful for advanced layout options like `secondary_y=True`.
108
+ :type trace_kwargs: dict, optional
109
+ :param scatter_kwargs: A passthrough dictionary of arguments sent to `go.Scatter`.
110
+ Useful for styling options like `line_color`, `marker_size`, etc.
111
+ :type scatter_kwargs: dict, optional
112
+ """
113
+ modes = []
114
+ if lines:
115
+ modes.append('lines')
116
+ if markers:
117
+ modes.append('markers')
118
+
119
+ trace_kwargs = trace_kwargs or {}
120
+ scatter_kwargs = scatter_kwargs or {}
121
+
122
+ self.fig.add_trace(
123
+ go.Scatter(
124
+ x=x,
125
+ y=y,
126
+ mode='+'.join(modes),
127
+ name=name,
128
+ **scatter_kwargs
129
+ ),
130
+ **trace_kwargs
131
+ )
132
+
133
+ class TimelinePlot(PlotBase):
134
+ """
135
+ A helper for creating Gantt-style timeline charts.
136
+
137
+ **Legacy Note:**
138
+ This is a legacy wrapper. Consider using `dtat-lib` for modern timeline visualization.
139
+ """
140
+ def __init__(self, data, x_start_name, x_end_name, y_name, color=None, title=None, date_format_str=None):
141
+ """
142
+ Initialize the timeline using `plotly.express.timeline`.
143
+
144
+ :param data: The dataset (usually a Pandas DataFrame).
145
+ :type data: pandas.DataFrame or list of dicts
146
+ :param x_start_name: The column name containing start timestamps.
147
+ :type x_start_name: str
148
+ :param x_end_name: The column name containing end timestamps.
149
+ :type x_end_name: str
150
+ :param y_name: The column name to use for the Y-axis labels (tasks/rows).
151
+ :type y_name: str
152
+ :param color: The column name to use for coloring the bars.
153
+ :type color: str, optional
154
+ :param title: The chart title.
155
+ :type title: str, optional
156
+ :param date_format_str: A d3-formatting string to control how dates appear on the X-axis
157
+ (e.g., '%Y-%m-%d').
158
+ :type date_format_str: str, optional
159
+ """
160
+ timeline = px.timeline(data, x_start=x_start_name, x_end=x_end_name, y=y_name, color=color)
161
+
162
+ super().__init__(fig=timeline, title=title)
163
+
164
+ # optionally update the formatting of the x-axis ticks (which are datetime compliant for all timeline plots)
165
+ if date_format_str:
166
+ self.fig.update_layout(xaxis_tickformat = date_format_str)