meerschaum 3.0.0rc3__py3-none-any.whl → 3.0.0rc7__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 (126) hide show
  1. meerschaum/_internal/arguments/_parser.py +14 -2
  2. meerschaum/_internal/cli/__init__.py +6 -0
  3. meerschaum/_internal/cli/daemons.py +103 -0
  4. meerschaum/_internal/cli/entry.py +220 -0
  5. meerschaum/_internal/cli/workers.py +434 -0
  6. meerschaum/_internal/docs/index.py +1 -2
  7. meerschaum/_internal/entry.py +44 -8
  8. meerschaum/_internal/shell/Shell.py +113 -19
  9. meerschaum/_internal/shell/__init__.py +4 -1
  10. meerschaum/_internal/static.py +3 -1
  11. meerschaum/_internal/term/TermPageHandler.py +1 -2
  12. meerschaum/_internal/term/__init__.py +40 -6
  13. meerschaum/_internal/term/tools.py +33 -8
  14. meerschaum/actions/__init__.py +6 -4
  15. meerschaum/actions/api.py +39 -11
  16. meerschaum/actions/attach.py +1 -0
  17. meerschaum/actions/delete.py +4 -2
  18. meerschaum/actions/edit.py +27 -8
  19. meerschaum/actions/login.py +8 -8
  20. meerschaum/actions/register.py +13 -7
  21. meerschaum/actions/reload.py +22 -5
  22. meerschaum/actions/restart.py +14 -0
  23. meerschaum/actions/show.py +69 -4
  24. meerschaum/actions/start.py +135 -14
  25. meerschaum/actions/stop.py +36 -3
  26. meerschaum/actions/sync.py +6 -1
  27. meerschaum/api/__init__.py +35 -13
  28. meerschaum/api/_events.py +7 -2
  29. meerschaum/api/_oauth2.py +47 -4
  30. meerschaum/api/dash/callbacks/dashboard.py +103 -97
  31. meerschaum/api/dash/callbacks/jobs.py +3 -2
  32. meerschaum/api/dash/callbacks/login.py +10 -1
  33. meerschaum/api/dash/callbacks/pipes.py +136 -57
  34. meerschaum/api/dash/callbacks/register.py +9 -2
  35. meerschaum/api/dash/callbacks/tokens.py +2 -1
  36. meerschaum/api/dash/components.py +6 -7
  37. meerschaum/api/dash/keys.py +17 -1
  38. meerschaum/api/dash/pages/login.py +2 -2
  39. meerschaum/api/dash/pages/pipes.py +14 -4
  40. meerschaum/api/dash/pipes.py +186 -65
  41. meerschaum/api/dash/tokens.py +1 -1
  42. meerschaum/api/dash/webterm.py +14 -6
  43. meerschaum/api/models/_pipes.py +7 -1
  44. meerschaum/api/resources/static/js/terminado.js +3 -0
  45. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  46. meerschaum/api/resources/templates/termpage.html +1 -0
  47. meerschaum/api/routes/_jobs.py +23 -11
  48. meerschaum/api/routes/_login.py +73 -5
  49. meerschaum/api/routes/_pipes.py +6 -4
  50. meerschaum/api/routes/_webterm.py +3 -3
  51. meerschaum/config/__init__.py +60 -13
  52. meerschaum/config/_default.py +89 -61
  53. meerschaum/config/_edit.py +10 -8
  54. meerschaum/config/_formatting.py +2 -0
  55. meerschaum/config/_patch.py +4 -2
  56. meerschaum/config/_paths.py +127 -12
  57. meerschaum/config/_read_config.py +20 -10
  58. meerschaum/config/_version.py +1 -1
  59. meerschaum/config/environment.py +262 -0
  60. meerschaum/config/stack/__init__.py +7 -5
  61. meerschaum/connectors/_Connector.py +1 -2
  62. meerschaum/connectors/__init__.py +37 -2
  63. meerschaum/connectors/api/_APIConnector.py +1 -1
  64. meerschaum/connectors/api/_jobs.py +11 -0
  65. meerschaum/connectors/api/_pipes.py +7 -1
  66. meerschaum/connectors/instance/_plugins.py +9 -1
  67. meerschaum/connectors/instance/_tokens.py +20 -3
  68. meerschaum/connectors/instance/_users.py +8 -1
  69. meerschaum/connectors/parse.py +1 -1
  70. meerschaum/connectors/sql/_create_engine.py +3 -0
  71. meerschaum/connectors/sql/_pipes.py +98 -79
  72. meerschaum/connectors/sql/_users.py +8 -1
  73. meerschaum/connectors/sql/tables/__init__.py +20 -3
  74. meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
  75. meerschaum/connectors/valkey/_pipes.py +7 -5
  76. meerschaum/core/Pipe/__init__.py +62 -72
  77. meerschaum/core/Pipe/_attributes.py +66 -90
  78. meerschaum/core/Pipe/_cache.py +555 -0
  79. meerschaum/core/Pipe/_clear.py +0 -11
  80. meerschaum/core/Pipe/_data.py +0 -50
  81. meerschaum/core/Pipe/_deduplicate.py +0 -13
  82. meerschaum/core/Pipe/_delete.py +12 -21
  83. meerschaum/core/Pipe/_drop.py +11 -23
  84. meerschaum/core/Pipe/_dtypes.py +1 -1
  85. meerschaum/core/Pipe/_index.py +8 -14
  86. meerschaum/core/Pipe/_sync.py +12 -18
  87. meerschaum/core/Plugin/_Plugin.py +7 -1
  88. meerschaum/core/Token/_Token.py +1 -1
  89. meerschaum/core/User/_User.py +1 -2
  90. meerschaum/jobs/_Executor.py +88 -4
  91. meerschaum/jobs/_Job.py +135 -35
  92. meerschaum/jobs/systemd.py +7 -2
  93. meerschaum/plugins/__init__.py +277 -81
  94. meerschaum/utils/_get_pipes.py +30 -4
  95. meerschaum/utils/daemon/Daemon.py +195 -41
  96. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  97. meerschaum/utils/daemon/RotatingFile.py +63 -36
  98. meerschaum/utils/daemon/StdinFile.py +53 -13
  99. meerschaum/utils/daemon/__init__.py +18 -5
  100. meerschaum/utils/daemon/_names.py +6 -3
  101. meerschaum/utils/debug.py +34 -4
  102. meerschaum/utils/dtypes/__init__.py +5 -1
  103. meerschaum/utils/formatting/__init__.py +4 -1
  104. meerschaum/utils/formatting/_jobs.py +1 -1
  105. meerschaum/utils/formatting/_pipes.py +47 -46
  106. meerschaum/utils/formatting/_pprint.py +1 -0
  107. meerschaum/utils/formatting/_shell.py +16 -6
  108. meerschaum/utils/misc.py +18 -38
  109. meerschaum/utils/packages/__init__.py +15 -13
  110. meerschaum/utils/packages/_packages.py +1 -0
  111. meerschaum/utils/pipes.py +39 -7
  112. meerschaum/utils/process.py +1 -1
  113. meerschaum/utils/prompt.py +171 -144
  114. meerschaum/utils/sql.py +12 -2
  115. meerschaum/utils/threading.py +42 -0
  116. meerschaum/utils/venv/__init__.py +2 -0
  117. meerschaum/utils/warnings.py +19 -13
  118. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/METADATA +3 -1
  119. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/RECORD +125 -119
  120. meerschaum/config/_environment.py +0 -145
  121. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/WHEEL +0 -0
  122. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/entry_points.txt +0 -0
  123. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/LICENSE +0 -0
  124. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/NOTICE +0 -0
  125. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/top_level.txt +0 -0
  126. {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/zip-safe +0 -0
@@ -5,28 +5,35 @@
5
5
  Define callbacks for the `/dash/pipes/` page.
6
6
  """
7
7
 
8
- from urllib.parse import parse_qs
8
+ from urllib.parse import parse_qs, quote_plus
9
+ from typing import List, Optional, Dict, Any
9
10
 
11
+ import dash
10
12
  from dash.dependencies import Input, Output, State
11
13
  from dash import no_update
12
14
  from dash.exceptions import PreventUpdate
15
+ import dash_bootstrap_components as dbc
13
16
 
14
17
  import meerschaum as mrsm
15
18
  from meerschaum.api.dash import dash_app
16
- from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
19
+ from meerschaum.api.dash.components import (
20
+ alert_from_success_tuple,
21
+ build_cards_grid,
22
+ )
17
23
  from meerschaum.api.dash.pipes import (
18
24
  build_pipe_card,
19
25
  build_pipes_dropdown_keys_row,
20
26
  build_pipes_tags_dropdown,
27
+ build_pipes_navbar,
21
28
  )
22
29
  from meerschaum.api import CHECK_UPDATE, get_api_connector
23
30
  from meerschaum.utils.packages import import_html, import_dcc
24
31
  from meerschaum.api.dash.sessions import is_session_authenticated
25
- from meerschaum.utils.typing import Optional, Dict, Any
26
32
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
27
33
 
28
34
 
29
35
  @dash_app.callback(
36
+ Output('pipes-navbar-div', 'children'),
30
37
  Output('pipe-output-div', 'children'),
31
38
  Input('pipes-location', 'pathname'),
32
39
  State('pipes-location', 'search'),
@@ -38,7 +45,7 @@ def render_pipe_page_from_url(
38
45
  session_data: Optional[Dict[str, Any]],
39
46
  ):
40
47
  if not str(pathname).startswith('/dash/pipes'):
41
- return no_update
48
+ raise PreventUpdate
42
49
 
43
50
  session_id = (session_data or {}).get('session-id', None)
44
51
  authenticated = is_session_authenticated(str(session_id))
@@ -60,63 +67,103 @@ def render_pipe_page_from_url(
60
67
  if isinstance(location_keys, str):
61
68
  location_keys = location_keys.split(',')
62
69
 
70
+ keys = pathname.replace('/dash/pipes', '').lstrip('/').rstrip('/').split('/')
63
71
  instance_connector = mrsm.get_connector(instance)
72
+ viewing_single_pipe = len(keys) in (2, 3)
64
73
  if instance_connector is None:
65
- return [
66
- html.Br(),
67
- alert_from_success_tuple((False, f"Invalid instance keys '{instance}'.")),
68
- html.Br(),
69
- ]
70
-
71
- keys = pathname.replace('/dash/pipes', '').lstrip('/').rstrip('/').split('/')
72
- if len(keys) not in (2, 3):
73
- pipes = mrsm.get_pipes(
74
- as_list=True,
75
- connector_keys=connector_keys,
76
- metric_keys=metric_keys,
77
- location_keys=location_keys,
78
- tags=tags,
79
- instance=instance_connector,
74
+ return (
75
+ build_pipes_navbar(instance, with_instance_select=(not viewing_single_pipe)),
76
+ [
77
+ html.Br(),
78
+ alert_from_success_tuple((False, f"Invalid instance keys '{instance}'.")),
79
+ html.Br(),
80
+ ]
80
81
  )
82
+
83
+ if not viewing_single_pipe:
84
+ try:
85
+ pipes = mrsm.get_pipes(
86
+ as_list=True,
87
+ connector_keys=connector_keys,
88
+ metric_keys=metric_keys,
89
+ location_keys=location_keys,
90
+ tags=tags,
91
+ instance=instance_connector,
92
+ )
93
+ except Exception as e:
94
+ return (
95
+ build_pipes_navbar(instance, with_instance_select=False),
96
+ [
97
+ html.Br(),
98
+ alert_from_success_tuple(
99
+ (False, f"Failed to get pipes for instance '{instance}':\n{e}")
100
+ ),
101
+ html.Br(),
102
+ dbc.Row(
103
+ [
104
+ dbc.Button(
105
+ "Reload",
106
+ id='pipes-reload-button',
107
+ size='lg',
108
+ href=(
109
+ "/dash/pipes"
110
+ if pathname.startswith('/dash/pipes/')
111
+ else "/dash/pipes/"
112
+ )
113
+ ),
114
+ ],
115
+ justify='center',
116
+ align='center',
117
+ className='h-50',
118
+ ),
119
+ ]
120
+ )
121
+
81
122
  cards = [
82
123
  build_pipe_card(pipe, authenticated=authenticated, include_manage=False)
83
124
  for pipe in pipes
84
125
  ]
85
- return [
86
- html.Div([
126
+ return (
127
+ build_pipes_navbar(instance, with_instance_select=True),
128
+ [
129
+ html.Div([
130
+ html.Br(),
131
+ build_pipes_dropdown_keys_row(
132
+ connector_keys,
133
+ metric_keys,
134
+ location_keys,
135
+ tags,
136
+ pipes,
137
+ instance_connector,
138
+ ),
139
+ html.Br(),
140
+ build_pipes_tags_dropdown(
141
+ connector_keys,
142
+ metric_keys,
143
+ location_keys,
144
+ tags,
145
+ instance,
146
+ ),
147
+ ]),
87
148
  html.Br(),
88
- build_pipes_dropdown_keys_row(
89
- connector_keys,
90
- metric_keys,
91
- location_keys,
92
- tags,
93
- pipes,
94
- instance_connector,
95
- ),
149
+ build_cards_grid(cards, 1),
96
150
  html.Br(),
97
- build_pipes_tags_dropdown(
98
- connector_keys,
99
- metric_keys,
100
- location_keys,
101
- tags,
102
- instance,
103
- ),
104
- ]),
105
- html.Br(),
106
- build_cards_grid(cards, 1),
107
- html.Br(),
108
- ]
151
+ ]
152
+ )
109
153
 
110
154
  ck = keys[0]
111
155
  mk = keys[1]
112
156
  lk = keys[2] if len(keys) == 3 else None
113
157
 
114
158
  pipe = mrsm.Pipe(ck, mk, lk, instance=instance)
115
- return [
116
- html.Br(),
117
- build_pipe_card(pipe, authenticated=authenticated, include_manage=False),
118
- html.Br(),
119
- ]
159
+ return (
160
+ build_pipes_navbar(instance, with_instance_select=False),
161
+ [
162
+ html.Br(),
163
+ build_pipe_card(pipe, authenticated=authenticated, include_manage=False),
164
+ html.Br(),
165
+ ]
166
+ )
120
167
 
121
168
 
122
169
  @dash_app.callback(
@@ -125,32 +172,64 @@ def render_pipe_page_from_url(
125
172
  Input('pipes-metric-keys-dropdown', 'value'),
126
173
  Input('pipes-location-keys-dropdown', 'value'),
127
174
  Input('pipes-tags-dropdown', 'value'),
175
+ Input('instance-select', 'value'),
176
+ Input('pipes-clear-all-button', 'n_clicks'),
128
177
  )
129
- def update_location_on_pipes_filter_change(connector_keys, metric_keys, location_keys, tags):
178
+ def update_location_on_pipes_filter_change(
179
+ connector_keys: Optional[List[str]],
180
+ metric_keys: Optional[List[str]],
181
+ location_keys: Optional[List[str]],
182
+ tags: Optional[List[str]],
183
+ instance_keys: str,
184
+ clear_all_button_n_clicks: Optional[int],
185
+ ):
130
186
  """
131
187
  Update the URL parameters when clicking the dropdowns.
132
188
  """
133
- if not any((connector_keys or []) + (metric_keys or []) + (location_keys or []) + (tags or [])):
189
+ ctx = dash.callback_context.triggered
190
+ if len(ctx) != 1:
191
+ raise PreventUpdate
192
+
193
+ if not any(
194
+ (connector_keys or [])
195
+ + (metric_keys or [])
196
+ + (location_keys or [])
197
+ + (tags or [])
198
+ + ([instance_keys] if instance_keys else [])
199
+ ):
134
200
  return ''
135
201
 
136
- search_str = "?"
202
+ if ctx[0].get('prop_id', None) == 'pipes-clear-all-button.n_clicks':
203
+ connector_keys = []
204
+ metric_keys = []
205
+ location_keys = []
206
+ tags = []
207
+
208
+ include_instance_keys = instance_keys and instance_keys != str(get_api_connector())
209
+ search_str = ""
137
210
 
138
211
  if connector_keys:
139
- search_str += "connector_keys=" + ','.join(connector_keys)
140
- if metric_keys or location_keys or tags:
212
+ search_str += "connector_keys=" + ','.join((quote_plus(ck) for ck in connector_keys))
213
+ if metric_keys or location_keys or tags or include_instance_keys:
141
214
  search_str += '&'
142
215
 
143
216
  if metric_keys:
144
- search_str += "metric_keys=" + ','.join(metric_keys)
145
- if location_keys or tags:
217
+ search_str += "metric_keys=" + ','.join((quote_plus(mk) for mk in metric_keys))
218
+ if location_keys or tags or include_instance_keys:
146
219
  search_str += '&'
147
220
 
148
221
  if location_keys:
149
- search_str += "location_keys=" + ','.join(location_keys)
150
- if tags:
222
+ search_str += "location_keys=" + ','.join((quote_plus(str(lk)) for lk in location_keys))
223
+ if tags or include_instance_keys:
151
224
  search_str += '&'
152
225
 
153
226
  if tags:
154
- search_str += "tags=" + ','.join(tags)
227
+ search_str += "tags=" + ','.join((quote_plus(tag) for tag in tags))
228
+ if include_instance_keys:
229
+ search_str += '&'
230
+
231
+ if instance_keys:
232
+ if include_instance_keys:
233
+ search_str += "instance=" + quote_plus(instance_keys)
155
234
 
156
- return search_str
235
+ return ('?' + search_str) if search_str else ''
@@ -15,6 +15,7 @@ from dash.exceptions import PreventUpdate
15
15
  from meerschaum.core import User
16
16
  from meerschaum._internal.static import STATIC_CONFIG
17
17
  from meerschaum.utils.packages import attempt_import
18
+ from meerschaum.api._oauth2 import CustomOAuth2PasswordRequestForm
18
19
  dash = attempt_import('dash', check_update=CHECK_UPDATE)
19
20
  from fastapi.exceptions import HTTPException
20
21
 
@@ -97,10 +98,16 @@ def register_button_click(
97
98
  form_class += ' is-invalid'
98
99
  return {}, form_class, dash.no_update
99
100
  try:
100
- _ = login({'username': username, 'password': password})
101
+ form = CustomOAuth2PasswordRequestForm(
102
+ grant_type='password',
103
+ username=username,
104
+ password=password,
105
+ scope=' '.join(STATIC_CONFIG['tokens']['scopes'])
106
+ )
107
+ _ = login(form)
101
108
  session_data = {'session-id': str(uuid.uuid4())}
102
109
  set_session(session_data['session-id'], {'username': username})
103
- except HTTPException as e:
110
+ except HTTPException:
104
111
  form_class += ' is-invalid'
105
112
  session_data = None
106
113
  return session_data, form_class, (dash.no_update if not session_data else endpoints['dash'])
@@ -54,7 +54,8 @@ def refresh_tokens_button_click(
54
54
  html.H4('No tokens registered.'),
55
55
  html.P('Click the `+` button to register a new token.'),
56
56
  ],
57
- alerts
57
+ build_tokens_register_input_modal(),
58
+ alerts,
58
59
  )
59
60
 
60
61
  return tokens_table, build_tokens_register_input_modal(), alerts
@@ -13,7 +13,7 @@ from meerschaum.utils.typing import SuccessTuple, List
13
13
  from meerschaum._internal.static import STATIC_CONFIG
14
14
  from meerschaum.utils.misc import remove_ansi
15
15
  from meerschaum._internal.shell.Shell import get_shell_intro
16
- from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled
16
+ from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled, get_api_connector
17
17
  from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
18
18
  from meerschaum.utils.misc import get_connector_labels
19
19
  from meerschaum.config import __doc__ as doc
@@ -104,7 +104,7 @@ instance_select = dbc.Select(
104
104
  id='instance-select',
105
105
  size='sm',
106
106
  options=[
107
- {'label': i, 'value': i}
107
+ {'label': (i[:32] + '…') if len(i) > 32 else i, 'value': i}
108
108
  for i in get_connector_labels(*instance_types)
109
109
  ],
110
110
  class_name='dbc_dark custom-select custom-select-sm',
@@ -163,6 +163,7 @@ pages_navbar = html.Div(
163
163
  id='pages-navbar-div',
164
164
  )
165
165
 
166
+
166
167
  navbar = dbc.Navbar(
167
168
  dbc.Container(
168
169
  [
@@ -171,13 +172,11 @@ navbar = dbc.Navbar(
171
172
  dbc.Collapse(
172
173
  dbc.Row(
173
174
  [
174
- dbc.Col(instance_select),
175
- dbc.Col(
176
- sign_out_button,
177
- className="ms-auto",
178
- ),
175
+ dbc.Col(instance_select, width="auto"),
176
+ dbc.Col(sign_out_button, width="auto"),
179
177
  ],
180
178
  className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
179
+ align="center",
181
180
  ),
182
181
  id='navbar-collapse',
183
182
  is_open=False,
@@ -227,7 +227,23 @@ dropdown_tab_content = html.Div([
227
227
  [
228
228
  dropdown_keys_row,
229
229
  html.Br(),
230
- tags_dropdown,
230
+ dbc.Row(
231
+ [
232
+ dbc.Col(tags_dropdown, width=True),
233
+ dbc.Col(
234
+ dbc.Button(
235
+ "Clear all",
236
+ id='clear-all-keys-button',
237
+ color='link',
238
+ size='sm',
239
+ style={'text-decoration': 'none'},
240
+ ),
241
+ width='auto',
242
+ ),
243
+ ],
244
+ className='g-0',
245
+ align='center',
246
+ ),
231
247
  ], ### end of card children
232
248
  className='card-text',
233
249
  )
@@ -49,11 +49,11 @@ registration_div = html.Div(
49
49
  html.Code('mrsm register user newuser -i sql:main', className='codeblock'),
50
50
  ),
51
51
  dcc.Markdown("""
52
- To enable online registration, open the `system` configuration file and""" +
52
+ To enable online registration, open the `api` configuration file and""" +
53
53
  """ set the permissions to `true`:"""
54
54
  ),
