meerschaum 3.0.0rc2__py3-none-any.whl → 3.0.0rc4__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 (47) 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/_events.py +5 -0
  6. meerschaum/api/dash/callbacks/__init__.py +1 -0
  7. meerschaum/api/dash/callbacks/dashboard.py +93 -115
  8. meerschaum/api/dash/callbacks/jobs.py +11 -5
  9. meerschaum/api/dash/callbacks/pipes.py +194 -14
  10. meerschaum/api/dash/callbacks/settings/__init__.py +0 -1
  11. meerschaum/api/dash/callbacks/{settings/tokens.py → tokens.py} +3 -2
  12. meerschaum/api/dash/components.py +6 -7
  13. meerschaum/api/dash/jobs.py +1 -1
  14. meerschaum/api/dash/keys.py +17 -1
  15. meerschaum/api/dash/pages/__init__.py +2 -1
  16. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  17. meerschaum/api/dash/pages/pipes.py +16 -5
  18. meerschaum/api/dash/pages/settings/__init__.py +0 -1
  19. meerschaum/api/dash/pages/{settings/tokens.py → tokens.py} +6 -8
  20. meerschaum/api/dash/pipes.py +219 -3
  21. meerschaum/api/dash/tokens.py +27 -30
  22. meerschaum/config/_default.py +5 -4
  23. meerschaum/config/_paths.py +1 -0
  24. meerschaum/config/_version.py +1 -1
  25. meerschaum/connectors/instance/_tokens.py +6 -2
  26. meerschaum/connectors/sql/_SQLConnector.py +14 -0
  27. meerschaum/connectors/sql/_pipes.py +63 -23
  28. meerschaum/connectors/sql/tables/__init__.py +254 -122
  29. meerschaum/core/Pipe/__init__.py +17 -1
  30. meerschaum/core/Pipe/_attributes.py +5 -2
  31. meerschaum/core/Token/_Token.py +1 -1
  32. meerschaum/plugins/bootstrap.py +508 -3
  33. meerschaum/utils/_get_pipes.py +31 -5
  34. meerschaum/utils/dataframe.py +8 -2
  35. meerschaum/utils/dtypes/__init__.py +2 -3
  36. meerschaum/utils/dtypes/sql.py +11 -11
  37. meerschaum/utils/formatting/_pprint.py +1 -0
  38. meerschaum/utils/pipes.py +6 -2
  39. meerschaum/utils/sql.py +1 -1
  40. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/METADATA +1 -1
  41. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/RECORD +47 -47
  42. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/WHEEL +0 -0
  43. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/entry_points.txt +0 -0
  44. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/licenses/LICENSE +0 -0
  45. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/licenses/NOTICE +0 -0
  46. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.dist-info}/top_level.txt +0 -0
  47. {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc4.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)
meerschaum/api/_events.py CHANGED
@@ -98,6 +98,11 @@ async def startup():
98
98
  await shutdown()
99
99
  os._exit(1)
100
100
 
101
+ conn = get_api_connector()
102
+ if conn.type == 'sql':
103
+ from meerschaum.connectors.sql.tables import get_tables
104
+ _ = get_tables(conn, refresh=True, create=True, debug=debug)
105
+
101
106
  start_check_jobs_thread()
102
107
 
103
108
 
@@ -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,17 +498,9 @@ 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
- Output('connector-keys-dropdown', 'value'),
500
501
  Output('metric-keys-dropdown', 'options'),
501
- Output('metric-keys-list', 'children'),
502
- Output('metric-keys-dropdown', 'value'),
503
502
  Output('location-keys-dropdown', 'options'),
504
- Output('location-keys-list', 'children'),
505
- Output('location-keys-dropdown', 'value'),
506
503
  Output('tags-dropdown', 'options'),
507
- Output('tags-list', 'children'),
508
- Output('tags-dropdown', 'value'),
509
504
  Output('instance-select', 'value'),
510
505
  Output('instance-alert-div', 'children'),
511
506
  Input('connector-keys-dropdown', 'value'),
