meerschaum 2.8.4__py3-none-any.whl → 2.9.0__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 (57) hide show
  1. meerschaum/api/_chunks.py +67 -0
  2. meerschaum/api/dash/callbacks/__init__.py +5 -2
  3. meerschaum/api/dash/callbacks/custom.py +21 -8
  4. meerschaum/api/dash/callbacks/dashboard.py +26 -4
  5. meerschaum/api/dash/callbacks/settings/__init__.py +8 -0
  6. meerschaum/api/dash/callbacks/settings/password_reset.py +76 -0
  7. meerschaum/api/dash/components.py +136 -25
  8. meerschaum/api/dash/pages/__init__.py +1 -0
  9. meerschaum/api/dash/pages/dashboard.py +11 -9
  10. meerschaum/api/dash/pages/plugins.py +31 -27
  11. meerschaum/api/dash/pages/settings/__init__.py +8 -0
  12. meerschaum/api/dash/pages/settings/password_reset.py +63 -0
  13. meerschaum/api/dash/webterm.py +6 -3
  14. meerschaum/api/resources/static/css/dash.css +8 -1
  15. meerschaum/api/resources/templates/termpage.html +4 -0
  16. meerschaum/api/routes/_pipes.py +232 -79
  17. meerschaum/config/_default.py +4 -0
  18. meerschaum/config/_version.py +1 -1
  19. meerschaum/connectors/__init__.py +1 -0
  20. meerschaum/connectors/api/_APIConnector.py +12 -1
  21. meerschaum/connectors/api/_pipes.py +106 -45
  22. meerschaum/connectors/api/_plugins.py +51 -45
  23. meerschaum/connectors/api/_request.py +1 -1
  24. meerschaum/connectors/parse.py +1 -2
  25. meerschaum/connectors/sql/_SQLConnector.py +3 -0
  26. meerschaum/connectors/sql/_cli.py +1 -0
  27. meerschaum/connectors/sql/_create_engine.py +51 -4
  28. meerschaum/connectors/sql/_pipes.py +38 -6
  29. meerschaum/connectors/sql/_sql.py +35 -4
  30. meerschaum/connectors/valkey/_ValkeyConnector.py +2 -0
  31. meerschaum/connectors/valkey/_pipes.py +51 -39
  32. meerschaum/core/Pipe/__init__.py +1 -0
  33. meerschaum/core/Pipe/_data.py +1 -2
  34. meerschaum/core/Pipe/_sync.py +64 -4
  35. meerschaum/plugins/_Plugin.py +21 -5
  36. meerschaum/plugins/__init__.py +32 -8
  37. meerschaum/utils/dataframe.py +139 -2
  38. meerschaum/utils/dtypes/__init__.py +211 -1
  39. meerschaum/utils/dtypes/sql.py +296 -5
  40. meerschaum/utils/formatting/_shell.py +1 -4
  41. meerschaum/utils/misc.py +1 -1
  42. meerschaum/utils/packages/_packages.py +7 -1
  43. meerschaum/utils/sql.py +139 -11
  44. meerschaum/utils/venv/__init__.py +6 -1
  45. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/METADATA +17 -3
  46. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/RECORD +52 -52
  47. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/WHEEL +1 -1
  48. meerschaum/_internal/gui/__init__.py +0 -43
  49. meerschaum/_internal/gui/app/__init__.py +0 -50
  50. meerschaum/_internal/gui/app/_windows.py +0 -74
  51. meerschaum/_internal/gui/app/actions.py +0 -30
  52. meerschaum/_internal/gui/app/pipes.py +0 -47
  53. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/LICENSE +0 -0
  54. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/NOTICE +0 -0
  55. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/entry_points.txt +0 -0
  56. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/top_level.txt +0 -0
  57. {meerschaum-2.8.4.dist-info → meerschaum-2.9.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,67 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Utility functions for the retrieval, caching, and response of chunk data.
6
+ """
7
+
8
+ import random
9
+ from datetime import datetime, timedelta, timezone
10
+ from typing import Dict, Generator, Any, Union, Optional, List
11
+
12
+ import meerschaum as mrsm
13
+ from meerschaum.api import get_cache_connector
14
+ from meerschaum.utils.misc import generate_password
15
+
16
+ CHUNKS_TOKENS_GENERATORS: Dict[str, Dict[str, Union[Generator[Any, None, None], datetime, int]]]
17
+ DEFAULT_TTL_SECONDS = mrsm.get_config('system', 'api', 'data', 'chunks', 'ttl_seconds')
18
+
19
+
20
+ def generate_chunks_cursor_token(
21
+ pipe: mrsm.Pipe,
22
+ select_columns: Optional[List[str]] = None,
23
+ omit_columns: Optional[List[str]] = None,
24
+ begin: Union[datetime, int, None] = None,
25
+ end: Union[datetime, int, None] = None,
26
+ params: Optional[Dict[str, Any]] = None,
27
+ limit: Optional[int] = None,
28
+ order: Optional[str] = 'asc',
29
+ ttl_seconds: Optional[int] = None,
30
+ debug: bool = False,
31
+ ) -> str:
32
+ """
33
+ Store a generator in the `CHUNKS_TOKENS_GENERATORS`
34
+ """
35
+ now = datetime.now(timezone.utc)
36
+ cache_connector = get_cache_connector()
37
+ if cache_connector is None:
38
+ pass
39
+
40
+ ttl_seconds = ttl_seconds or DEFAULT_TTL_SECONDS
41
+ chunk_bounds = pipe.get_chunk_bounds(
42
+ begin=begin,
43
+ end=end,
44
+ bounded=True,
45
+ )
46
+
47
+ while True:
48
+ chunk_token = prefix + generate_password(random.randint(6, 12))
49
+ if chunk_token in CHUNKS_TOKENS_GENERATORS:
50
+ continue
51
+ break
52
+
53
+ CHUNKS_TOKENS_GENERATORS[chunk_token] = {
54
+ 'generator': chunk_generator,
55
+ 'created': now,
56
+ 'ttl': ttl_seconds,
57
+ 'last_accessed': now,
58
+ }
59
+
60
+ return chunk_token
61
+
62
+
63
+ def deallocate_expired_generators():
64
+ """
65
+ Periodically delete chunk tokens with an expired ttl timestamp.
66
+ """
67
+ chunk_tokens = list(CHUNKS_TOKENS_GENERATORS)
@@ -6,13 +6,16 @@
6
6
  Define callbacks for pages.
7
7
  """
8
8
 
9
+ from meerschaum.api import debug as _debug
9
10
  import meerschaum.api.dash.callbacks.dashboard
10
11
  import meerschaum.api.dash.callbacks.login
11
12
  import meerschaum.api.dash.callbacks.plugins
12
13
  import meerschaum.api.dash.callbacks.jobs
13
14
  import meerschaum.api.dash.callbacks.register
14
15
  import meerschaum.api.dash.callbacks.pipes
16
+ import meerschaum.api.dash.callbacks.settings
15
17
  from meerschaum.api.dash.callbacks.custom import init_dash_plugins, add_plugin_pages
16
18
 
17
- init_dash_plugins()
18
- add_plugin_pages()
19
+ init_dash_plugins(debug=_debug)
20
+ add_plugin_pages(debug=_debug)
21
+
@@ -7,13 +7,16 @@ Import custom callbacks created by plugins.
7
7
  """
8
8
 
9
9
  import traceback
10
+ from typing import Any, Dict
11
+
10
12
  from meerschaum.api.dash import dash_app
11
13
  from meerschaum.plugins import _dash_plugins, _plugin_endpoints_to_pages
12
- from meerschaum.utils.warnings import warn
13
- from meerschaum.api.dash.callbacks.dashboard import _paths, _required_login
14
+ from meerschaum.utils.warnings import warn, dprint
15
+ from meerschaum.api.dash.callbacks.dashboard import _paths, _required_login, _pages
16
+ from meerschaum.api.dash.components import pages_navbar
14
17
 
15
18
 
16
- def init_dash_plugins():
19
+ def init_dash_plugins(debug: bool = False):
17
20
  """
18
21
  Fire the initial callbacks for Dash plugins.
19
22
  """
@@ -29,11 +32,21 @@ def init_dash_plugins():
29
32
  )
30
33
 
31
34
 
32
- def add_plugin_pages():
35
+ def add_plugin_pages(debug: bool = False):
33
36
  """
34
37
  Allow users to add pages via the `@web_page` decorator.
35
38
  """
36
- for _endpoint, _page_dict in _plugin_endpoints_to_pages.items():
37
- _paths[_endpoint] = _page_dict['function']()
38
- if _page_dict['login_required']:
39
- _required_login.add(_endpoint)
39
+ for plugin_name, pages_dicts in _plugin_endpoints_to_pages.items():
40
+ if debug:
41
+ dprint(f"Adding pages from plugin '{plugin_name}'...")
42
+ for _endpoint, _page_dict in pages_dicts.items():
43
+ page_layout = _page_dict['function']()
44
+ if not _page_dict['skip_navbar']:
45
+ if isinstance(page_layout, list):
46
+ page_layout = [pages_navbar] + page_layout
47
+ else:
48
+ page_layout = [pages_navbar, page_layout]
49
+ _pages[_page_dict['page_key']] = _endpoint
50
+ _paths[_endpoint] = page_layout
51
+ if _page_dict['login_required']:
52
+ _required_login.add(_endpoint)
@@ -33,7 +33,10 @@ from meerschaum.api.dash.users import get_users_cards
33
33
  from meerschaum.api.dash.graphs import get_graphs_cards
34
34
  from meerschaum.api.dash.webterm import get_webterm
35
35
  from meerschaum.api.dash.components import (
36
- alert_from_success_tuple, console_div, build_cards_grid,
36
+ alert_from_success_tuple,
37
+ console_div,
38
+ build_cards_grid,
39
+ build_pages_offcanvas_children,
37
40
  )
38
41
  from meerschaum.api.dash import pages
39
42
  from meerschaum.utils.typing import Dict
@@ -103,6 +106,10 @@ _paths = {
103
106
  'job' : pages.job.layout,
104
107
  }
105
108
  _required_login = {''}
109
+ _pages = {
110
+ 'Web Console': '/dash/',
111
+ 'Plugins': '/dash/plugins',
112
+ }
106
113
 
107
114
 
108
115
  @dash_app.callback(
@@ -696,7 +703,6 @@ dash_app.clientside_callback(
696
703
  dash_app.clientside_callback(
697
704
  """
698
705
  function(n_clicks){
699
- console.log('fullscreen');
700
706
  if (!n_clicks) { return dash_clientside.no_update; }
701
707
  iframe = document.getElementById('webterm-iframe');
702
708
  if (!iframe){ return dash_clientside.no_update; }
@@ -707,11 +713,11 @@ dash_app.clientside_callback(
707
713
  if (leftCol.style.display === 'none') {
708
714
  leftCol.style.display = '';
709
715
  rightCol.className = 'col-6';
710
- button.innerHTML = "Full View";
716
+ button.innerHTML = "";
711
717
  } else {
712
718
  leftCol.style.display = 'none';
713
719
  rightCol.className = 'col-12';
714
- button.innerHTML = "Side-by-side View";
720
+ button.innerHTML = "🀲";
715
721
  }
716
722
 
717
723
  return dash_clientside.no_update;
@@ -1078,3 +1084,19 @@ def parameters_as_yaml_or_json_click(
1078
1084
  if as_yaml:
1079
1085
  return yaml.dump(pipe.parameters)
1080
1086
  return json.dumps(pipe.parameters, indent=4, separators=(',', ': '), sort_keys=True)
1087
+
1088
+
1089
+ @dash_app.callback(
1090
+ Output('pages-offcanvas', 'is_open'),
1091
+ Output('pages-offcanvas', 'children'),
1092
+ Input('logo-img', 'n_clicks'),
1093
+ State('pages-offcanvas', 'is_open'),
1094
+ )
1095
+ def toggle_pages_offcanvas(n_clicks: Optional[int], is_open: bool):
1096
+ """
1097
+ Toggle the pages sidebar.
1098
+ """
1099
+ pages_children = build_pages_offcanvas_children()
1100
+ if n_clicks:
1101
+ return not is_open, pages_children
1102
+ return is_open, pages_children
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the callbacks for the settings pages.
6
+ """
7
+
8
+ import meerschaum.api.dash.callbacks.settings.password_reset
@@ -0,0 +1,76 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the callbacks for the password reset page.
6
+ """
7
+
8
+ import dash
9
+ from dash.exceptions import PreventUpdate
10
+ from dash.dependencies import Input, Output, State
11
+ import dash_bootstrap_components as dbc
12
+
13
+ from meerschaum.core.User import User
14
+ from meerschaum.config.static import STATIC_CONFIG
15
+ from meerschaum.api import get_api_connector, debug
16
+ from meerschaum.api.dash import dash_app
17
+ from meerschaum.api.dash.components import alert_from_success_tuple
18
+ from meerschaum.api.dash.sessions import get_username_from_session
19
+
20
+
21
+ @dash_app.callback(
22
+ Output('password-reset-alert-div', 'children'),
23
+ Input('password-reset-button', 'n_clicks'),
24
+ State('password-reset-input', 'value',),
25
+ State('session-store', 'data'),
26
+ )
27
+ def password_reset_button_click(n_clicks, new_password_value, session_store_data):
28
+ """
29
+ Attempt the password reset with the form data.
30
+ """
31
+ if not n_clicks:
32
+ raise PreventUpdate
33
+
34
+ session_id = session_store_data.get('session-id', None)
35
+ username = get_username_from_session(session_id)
36
+ if not username:
37
+ return alert_from_success_tuple(
38
+ (False, "Invalid session. Are you logged in correctly?")
39
+ )
40
+
41
+ instance_connector = get_api_connector()
42
+ user = User(username, new_password_value)
43
+ success, msg = instance_connector.edit_user(user, debug=debug)
44
+ return alert_from_success_tuple((success, msg))
45
+
46
+
47
+ @dash_app.callback(
48
+ Output('password-reset-input', 'valid'),
49
+ Output('password-reset-input', 'invalid'),
50
+ Input('password-reset-input', 'value'),
51
+ )
52
+ def validate_new_password(new_password_value):
53
+ if not new_password_value:
54
+ raise PreventUpdate
55
+
56
+ valid = (len(new_password_value) >= STATIC_CONFIG['users']['min_password_length'])
57
+ return valid, not valid
58
+
59
+
60
+ @dash_app.callback(
61
+ Output("password-reset-confirm-input", "valid"),
62
+ Output("password-reset-confirm-input", "invalid"),
63
+ Output("password-reset-button", 'disabled'),
64
+ Input("password-reset-confirm-input", "value"),
65
+ State("password-reset-input", "value"),
66
+ )
67
+ def validate_new_passwords_match(confirm_password_value, new_password_value):
68
+ if not confirm_password_value:
69
+ raise PreventUpdate
70
+
71
+ new_password_is_valid, _ = validate_new_password(new_password_value)
72
+ if not new_password_is_valid:
73
+ raise PreventUpdate
74
+
75
+ valid = confirm_password_value == new_password_value
76
+ return valid, not valid, not valid
@@ -92,9 +92,9 @@ search_parameters_editor = dash_ace.DashAceEditor(
92
92
  style={'height': 100},
93
93
  )
94
94
 
95
- sidebar = dbc.Offcanvas(
96
- children=[],
97
- title='Pages',
95
+ pages_offcanvas = dbc.Offcanvas(
96
+ title='',
97
+ id='pages-offcanvas',
98
98
  )
99
99
 
100
100
  download_dataframe = dcc.Download(id='download-dataframe-csv')
@@ -110,38 +110,71 @@ instance_select = dbc.Select(
110
110
  class_name='dbc_dark custom-select custom-select-sm',
111
111
  )
112
112
 
113
+ sign_out_button = dbc.Button(
114
+ "Sign out",
115
+ color='link',
116
+ style={'margin-left': '30px'},
117
+ id='sign-out-button',
118
+ )
119
+
120
+ logo_row = dbc.Row(
121
+ [
122
+ dbc.Col(
123
+ html.Img(
124
+ src=endpoints['dash'] + "/assets/logo_48x48.png",
125
+ title=doc,
126
+ id="logo-img",
127
+ style={'cursor': 'pointer'},
128
+ ),
129
+ ),
130
+ ],
131
+ align='center',
132
+ className='g-0 navbar-logo-row',
133
+ )
134
+
135
+ pages_navbar = html.Div(
136
+ [
137
+ pages_offcanvas,
138
+ dbc.Navbar(
139
+ dbc.Container(
140
+ [
141
+ logo_row,
142
+ dbc.NavbarToggler(id="navbar-toggler", n_clicks=0),
143
+ dbc.Collapse(
144
+ dbc.Row(
145
+ [
146
+ dbc.Col(
147
+ sign_out_button,
148
+ className="ms-auto",
149
+ ),
150
+ ],
151
+ className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
152
+ ),
153
+ id='navbar-collapse',
154
+ is_open=False,
155
+ navbar=True,
156
+ ),
157
+ ]
158
+ ),
159
+ dark=True,
160
+ color='dark'
161
+ ),
162
+ ],
163
+ id='pages-navbar-div',
164
+ )
113
165
 
114
166
  navbar = dbc.Navbar(
115
167
  dbc.Container(
116
168
  [
117
- html.A(
118
- dbc.Row(
119
- [
120
- dbc.Col(
121
- html.Img(
122
- src=endpoints['dash'] + "/assets/logo_48x48.png",
123
- title=doc,
124
- ),
125
- ),
126
- ],
127
- align='center',
128
- className='g-0 navbar-logo-row',
129
- ),
130
- href=('/docs' if docs_enabled else '#'),
131
- style={"textDecoration": "none"},
132
- ),
169
+ logo_row,
133
170
  dbc.NavbarToggler(id="navbar-toggler", n_clicks=0),
134
171
  dbc.Collapse(
135
172
  dbc.Row(
136
173
  [
137
174
  dbc.Col(instance_select),
138
175
  dbc.Col(
139
- dbc.Button(
140
- "Sign out",
141
- color='link',
142
- style={'margin-left': '30px'},
143
- id='sign-out-button',
144
- ),
176
+ sign_out_button,
177
+ className="ms-auto",
145
178
  ),
146
179
  ],
147
180
  className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
@@ -199,3 +232,81 @@ def build_cards_grid(cards: List[dbc.Card], num_columns: int = 3) -> html.Div:
199
232
  rows.append(r)
200
233
  rows.append(html.Br())
201
234
  return html.Div(rows)
235
+
236
+
237
+ def build_pages_offcanvas_children():
238
+ """
239
+ Return the contents of the pages offcanvas.
240
+ """
241
+ from meerschaum.api.dash.callbacks.dashboard import _pages
242
+ from meerschaum.api.dash.callbacks.custom import _plugin_endpoints_to_pages
243
+ pages_listgroup_items = []
244
+ custom_pages = []
245
+ for pages_dicts in _plugin_endpoints_to_pages.values():
246
+ for page_dict in pages_dicts.values():
247
+ custom_pages.append(page_dict['page_key'])
248
+
249
+ for page_key, page_href in _pages.items():
250
+ if page_key in custom_pages:
251
+ continue
252
+ pages_listgroup_items.append(
253
+ dbc.ListGroupItem(
254
+ dbc.Button(
255
+ ' '.join([word.capitalize() for word in page_key.split(' ')]),
256
+ style={
257
+ 'width': '100%',
258
+ 'text-align': 'left',
259
+ 'text-decoration': 'none',
260
+ 'padding-left': '0',
261
+ },
262
+ href=page_href,
263
+ color='dark',
264
+ )
265
+ )
266
+ )
267
+
268
+ plugins_accordion_items = []
269
+ for page_group, pages_dicts in _plugin_endpoints_to_pages.items():
270
+ plugin_listgroup_items = [
271
+ dbc.ListGroupItem(
272
+ dbc.Button(
273
+ ' '.join([word.capitalize() for word in page_dict['page_key'].split(' ')]),
274
+ style={
275
+ 'width': '100%',
276
+ 'text-align': 'left',
277
+ 'text-decoration': 'none',
278
+ 'padding-left': '0',
279
+ },
280
+ href=page_href,
281
+ color='dark',
282
+ )
283
+ )
284
+ for page_href, page_dict in pages_dicts.items()
285
+ ]
286
+ plugin_listgroup = dbc.ListGroup(plugin_listgroup_items, flush=True)
287
+ plugin_accordion_item = dbc.AccordionItem(
288
+ plugin_listgroup,
289
+ title=(
290
+ page_group.capitalize()
291
+ if page_group and not page_group[0].isupper()
292
+ else page_group
293
+ ),
294
+ class_name='pages-offcanvas-accordion',
295
+ )
296
+ plugins_accordion_items.append(plugin_accordion_item)
297
+ plugins_accordion = dbc.Accordion(
298
+ plugins_accordion_items,
299
+ start_collapsed=True,
300
+ flush=True,
301
+ always_open=True,
302
+ )
303
+ pages_listgroup_items.append(plugins_accordion)
304
+
305
+ pages_children = dbc.Card(
306
+ dbc.ListGroup(
307
+ pages_listgroup_items,
308
+ flush=True,
309
+ ),
310
+ outline=True,
311
+ )
312
+ return pages_children
@@ -12,3 +12,4 @@ import meerschaum.api.dash.pages.plugins
12
12
  import meerschaum.api.dash.pages.register
13
13
  import meerschaum.api.dash.pages.pipes
14
14
  import meerschaum.api.dash.pages.job
15
+ import meerschaum.api.dash.pages.settings
@@ -37,6 +37,7 @@ from meerschaum.api.dash.components import (
37
37
  console_div,
38
38
  download_dataframe,
39
39
  navbar,
40
+ pages_offcanvas,
40
41
  download_logs,
41
42
  refresh_jobs_interval,
42
43
  )
@@ -47,22 +48,23 @@ from meerschaum.api.dash.keys import (
47
48
  )
48
49
 
49
50
  layout = html.Div(
50
- id = 'main-div',
51
- children = [
51
+ id='main-div',
52
+ children=[
52
53
  keys_lists_content,
53
54
  download_dataframe,
54
55
  download_logs,
55
56
  refresh_jobs_interval,
56
57
  navbar,
58
+ pages_offcanvas,
57
59
  html.Div(
58
60
  dbc.Row(
59
- id = 'content-row',
60
- children = [
61
+ id='content-row',
62
+ children=[
61
63
  dbc.Col(
62
- children = [
64
+ children=[
63
65
  dbc.Tabs(
64
- id = 'pipes-filter-tabs',
65
- children = [
66
+ id='pipes-filter-tabs',
67
+ children=[
66
68
  dbc.Tab(
67
69
  dropdown_tab_content,
68
70
  label='Filters',
@@ -107,9 +109,9 @@ layout = html.Div(
107
109
  id='content-col-right',
108
110
  ),
109
111
  ],
110
- style = {'max-width': '100%', 'padding': '15px'},
112
+ style={'max-width': '100%', 'padding': '15px'},
111
113
  ), ### end of Row
112
- className = 'container-fluid',
114
+ className='container-fluid',
113
115
  ), ### end of Div
114
116
  html.P('', id='line-buffer', style={'display': 'none'}),
115
117
  ],
@@ -13,6 +13,7 @@ html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHEC
13
13
  import dash_bootstrap_components as dbc
14
14
  from meerschaum.core import Plugin
15
15
  from meerschaum.utils.typing import Optional
16
+ from meerschaum.api.dash.components import pages_navbar
16
17
 
17
18
  search_box = dbc.Input(
18
19
  id = "search-plugins-input",
@@ -20,32 +21,35 @@ search_box = dbc.Input(
20
21
  type = "text",
21
22
  )
22
23
 
23
- layout = dbc.Container([
24
- html.Div([
25
- html.Br(),
26
- html.Div(
27
- dbc.Container([
28
- html.H3('Plugins'),
29
- html.P([
30
- (
31
- 'Plugins extend the functionality of Meerschaum.'
32
- + ' To find out more, check out the '
33
- ),
34
- html.A(
35
- 'plugins documentation',
36
- href = 'https://meerschaum.io/reference/plugins/',
37
- rel = "noreferrer noopener",
38
- target = "_blank",
39
- ),
40
- '.',
24
+ layout = [
25
+ pages_navbar,
26
+ dbc.Container([
27
+ html.Div([
28
+ html.Br(),
29
+ html.Div(
30
+ dbc.Container([
31
+ html.H3('Plugins'),
32
+ html.P([
33
+ (
34
+ 'Plugins extend the functionality of Meerschaum.'
35
+ ' To find out more, check out the '
36
+ ),
37
+ html.A(
38
+ 'plugins documentation',
39
+ href='https://meerschaum.io/reference/plugins/',
40
+ rel="noreferrer noopener",
41
+ target="_blank",
42
+ ),
43
+ '.',
44
+ ]),
41
45
  ]),
42
- ]),
43
- className = 'page-header',
44
- style = {'background-color': 'var(--dark)', 'padding': '1em'},
45
- ),
46
- html.Br(),
47
- search_box,
48
- html.Br(),
49
- html.Div([], id='plugins-cards-div'),
46
+ className='page-header',
47
+ style={'background-color': 'var(--dark)', 'padding': '1em'},
48
+ ),
49
+ html.Br(),
50
+ search_box,
51
+ html.Br(),
52
+ html.Div([], id='plugins-cards-div'),
53
+ ])
50
54
  ])
51
- ])
55
+ ]
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the layouts for the `settings` pages.
6
+ """
7
+
8
+ import meerschaum.api.dash.pages.settings.password_reset
@@ -0,0 +1,63 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define the password reset page layout.
6
+ """
7
+
8
+ import dash_bootstrap_components as dbc
9
+ import dash.html as html
10
+ import dash.dcc as dcc
11
+ from meerschaum.plugins import web_page
12
+
13
+
14
+ @web_page('password-reset', login_required=True, page_group='Settings')
15
+ def page_layout():
16
+ """
17
+ Return the layout for this page.
18
+ """
19
+ return dbc.Container([
20
+ html.Br(),
21
+ html.H3('Password Reset'),
22
+ html.Br(),
23
+ html.Div(id="password-reset-alert-div"),
24
+ dbc.Form([
25
+ dbc.Row(
26
+ [
27
+ dbc.Label("New Password", html_for="password-reset-input", width=2),
28
+ dbc.Col(
29
+ dbc.Input(
30
+ type="password",
31
+ id="password-reset-input",
32
+ placeholder="Enter new password",
33
+ ),
34
+ width=10,
35
+ ),
36
+ ],
37
+ className="mb-3",
38
+ ),
39
+ dbc.Row(
40
+ [
41
+ dbc.Label("Confirm Password", html_for="password-reset-confirm-input", width=2),
42
+ dbc.Col(
43
+ dbc.Input(
44
+ type="password",
45
+ id="password-reset-confirm-input",
46
+ placeholder="Confirm new password",
47
+ ),
48
+ width=10,
49
+ ),
50
+ ],
51
+ className="mb-3",
52
+ ),
53
+ dbc.Row(
54
+ [
55
+ dbc.Col(
56
+ dbc.Button("Submit", id="password-reset-button", disabled=True),
57
+ ),
58
+ ],
59
+ justify="end",
60
+ className="mb-3",
61
+ ),
62
+ ]),
63
+ ])