meerschaum 3.0.0rc2__py3-none-any.whl → 3.0.0rc3__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 (41) hide show
  1. meerschaum/_internal/shell/Shell.py +5 -4
  2. meerschaum/actions/bootstrap.py +1 -1
  3. meerschaum/actions/edit.py +6 -3
  4. meerschaum/actions/start.py +1 -1
  5. meerschaum/api/dash/callbacks/__init__.py +1 -0
  6. meerschaum/api/dash/callbacks/dashboard.py +19 -18
  7. meerschaum/api/dash/callbacks/jobs.py +11 -5
  8. meerschaum/api/dash/callbacks/pipes.py +106 -5
  9. meerschaum/api/dash/callbacks/settings/__init__.py +0 -1
  10. meerschaum/api/dash/callbacks/{settings/tokens.py → tokens.py} +1 -1
  11. meerschaum/api/dash/jobs.py +1 -1
  12. meerschaum/api/dash/pages/__init__.py +2 -1
  13. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  14. meerschaum/api/dash/pages/pipes.py +4 -3
  15. meerschaum/api/dash/pages/settings/__init__.py +0 -1
  16. meerschaum/api/dash/pages/{settings/tokens.py → tokens.py} +6 -8
  17. meerschaum/api/dash/pipes.py +131 -0
  18. meerschaum/api/dash/tokens.py +26 -29
  19. meerschaum/config/_default.py +5 -4
  20. meerschaum/config/_paths.py +1 -0
  21. meerschaum/config/_version.py +1 -1
  22. meerschaum/connectors/instance/_tokens.py +6 -2
  23. meerschaum/connectors/sql/_SQLConnector.py +14 -0
  24. meerschaum/connectors/sql/_pipes.py +57 -22
  25. meerschaum/connectors/sql/tables/__init__.py +237 -122
  26. meerschaum/core/Pipe/_attributes.py +5 -2
  27. meerschaum/core/Token/_Token.py +1 -1
  28. meerschaum/plugins/bootstrap.py +508 -3
  29. meerschaum/utils/_get_pipes.py +1 -1
  30. meerschaum/utils/dataframe.py +8 -2
  31. meerschaum/utils/dtypes/__init__.py +2 -3
  32. meerschaum/utils/dtypes/sql.py +11 -11
  33. meerschaum/utils/sql.py +1 -1
  34. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/METADATA +1 -1
  35. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/RECORD +41 -41
  36. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/WHEEL +0 -0
  37. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/entry_points.txt +0 -0
  38. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/licenses/LICENSE +0 -0
  39. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/licenses/NOTICE +0 -0
  40. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/top_level.txt +0 -0
  41. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/zip-safe +0 -0
@@ -781,10 +781,11 @@ class Shell(cmd.Cmd):
781
781
  instance_keys += ':main'
782
782
 
783
783
  conn_attrs = parse_instance_keys(instance_keys, construct=False, debug=debug)
784
- if conn_attrs is None or not conn_attrs:
785
- conn_keys = str(get_connector(debug=debug))
786
- else:
787
- conn_keys = instance_keys
784
+ conn_keys = (
785
+ str(get_connector(debug=debug))
786
+ if conn_attrs is None
787
+ else instance_keys
788
+ )
788
789
 
789
790
  shell_attrs['instance_keys'] = conn_keys
790
791
 