@@ -547,15 +542,6 @@ def update_keys_options(
547
542
  except Exception as e:
548
543
  instance_alerts += [alert_from_success_tuple((False, str(e)))]
549
544
 
550
- ### Update the keys filters.
551
- if connector_keys is None:
552
- connector_keys = []
553
- if metric_keys is None:
554
- metric_keys = []
555
- if location_keys is None:
556
- location_keys = []
557
- if tags is None:
558
- tags = []
559
545
  num_filter = 0
560
546
  if connector_keys:
561
547
  num_filter += 1
@@ -566,10 +552,6 @@ def update_keys_options(
566
552
  if tags:
567
553
  num_filter += 1
568
554
 
569
- _ck_filter = connector_keys
570
- _mk_filter = metric_keys
571
- _lk_filter = location_keys
572
- _tags_filter = tags
573
555
  _ck_alone = (connector_keys and num_filter == 1) or instance_click
574
556
  _mk_alone = (metric_keys and num_filter == 1) or instance_click
575
557
  _lk_alone = (location_keys and num_filter == 1) or instance_click
@@ -582,109 +564,95 @@ def update_keys_options(
582
564
  _keys = fetch_pipes_keys(
583
565
  'registered',
584
566
  get_web_connector(ctx.states),
585
- connector_keys=_ck_filter,
586
- metric_keys=_mk_filter,
587
- location_keys=_lk_filter,
588
- tags=_tags_filter,
567
+ connector_keys=connector_keys,
568
+ metric_keys=metric_keys,
569
+ location_keys=location_keys,
570
+ tags=tags,
589
571
  )
590
572
  _tags_pipes = mrsm.get_pipes(
591
- connector_keys=_ck_filter,
592
- metric_keys=_mk_filter,
593
- location_keys=_lk_filter,
594
- tags=_tags_filter,
573
+ connector_keys=connector_keys,
574
+ metric_keys=metric_keys,
575
+ location_keys=location_keys,
576
+ tags=tags,
595
577
  instance=get_web_connector(ctx.states),
596
578
  as_tags_dict=True,
597
579
  )
598
580
  _all_tags = list(
599
- mrsm.get_pipes(
600
- instance=get_web_connector(ctx.states),
601
- as_tags_dict=True,
602
- )
581
+ set(
582
+ mrsm.get_pipes(
583
+ instance=get_web_connector(ctx.states),
584
+ as_tags_dict=True,
585
+ )
586
+ ).union(tags or [])
603
587
  ) if _tags_alone else []
604
588
  except Exception as e:
605
589
  instance_alerts += [alert_from_success_tuple((False, str(e)))]
606
- _all_keys, _keys = [], []
607
- _connectors_options = []
608
- _metrics_options = []
609
- _locations_options = []
610
- _tags_options = []
611
-
612
- _seen_keys = {'ck' : set(), 'mk' : set(), 'lk' : set(), 'tags': set()}
613
-
614
- def add_options(options, keys, key_type):
615
- for ck, mk, lk in keys:
616
- k = locals()[key_type]
617
- if k not in _seen_keys[key_type]:
618
- _k = 'None' if k in (None, '[None]', 'None', 'null') else k
619
- options.append({'label': _k, 'value': _k})
620
- _seen_keys[key_type].add(k)
621
-
622
- add_options(_connectors_options, _all_keys if _ck_alone else _keys, 'ck')
623
- add_options(_metrics_options, _all_keys if _mk_alone else _keys, 'mk')
624
- add_options(_locations_options, _all_keys if _lk_alone else _keys, 'lk')
625
-
626
- _tags_options = [
627
- {'label': tag, 'value': tag}
628
- for tag in (_all_tags if _tags_alone else _tags_pipes)
629
- ]
590
+ _all_keys, _all_tags, _keys = [], [], []
591
+
592
+ connectors_options = sorted(
593
+ list(
594
+ set(
595
+ keys_tuple[0] for keys_tuple in (_all_keys if _ck_alone else _keys)
596
+ ).union(set(connector_keys or []))
597
+ ),
598
+ key=(lambda x: str(x).lower()),
599
+ )
600
+ metrics_options = sorted(
601
+ list(
602
+ set(
603
+ keys_tuple[1] for keys_tuple in (_all_keys if _mk_alone else _keys)
604
+ ).union(set(metric_keys or []))
605
+ ),
606
+ key=(lambda x: str(x).lower()),
607
+ )
608
+ locations_options = sorted(
609
+ list(
610
+ set(
611
+ (
612
+ str(keys_tuple[2])
613
+ for keys_tuple in (_all_keys if _lk_alone else _keys)
614
+ )
615
+ ).union(set((str(_lk) for _lk in (location_keys or []))))
616
+ ),
617
+ key=(lambda x: str(x).lower()),
618
+ )
619
+
620
+ tags_options = sorted(
621
+ list(
622
+ set(
623
+ (_all_tags if _tags_alone else _tags_pipes)
624
+ ).union(set(tags or []))
625
+ ),
626
+ key=(lambda x: str(x).lower()),
627
+ )
630
628
 
631
- _connectors_options.sort(key=lambda x: str(x).lower())
632
- _metrics_options.sort(key=lambda x: str(x).lower())
633
- _locations_options.sort(key=lambda x: str(x).lower())
634
- _tags_options.sort(key=lambda x: str(x).lower())
635
- connector_keys = [
636
- ck
637
- for ck in connector_keys
638
- if ck in [
639
- _ck['value']
640
- for _ck in _connectors_options
641
- ]
642
- ]
643
- metric_keys = [
644
- mk
645
- for mk in metric_keys
646
- if mk in [
647
- _mk['value']
648
- for _mk in _metrics_options
649
- ]
650
- ]
651
- location_keys = [
652
- lk
653
- for lk in location_keys
654
- if lk in [
655
- _lk['value']
656
- for _lk in _locations_options
657
- ]
658
- ]
659
- tags = [
660
- tag
661
- for tag in tags
662
- if tag in [
663
- _tag['value']
664
- for _tag in _tags_options
665
- ]
666
- ]
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
629
  return (
672
- _connectors_options,
673
- _connectors_datalist,
674
- connector_keys,
675
- _metrics_options,
676
- _metrics_datalist,
677
- metric_keys,
678
- _locations_options,
679
- _locations_datalist,
680
- location_keys,
681
- _tags_options,
682
- _tags_datalist,
683
- tags,
630
+ connectors_options,
631
+ metrics_options,
632
+ locations_options,
633
+ tags_options,
684
634
  (instance_keys if update_instance_keys else dash.no_update),
685
635
  instance_alerts,
686
636
  )
687
637
 
638
+
639
+ @dash_app.callback(
640
+ Output('connector-keys-dropdown', 'value'),
641
+ Output('metric-keys-dropdown', 'value'),
642
+ Output('location-keys-dropdown', 'value'),
643
+ Output('tags-dropdown', 'value'),
644
+ Input('clear-all-keys-button', 'n_clicks'),
645
+ prevent_initial_call=True,
646
+ )
647
+ def clear_all_keys_button_click(n_clicks):
648
+ """
649
+ Clear the keys dropdowns when the `Clear all` button is clicked.
650
+ """
651
+ if not n_clicks:
652
+ raise PreventUpdate
653
+
654
+ return [], [], [], []
655
+
688
656
  dash_app.clientside_callback(
689
657
  """
690
658
  function(
@@ -856,6 +824,7 @@ dash_app.clientside_callback(
856
824
  @dash_app.callback(
857
825
  Output("download-dataframe-csv", "data"),
858
826
  Input({'type': 'pipe-download-csv-button', 'index': ALL}, 'n_clicks'),
827
+ prevent_initial_call=True,
859
828
  )
860
829
  def download_pipe_csv(n_clicks):
861
830
  """
@@ -885,6 +854,7 @@ def download_pipe_csv(n_clicks):
885
854
  Output({'type': 'pipe-accordion', 'index': MATCH}, 'children'),
886
855
  Input({'type': 'pipe-accordion', 'index': MATCH}, 'active_item'),
887
856
  State('session-store', 'data'),
857
+ prevent_initial_call=True,
888
858
  )
889
859
  def update_pipe_accordion(item, session_store_data):
890
860
  """
@@ -908,7 +878,8 @@ def update_pipe_accordion(item, session_store_data):
908
878
  @dash_app.callback(
909
879
  Output({'type': 'update-parameters-success-div', 'index': MATCH}, 'children'),
910
880
  Input({'type': 'update-parameters-button', 'index': MATCH}, 'n_clicks'),
911
- State({'type': 'parameters-editor', 'index': MATCH}, 'value')
881
+ State({'type': 'parameters-editor', 'index': MATCH}, 'value'),
882
+ prevent_initial_call=True,
912
883
  )
913
884
  def update_pipe_parameters_click(n_clicks, parameters_editor_text):
914
885
  if not n_clicks:
@@ -942,6 +913,7 @@ def update_pipe_parameters_click(n_clicks, parameters_editor_text):
942
913
  Output({'type': 'update-sql-success-div', 'index': MATCH}, 'children'),
943
914
  Input({'type': 'update-sql-button', 'index': MATCH}, 'n_clicks'),
944
915
  State({'type': 'sql-editor', 'index': MATCH}, 'value'),
916
+ prevent_initial_call=True,
945
917
  )
946
918
  def update_pipe_sql_click(n_clicks, sql_editor_text):
947
919
  if not n_clicks:
@@ -968,7 +940,8 @@ def update_pipe_sql_click(n_clicks, sql_editor_text):
968
940
  @dash_app.callback(
969
941
  Output({'type': 'sync-success-div', 'index': MATCH}, 'children'),
970
942
  Input({'type': 'update-sync-button', 'index': MATCH}, 'n_clicks'),
971
- State({'type': 'sync-editor', 'index': MATCH}, 'value')
943
+ State({'type': 'sync-editor', 'index': MATCH}, 'value'),
944
+ prevent_initial_call=True,
972
945
  )
973
946
  def sync_documents_click(n_clicks, sync_editor_text):
974
947
  if not n_clicks:
@@ -1014,6 +987,7 @@ def sync_documents_click(n_clicks, sync_editor_text):
1014
987
  State({'type': 'limit-input', 'index': MATCH}, 'value'),
1015
988
  State({'type': 'query-data-begin-input', 'index': MATCH}, 'value'),
1016
989
  State({'type': 'query-data-end-input', 'index': MATCH}, 'value'),
990
+ prevent_initial_call=True,
1017
991
  )
1018
992
  def query_data_click(n_clicks, query_editor_text, limit_value, begin, end):
1019
993
  triggered = dash.callback_context.triggered
@@ -1130,6 +1104,7 @@ def toggle_navbar_collapse(n_clicks: Optional[int], is_open: bool) -> bool:
1130
1104
  Output('session-store', 'data'),
1131
1105
  Input("sign-out-button", "n_clicks"),
1132
1106
  State('session-store', 'data'),
1107
+ prevent_initial_call=True,
1133
1108
  )
1134
1109
  def sign_out_button_click(
1135
1110
  n_clicks: Optional[int],
@@ -1150,6 +1125,7 @@ def sign_out_button_click(
1150
1125
  Output({'type': 'parameters-editor', 'index': MATCH}, 'value'),
1151
1126
  Input({'type': 'parameters-as-yaml-button', 'index': MATCH}, 'n_clicks'),
1152
1127
  Input({'type': 'parameters-as-json-button', 'index': MATCH}, 'n_clicks'),
1128
+ prevent_initial_call=True,
1153
1129
  )
1154
1130
  def parameters_as_yaml_or_json_click(
1155
1131
  yaml_n_clicks: Optional[int],
@@ -1178,6 +1154,7 @@ def parameters_as_yaml_or_json_click(
1178
1154
  Output({'type': 'sync-editor', 'index': MATCH}, 'value'),
1179
1155
  Input({'type': 'sync-as-json-button', 'index': MATCH}, 'n_clicks'),
1180
1156
  Input({'type': 'sync-as-lines-button', 'index': MATCH}, 'n_clicks'),
1157
+ prevent_initial_call=True,
1181
1158
  )
1182
1159
  def sync_as_json_or_lines_click(
1183
1160
  json_n_clicks: Optional[int],
@@ -1206,6 +1183,7 @@ def sync_as_json_or_lines_click(
1206
1183
  Output('pages-offcanvas', 'children'),
1207
1184
  Input('logo-img', 'n_clicks'),
1208
1185
  State('pages-offcanvas', 'is_open'),
1186
+ prevent_initial_call=True,
1209
1187
  )
1210
1188
  def toggle_pages_offcanvas(n_clicks: Optional[int], is_open: bool):
1211
1189
  """
@@ -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():