55
55
  html.Pre(
56
- html.Code('mrsm edit config system', className='codeblock'),
56
+ html.Code('mrsm edit config api', className='codeblock'),
57
57
  ),
58
58
  html.Br(),
59
59
  dcc.Markdown('The settings file should look something like this:'),
@@ -7,17 +7,27 @@ Display pipes via a shareable URL.
7
7
 
8
8
  from meerschaum.api import CHECK_UPDATE
9
9
  from meerschaum.utils.packages import import_html, import_dcc
10
- from meerschaum.api.dash.components import download_dataframe, pages_navbar, navbar
10
+ from meerschaum.api.dash.components import (
11
+ download_dataframe,
12
+ )
11
13
 
12
14
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
13
15
  import dash_bootstrap_components as dbc
14
16
 
15
-
16
17
  layout = [
17
- pages_navbar,
18
+ html.Div(id='pipes-navbar-div'),
18
19
  dcc.Location('pipes-location'),
19
20
  download_dataframe,
20
21
  dbc.Container([
21
- html.Div(id='pipe-output-div'),
22
+ dcc.Loading(
23
+ html.Div(id='pipe-output-div'),
24
+ id='pipes-loading',
25
+ type='circle',
26
+ delay_hide=1000,
27
+ delay_show=1000,
28
+ style={
29
+ 'padding-top': '100px',
30
+ },
31
+ ),
22
32
  ])
23
33
  ]