@@ -429,7 +429,7 @@ def _bootstrap_plugins(
429
429
  action = [prompt("Enter the name of your new plugin:")]
430
430
 
431
431
  for plugin_name in action:
432
- bootstrap_success, bootstrap_msg = bootstrap_plugin(plugin_name)
432
+ bootstrap_success, bootstrap_msg = bootstrap_plugin(plugin_name, debug=debug)
433
433
  if not bootstrap_success:
434
434
  return bootstrap_success, bootstrap_msg
435
435
 
@@ -552,7 +552,12 @@ def _edit_tokens(
552
552
  dateutil_parser = mrsm.attempt_import('dateutil.parser')
553
553
 
554
554
  if not action:
555
- return False, "Provide token labels or IDs for the tokens to edit."
555
+ return (
556
+ False, (
557
+ "Provide token labels or IDs for the tokens to edit\n"
558
+ " (run `show tokens` to see registered tokens)."
559
+ )
560
+ )
556
561
 
557
562
  conn = parse_instance_keys(mrsm_instance)
558
563
 
@@ -628,8 +633,6 @@ def _edit_tokens(
628
633
 
629
634
  return True, msg
630
635
 
631
-
632
-
633
636
 
634
637
  ### NOTE: This must be the final statement of the module.
635
638
  ### Any subactions added below these lines will not
@@ -676,7 +676,7 @@ def _start_pipeline(
676
676
 
677
677
  try:
678
678
  run_loop()
679
- except KeyboardInterrupt:
679
+ except (KeyboardInterrupt, SystemExit):
680
680
  warn("Cancelled pipeline.", stack=False)
681
681
  if proc is not None:
682
682
  _stop_process(proc)
@@ -10,6 +10,7 @@ from meerschaum.api import debug as _debug
10
10
  import meerschaum.api.dash.callbacks.dashboard
11
11
  import meerschaum.api.dash.callbacks.login
12
12
  import meerschaum.api.dash.callbacks.plugins
13
+ import meerschaum.api.dash.callbacks.tokens
13
14
  import meerschaum.api.dash.callbacks.jobs
14
15
  import meerschaum.api.dash.callbacks.register
15
16
  import meerschaum.api.dash.callbacks.pipes
@@ -72,8 +72,6 @@ keys_state = (
72
72
  State({'type': 'input-flags-dropdown', 'index': ALL}, 'value'),
73
73
  State({'type': 'input-flags-dropdown-text', 'index': ALL}, 'value'),
74
74
  State('instance-select', 'value'),
75
- State('content-div-right', 'children'),
76
- State('success-alert-div', 'children'),
77
75
  State('session-store', 'data'),
78
76
  )
79
77
 
@@ -103,15 +101,20 @@ omit_actions = {
103
101
  _paths = {
104
102
  'login' : pages.login.layout,
105
103
  '' : pages.dashboard.layout,
104
+ 'pipes' : pages.pipes.layout,
106
105
  'plugins' : pages.plugins.layout,
106
+ 'tokens' : pages.tokens.layout,
107
107
  'register': pages.register.layout,
108
108
  'pipes' : pages.pipes.layout,
109
- 'job' : pages.job.layout,
109
+ 'jobs' : pages.jobs.layout,
110
110
  }
111
- _required_login = {''}
111
+ _required_login = {'', 'tokens', 'jobs', 'pipes'}
112
112
  _pages = {
113
113
  'Web Console': '/dash/',
114
+ 'Pipes': '/dash/pipes',
114
115
  'Plugins': '/dash/plugins',
116
+ 'Tokens': '/dash/tokens',
117
+ 'Jobs': '/dash/jobs',
115
118
  }
116
119
 
117
120
 
@@ -495,16 +498,12 @@ def update_flags(input_flags_dropdown_values, n_clicks, input_flags_texts):
495
498
 
496
499
  @dash_app.callback(
497
500
  Output('connector-keys-dropdown', 'options'),
498
- Output('connector-keys-list', 'children'),
499
501
  Output('connector-keys-dropdown', 'value'),
500
502
  Output('metric-keys-dropdown', 'options'),
501
- Output('metric-keys-list', 'children'),
502
503
  Output('metric-keys-dropdown', 'value'),
503
504
  Output('location-keys-dropdown', 'options'),
504
- Output('location-keys-list', 'children'),
505
505
  Output('location-keys-dropdown', 'value'),
506
506
  Output('tags-dropdown', 'options'),
507
- Output('tags-list', 'children'),
508
507
  Output('tags-dropdown', 'value'),
509
508
  Output('instance-select', 'value'),
510
509
  Output('instance-alert-div', 'children'),
@@ -664,22 +663,14 @@ def update_keys_options(
664
663
  for _tag in _tags_options
665
664
  ]
666
665
  ]
667
- _connectors_datalist = [html.Option(value=o['value']) for o in _connectors_options]
668
- _metrics_datalist = [html.Option(value=o['value']) for o in _metrics_options]
669
- _locations_datalist = [html.Option(value=o['value']) for o in _locations_options]
670
- _tags_datalist = [html.Option(value=o['value']) for o in _tags_options]
671
666
  return (
672
667
  _connectors_options,
673
- _connectors_datalist,
674
668
  connector_keys,
675
669
  _metrics_options,
676
- _metrics_datalist,
677
670
  metric_keys,
678
671
  _locations_options,
679
- _locations_datalist,
680
672
  location_keys,
681
673
  _tags_options,
682
- _tags_datalist,
683
674
  tags,
684
675
  (instance_keys if update_instance_keys else dash.no_update),
685
676
  instance_alerts,
@@ -856,6 +847,7 @@ dash_app.clientside_callback(
856
847
  @dash_app.callback(
857
848
  Output("download-dataframe-csv", "data"),
858
849
  Input({'type': 'pipe-download-csv-button', 'index': ALL}, 'n_clicks'),
850
+ prevent_initial_call=True,
859
851
  )
860
852
  def download_pipe_csv(n_clicks):
861
853
  """
@@ -885,6 +877,7 @@ def download_pipe_csv(n_clicks):
885
877
  Output({'type': 'pipe-accordion', 'index': MATCH}, 'children'),
886
878
  Input({'type': 'pipe-accordion', 'index': MATCH}, 'active_item'),
887
879
  State('session-store', 'data'),
880
+ prevent_initial_call=True,
888
881
  )
889
882
  def update_pipe_accordion(item, session_store_data):
890
883
  """
@@ -908,7 +901,8 @@ def update_pipe_accordion(item, session_store_data):
908
901
  @dash_app.callback(
909
902
  Output({'type': 'update-parameters-success-div', 'index': MATCH}, 'children'),
910
903
  Input({'type': 'update-parameters-button', 'index': MATCH}, 'n_clicks'),
911
- State({'type': 'parameters-editor', 'index': MATCH}, 'value')
904
+ State({'type': 'parameters-editor', 'index': MATCH}, 'value'),
905
+ prevent_initial_call=True,
912
906
  )
913
907
  def update_pipe_parameters_click(n_clicks, parameters_editor_text):
914
908
  if not n_clicks:
@@ -942,6 +936,7 @@ def update_pipe_parameters_click(n_clicks, parameters_editor_text):
942
936
  Output({'type': 'update-sql-success-div', 'index': MATCH}, 'children'),
943
937
  Input({'type': 'update-sql-button', 'index': MATCH}, 'n_clicks'),
944
938
  State({'type': 'sql-editor', 'index': MATCH}, 'value'),
939
+ prevent_initial_call=True,
945
940
  )
946
941
  def update_pipe_sql_click(n_clicks, sql_editor_text):
947
942
  if not n_clicks:
@@ -968,7 +963,8 @@ def update_pipe_sql_click(n_clicks, sql_editor_text):
968
963
  @dash_app.callback(
969
964
  Output({'type': 'sync-success-div', 'index': MATCH}, 'children'),
970
965
  Input({'type': 'update-sync-button', 'index': MATCH}, 'n_clicks'),
971
- State({'type': 'sync-editor', 'index': MATCH}, 'value')
966
+ State({'type': 'sync-editor', 'index': MATCH}, 'value'),
967
+ prevent_initial_call=True,
972
968
  )
973
969
  def sync_documents_click(n_clicks, sync_editor_text):
974
970
  if not n_clicks:
@@ -1014,6 +1010,7 @@ def sync_documents_click(n_clicks, sync_editor_text):
1014
1010
  State({'type': 'limit-input', 'index': MATCH}, 'value'),
1015
1011
  State({'type': 'query-data-begin-input', 'index': MATCH}, 'value'),
1016
1012
  State({'type': 'query-data-end-input', 'index': MATCH}, 'value'),
1013
+ prevent_initial_call=True,
1017
1014
  )
1018
1015
  def query_data_click(n_clicks, query_editor_text, limit_value, begin, end):
1019
1016
  triggered = dash.callback_context.triggered
@@ -1130,6 +1127,7 @@ def toggle_navbar_collapse(n_clicks: Optional[int], is_open: bool) -> bool:
1130
1127
  Output('session-store', 'data'),
1131
1128
  Input("sign-out-button", "n_clicks"),
1132
1129
  State('session-store', 'data'),
1130
+ prevent_initial_call=True,
1133
1131
  )
1134
1132
  def sign_out_button_click(
1135
1133
  n_clicks: Optional[int],
@@ -1150,6 +1148,7 @@ def sign_out_button_click(
1150
1148
  Output({'type': 'parameters-editor', 'index': MATCH}, 'value'),
1151
1149
  Input({'type': 'parameters-as-yaml-button', 'index': MATCH}, 'n_clicks'),
1152
1150
  Input({'type': 'parameters-as-json-button', 'index': MATCH}, 'n_clicks'),
1151
+ prevent_initial_call=True,
1153
1152
  )
1154
1153
  def parameters_as_yaml_or_json_click(
1155
1154
  yaml_n_clicks: Optional[int],
@@ -1178,6 +1177,7 @@ def parameters_as_yaml_or_json_click(
1178
1177
  Output({'type': 'sync-editor', 'index': MATCH}, 'value'),
1179
1178
  Input({'type': 'sync-as-json-button', 'index': MATCH}, 'n_clicks'),
1180
1179
  Input({'type': 'sync-as-lines-button', 'index': MATCH}, 'n_clicks'),
1180
+ prevent_initial_call=True,
1181
1181
  )
1182
1182
  def sync_as_json_or_lines_click(
1183
1183
  json_n_clicks: Optional[int],
@@ -1206,6 +1206,7 @@ def sync_as_json_or_lines_click(
1206
1206
  Output('pages-offcanvas', 'children'),
1207
1207
  Input('logo-img', 'n_clicks'),
1208
1208
  State('pages-offcanvas', 'is_open'),
1209
+ prevent_initial_call=True,
1209
1210
  )
1210
1211
  def toggle_pages_offcanvas(n_clicks: Optional[int], is_open: bool):
1211
1212
  """
@@ -13,12 +13,13 @@ import time
13
13
  import traceback
14
14
  from datetime import datetime, timezone
15
15
 
16
+ from meerschaum.jobs import get_jobs
16
17
  from meerschaum.utils.typing import Optional, Dict, Any
17
18
  from meerschaum.api import CHECK_UPDATE
18
19
  from meerschaum.api.dash import dash_app
19
20
  from meerschaum.api.dash.sessions import get_username_from_session
20
21
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html
21
- from meerschaum.api.dash.components import alert_from_success_tuple
22
+ from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
22
23
  from meerschaum.api.dash.jobs import (
23
24
  build_job_card,
24
25
  build_manage_job_buttons_div_children,
@@ -251,17 +252,22 @@ def render_job_page_from_url(
251
252
  session_data: Optional[Dict[str, Any]],
252
253
  ):
253
254
  """
254
- Load the `/job/{name}` page.
255
+ Load the `/dash/jobs/{name}` page.
255
256
  """
256
- if not str(pathname).startswith('/dash/job'):
257
+ if not str(pathname).startswith('/dash/jobs'):
257
258
  return no_update
258
259
 
259
260
  session_id = (session_data or {}).get('session-id', None)
260
261
  authenticated = is_session_authenticated(str(session_id))
261
262
 
262
- job_name = pathname.replace('/dash/job', '').lstrip('/').rstrip('/')
263
+ job_name = pathname.replace('/dash/jobs', '').lstrip('/').rstrip('/')
263
264
  if not job_name:
264
- return no_update
265
+ jobs = get_jobs(executor_keys='local', combine_local_and_systemd=True)
266
+ cards = [
267
+ build_job_card(job, authenticated=authenticated, include_follow=False)
268
+ for job in jobs.values()
269
+ ]
270
+ return [html.Br(), build_cards_grid(cards, 3), html.Br()]
265
271
 
266
272
  job = _get_job(job_name)
267
273
  if not job.exists():
@@ -9,11 +9,17 @@ from urllib.parse import parse_qs
9
9
 
10
10
  from dash.dependencies import Input, Output, State
11
11
  from dash import no_update
12
+ from dash.exceptions import PreventUpdate
12
13
 
13
14
  import meerschaum as mrsm
14
15
  from meerschaum.api.dash import dash_app
15
- from meerschaum.api.dash.pipes import build_pipe_card
16
- from meerschaum.api import CHECK_UPDATE
16
+ from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
17
+ from meerschaum.api.dash.pipes import (
18
+ build_pipe_card,
19
+ build_pipes_dropdown_keys_row,
20
+ build_pipes_tags_dropdown,
21
+ )
22
+ from meerschaum.api import CHECK_UPDATE, get_api_connector
17
23
  from meerschaum.utils.packages import import_html, import_dcc
18
24
  from meerschaum.api.dash.sessions import is_session_authenticated
19
25
  from meerschaum.utils.typing import Optional, Dict, Any
@@ -36,16 +42,74 @@ def render_pipe_page_from_url(
36
42
 
37
43
  session_id = (session_data or {}).get('session-id', None)
38
44
  authenticated = is_session_authenticated(str(session_id))
45
+ query_params = parse_qs(pipe_search.lstrip('?')) if pipe_search else {}
46
+ instance = query_params.get('instance', [None])[0] or str(get_api_connector())
47
+ tags = query_params.get('tags', [None])[0] or []
48
+ if isinstance(tags, str):
49
+ tags = tags.split(',')
50
+
51
+ connector_keys = query_params.get('connector_keys', [None])[0] or []
52
+ if isinstance(connector_keys, str):
53
+ connector_keys = connector_keys.split(',')
54
+
55
+ metric_keys = query_params.get('metric_keys', [None])[0] or []
56
+ if isinstance(metric_keys, str):
57
+ metric_keys = metric_keys.split(',')
58
+
59
+ location_keys = query_params.get('location_keys', [None])[0] or []
60
+ if isinstance(location_keys, str):
61
+ location_keys = location_keys.split(',')
62
+
63
+ instance_connector = mrsm.get_connector(instance)
64
+ 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
+ ]
39
70
 
40
71
  keys = pathname.replace('/dash/pipes', '').lstrip('/').rstrip('/').split('/')
41
72
  if len(keys) not in (2, 3):
42
- return no_update
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,
80
+ )
81
+ cards = [
82
+ build_pipe_card(pipe, authenticated=authenticated, include_manage=False)
83
+ for pipe in pipes
84
+ ]
85
+ return [
86
+ html.Div([
87
+ 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
+ ),
96
+ 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
+ ]
43
109
 
44
110
  ck = keys[0]
45
111
  mk = keys[1]
46
112
  lk = keys[2] if len(keys) == 3 else None
47
- query_params = parse_qs(pipe_search.lstrip('?')) if pipe_search else {}
48
- instance = query_params.get('instance', [None])[0]
49
113
 
50
114
  pipe = mrsm.Pipe(ck, mk, lk, instance=instance)
51
115
  return [
@@ -53,3 +117,40 @@ def render_pipe_page_from_url(
53
117
  build_pipe_card(pipe, authenticated=authenticated, include_manage=False),
54
118
  html.Br(),
55
119
  ]
120
+
121
+
122
+ @dash_app.callback(
123
+ Output('pipes-location', 'search'),
124
+ Input('pipes-connector-keys-dropdown', 'value'),
125
+ Input('pipes-metric-keys-dropdown', 'value'),
126
+ Input('pipes-location-keys-dropdown', 'value'),
127
+ Input('pipes-tags-dropdown', 'value'),
128
+ )
129
+ def update_location_on_pipes_filter_change(connector_keys, metric_keys, location_keys, tags):
130
+ """
131
+ Update the URL parameters when clicking the dropdowns.
132
+ """
133
+ if not any((connector_keys or []) + (metric_keys or []) + (location_keys or []) + (tags or [])):
134
+ return ''
135
+
136
+ search_str = "?"
137
+
138
+ if connector_keys:
139
+ search_str += "connector_keys=" + ','.join(connector_keys)
140
+ if metric_keys or location_keys or tags:
141
+ search_str += '&'
142
+
143
+ if metric_keys:
144
+ search_str += "metric_keys=" + ','.join(metric_keys)
145
+ if location_keys or tags:
146
+ search_str += '&'
147
+
148
+ if location_keys:
149
+ search_str += "location_keys=" + ','.join(location_keys)
150
+ if tags:
151
+ search_str += '&'
152
+
153
+ if tags:
154
+ search_str += "tags=" + ','.join(tags)
155
+
156
+ return search_str
@@ -6,4 +6,3 @@ Define the callbacks for the settings pages.
6
6
  """
7
7
 
8
8
  import meerschaum.api.dash.callbacks.settings.password_reset
9
- import meerschaum.api.dash.callbacks.settings.tokens
@@ -163,7 +163,7 @@ def register_token_click(
163
163
  token = Token(
164
164
  label=(name or None),
165
165
  user=get_user_from_session(session_id),
166
- expiration=(datetime.fromisoformat(f"{expiration}T00:00:00Z") if expiration is not None else None),
166
+ expiration=(datetime.fromisoformat(f"{expiration}") if expiration is not None else None),
167
167
  )
168
168
  return False, True, build_tokens_register_output_modal(token)
169
169
 
@@ -98,7 +98,7 @@ def build_job_card(
98
98
  html.B(
99
99
  html.A(
100
100
  "🔗 " + job.name,
101
- href=f"/dash/job/{job.name}",
101
+ href=f"/dash/jobs/{job.name}",
102
102
  target="_blank",
103
103
  style={
104
104
  'color': 'white',
@@ -9,7 +9,8 @@ import meerschaum.api.dash.pages.error
9
9
  import meerschaum.api.dash.pages.login
10
10
  import meerschaum.api.dash.pages.dashboard
11
11
  import meerschaum.api.dash.pages.plugins
12
+ import meerschaum.api.dash.pages.tokens
12
13
  import meerschaum.api.dash.pages.register
13
14
  import meerschaum.api.dash.pages.pipes
14
- import meerschaum.api.dash.pages.job
15
+ import meerschaum.api.dash.pages.jobs
15
16
  import meerschaum.api.dash.pages.settings
@@ -11,11 +11,14 @@ from meerschaum.utils.packages import import_html, import_dcc
11
11
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
12
12
  import dash_bootstrap_components as dbc
13
13
 
14
- from meerschaum.api.dash.components import download_logs, refresh_jobs_interval
14
+ from meerschaum.api.dash.components import download_logs, refresh_jobs_interval, pages_navbar
15
15
 
16
- layout = dbc.Container([
17
- dcc.Location('job-location'),
18
- html.Div(id='job-output-div'),
19
- download_logs,
20
- refresh_jobs_interval,
21
- ])
16
+ layout = [
17
+ pages_navbar,
18
+ dbc.Container([
19
+ dcc.Location('job-location'),
20
+ html.Div(id='job-output-div'),
21
+ download_logs,
22
+ refresh_jobs_interval,
23
+ ]),
24
+ ]
@@ -7,16 +7,17 @@ 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
10
+ from meerschaum.api.dash.components import download_dataframe, pages_navbar, navbar
11
11
 
12
12
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
13
13
  import dash_bootstrap_components as dbc
14
14
 
15
+
15
16
  layout = [
16
17
  pages_navbar,
18
+ dcc.Location('pipes-location'),
19
+ download_dataframe,
17
20
  dbc.Container([
18
- dcc.Location('pipes-location'),
19
- download_dataframe,
20
21
  html.Div(id='pipe-output-div'),
21
22
  ])
22
23
  ]
@@ -6,4 +6,3 @@ Define the layouts for the `settings` pages.
6
6
  """
7
7
 
8
8
  import meerschaum.api.dash.pages.settings.password_reset
9
- import meerschaum.api.dash.pages.settings.tokens
@@ -8,16 +8,13 @@ Define the tokens page layout.
8
8
  import dash_bootstrap_components as dbc
9
9
  import dash.html as html
10
10
  import dash.dcc as dcc
11
- from meerschaum.plugins import web_page
12
11
  from meerschaum._internal.static import STATIC_CONFIG
12
+ from meerschaum.api.dash.components import pages_navbar
13
13
 
14
14
 
15
- @web_page('tokens', login_required=True, page_group='Settings')
16
- def page_layout():
17
- """
18
- Return the layout for the tokens page.
19
- """
20
- return dbc.Container([
15
+ layout = [
16
+ pages_navbar,
17
+ dbc.Container([
21
18
  html.Br(),
22
19
  html.H3('Tokens'),
23
20
  html.Div(id="tokens-alert-div"),
@@ -52,4 +49,5 @@ def page_layout():
52
49
  style={'text-align': 'right'},
53
50
  ),
54
51
  html.Div(id='tokens-output-div'),
55
- ])
52
+ ]),
53
+ ]
@@ -12,6 +12,7 @@ import shlex
12
12
  from textwrap import dedent
13
13
  from urllib.parse import urlencode
14
14
 
15
+ from meerschaum.utils import fetch_pipes_keys
15
16
  from meerschaum.utils.typing import List, Optional, Dict, Any, Tuple, Union
16
17
  from meerschaum.utils.misc import string_to_dict
17
18
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html, import_pandas
@@ -817,3 +818,133 @@ def get_backtrack_text(
817
818
  json_text = '[]'
818
819
 
819
820
  return json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
821
+
822
+
823
+ def build_pipes_dropdown_keys_row(
824
+ connector_keys: List[str],
825
+ metric_keys: List[str],
826
+ location_keys: List[str],
827
+ tags: List[str],
828
+ pipes: List[mrsm.Pipe],
829
+ instance_connector: mrsm.connectors.InstanceConnector,
830
+ ) -> dbc.Row:
831
+ """
832
+ Return the dropdown keys row for the dedicated pipes page.
833
+ """
834
+ ck_alone = connector_keys and not any([str(x) for x in (tags + metric_keys + location_keys)])
835
+ mk_alone = metric_keys and not any([str(x) for x in (connector_keys + tags + location_keys)])
836
+ lk_alone = location_keys and not any([str(x) for x in (connector_keys + metric_keys + tags)])
837
+ all_keys = fetch_pipes_keys('registered', instance_connector)
838
+ return dbc.Row(
839
+ [
840
+ dbc.Col(
841
+ html.Div(
842
+ [
843
+ dcc.Dropdown(
844
+ id='pipes-connector-keys-dropdown',
845
+ options=(
846
+ sorted(list({pipe.connector_keys for pipe in pipes}))
847
+ if not ck_alone
848
+ else sorted(list({ck for ck, _, _ in all_keys}))
849
+ ),
850
+ value=[str(ck) for ck in connector_keys],
851
+ placeholder='Connectors',
852
+ multi=True,
853
+ ),
854
+ ],
855
+ className='dbc_dark',
856
+ ),
857
+ lg=4,
858
+ md=12,
859
+ sm=12,
860
+ ),
861
+ dbc.Col(
862
+ html.Div(
863
+ [
864
+ dcc.Dropdown(
865
+ id='pipes-metric-keys-dropdown',
866
+ options=(
867
+ sorted(list({pipe.metric_key for pipe in pipes}))
868
+ if not mk_alone
869
+ else sorted(list({mk for _, mk, _ in all_keys}))
870
+ ),
871
+ value=[str(mk) for mk in metric_keys],
872
+ placeholder='Metrics',
873
+ multi=True,
874
+ ),
875
+ ],
876
+ className='dbc_dark'
877
+ ),
878
+ lg=4,
879
+ md=12,
880
+ sm=12,
881
+ ),
882
+ dbc.Col(
883
+ html.Div(
884
+ [
885
+ dcc.Dropdown(
886
+ id='pipes-location-keys-dropdown',
887
+ options=(
888
+ sorted(list({str(pipe.location_key) for pipe in pipes}))
889
+ if not lk_alone
890
+ else sorted(list({str(lk) for _, _, lk in all_keys}))
891
+ ),
892
+ value=[str(lk) for lk in location_keys],
893
+ placeholder='Locations',
894
+ multi=True,
895
+ ),
896
+ ],
897
+ className='dbc_dark'
898
+ ),
899
+ lg=4,
900
+ md=12,
901
+ sm=12,
902
+ ),
903
+ ] ### end of filters row children
904
+ )
905
+
906
+
907
+ def build_pipes_tags_dropdown(
908
+ connector_keys: List[str],
909
+ metric_keys: List[str],
910
+ location_keys: List[str],
911
+ tags: List[str],
912
+ instance: str,
913
+ ) -> html.Div:
914
+ """
915
+ Build the tags dropdown for the dedicated pipes page.
916
+ """
917
+ _tags_alone = tags and not any([str(x) for x in (connector_keys + metric_keys + location_keys)])
918
+ _tags_pipes = mrsm.get_pipes(
919
+ connector_keys=connector_keys,
920
+ metric_keys=metric_keys,
921
+ location_keys=location_keys,
922
+ tags=tags,
923
+ instance=instance,
924
+ as_tags_dict=True,
925
+ )
926
+
927
+ _all_tags = list(
928
+ mrsm.get_pipes(
929
+ instance=instance,
930
+ as_tags_dict=True,
931
+ )
932
+ ) if _tags_alone else []
933
+
934
+ tags_options = [
935
+ str(tag)
936
+ for tag in (_all_tags if _tags_alone else _tags_pipes)
937
+ ]
938
+
939
+ return html.Div(
940
+ dcc.Dropdown(
941
+ id='pipes-tags-dropdown',
942
+ options=tags_options,
943
+ value=tags,
944
+ placeholder='Tags',
945
+ multi=True,
946
+ searchable=True,
947
+ ),
948
+ className="dbc_dark",
949
+ id="pipes-tags-dropdown-div",
950
+ )