meerschaum 2.9.4__py3-none-any.whl → 3.0.0rc1__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 (154) hide show
  1. meerschaum/__init__.py +5 -2
  2. meerschaum/_internal/__init__.py +1 -0
  3. meerschaum/_internal/arguments/_parse_arguments.py +4 -4
  4. meerschaum/_internal/arguments/_parser.py +17 -1
  5. meerschaum/_internal/entry.py +6 -6
  6. meerschaum/_internal/shell/Shell.py +1 -1
  7. meerschaum/_internal/static.py +372 -0
  8. meerschaum/actions/api.py +12 -2
  9. meerschaum/actions/bootstrap.py +7 -7
  10. meerschaum/actions/edit.py +142 -18
  11. meerschaum/actions/register.py +137 -6
  12. meerschaum/actions/show.py +117 -29
  13. meerschaum/actions/stop.py +4 -1
  14. meerschaum/actions/sync.py +1 -1
  15. meerschaum/actions/tag.py +9 -8
  16. meerschaum/api/__init__.py +9 -2
  17. meerschaum/api/_events.py +39 -2
  18. meerschaum/api/_oauth2.py +118 -8
  19. meerschaum/api/_tokens.py +102 -0
  20. meerschaum/api/dash/__init__.py +0 -1
  21. meerschaum/api/dash/callbacks/custom.py +2 -2
  22. meerschaum/api/dash/callbacks/dashboard.py +133 -18
  23. meerschaum/api/dash/callbacks/plugins.py +0 -1
  24. meerschaum/api/dash/callbacks/register.py +1 -1
  25. meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
  26. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  27. meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
  28. meerschaum/api/dash/components.py +30 -8
  29. meerschaum/api/dash/keys.py +19 -93
  30. meerschaum/api/dash/pages/dashboard.py +1 -20
  31. meerschaum/api/dash/pages/settings/__init__.py +1 -0
  32. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  33. meerschaum/api/dash/pages/settings/tokens.py +55 -0
  34. meerschaum/api/dash/pipes.py +156 -58
  35. meerschaum/api/dash/sessions.py +12 -0
  36. meerschaum/api/dash/tokens.py +606 -0
  37. meerschaum/api/dash/websockets.py +1 -1
  38. meerschaum/api/dash/webterm.py +4 -0
  39. meerschaum/api/models/__init__.py +23 -3
  40. meerschaum/api/models/_actions.py +22 -0
  41. meerschaum/api/models/_pipes.py +85 -7
  42. meerschaum/api/models/_tokens.py +81 -0
  43. meerschaum/api/resources/static/css/dash.css +16 -0
  44. meerschaum/api/resources/templates/termpage.html +12 -0
  45. meerschaum/api/routes/__init__.py +1 -0
  46. meerschaum/api/routes/_actions.py +3 -4
  47. meerschaum/api/routes/_connectors.py +3 -7
  48. meerschaum/api/routes/_jobs.py +14 -35
  49. meerschaum/api/routes/_login.py +49 -12
  50. meerschaum/api/routes/_misc.py +5 -10
  51. meerschaum/api/routes/_pipes.py +134 -111
  52. meerschaum/api/routes/_plugins.py +38 -28
  53. meerschaum/api/routes/_tokens.py +236 -0
  54. meerschaum/api/routes/_users.py +47 -35
  55. meerschaum/api/routes/_version.py +3 -3
  56. meerschaum/config/__init__.py +43 -20
  57. meerschaum/config/_default.py +32 -5
  58. meerschaum/config/_edit.py +28 -24
  59. meerschaum/config/_environment.py +1 -1
  60. meerschaum/config/_patch.py +6 -6
  61. meerschaum/config/_paths.py +5 -1
  62. meerschaum/config/_read_config.py +65 -34
  63. meerschaum/config/_sync.py +6 -3
  64. meerschaum/config/_version.py +1 -1
  65. meerschaum/config/stack/__init__.py +24 -5
  66. meerschaum/config/static.py +18 -0
  67. meerschaum/connectors/_Connector.py +10 -4
  68. meerschaum/connectors/__init__.py +4 -20
  69. meerschaum/connectors/api/_APIConnector.py +34 -6
  70. meerschaum/connectors/api/_actions.py +2 -2
  71. meerschaum/connectors/api/_jobs.py +1 -1
  72. meerschaum/connectors/api/_login.py +33 -7
  73. meerschaum/connectors/api/_misc.py +2 -2
  74. meerschaum/connectors/api/_pipes.py +15 -14
  75. meerschaum/connectors/api/_plugins.py +2 -2
  76. meerschaum/connectors/api/_request.py +1 -1
  77. meerschaum/connectors/api/_tokens.py +146 -0
  78. meerschaum/connectors/api/_users.py +70 -58
  79. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  80. meerschaum/connectors/instance/__init__.py +10 -0
  81. meerschaum/connectors/instance/_pipes.py +442 -0
  82. meerschaum/connectors/instance/_plugins.py +151 -0
  83. meerschaum/connectors/instance/_tokens.py +296 -0
  84. meerschaum/connectors/instance/_users.py +181 -0
  85. meerschaum/connectors/parse.py +4 -1
  86. meerschaum/connectors/sql/_SQLConnector.py +8 -5
  87. meerschaum/connectors/sql/_cli.py +12 -11
  88. meerschaum/connectors/sql/_create_engine.py +6 -154
  89. meerschaum/connectors/sql/_fetch.py +2 -18
  90. meerschaum/connectors/sql/_pipes.py +42 -31
  91. meerschaum/connectors/sql/_plugins.py +29 -0
  92. meerschaum/connectors/sql/_sql.py +9 -2
  93. meerschaum/connectors/sql/_users.py +29 -2
  94. meerschaum/connectors/sql/tables/__init__.py +1 -1
  95. meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
  96. meerschaum/connectors/valkey/_pipes.py +9 -10
  97. meerschaum/connectors/valkey/_plugins.py +2 -26
  98. meerschaum/core/Pipe/__init__.py +31 -14
  99. meerschaum/core/Pipe/_attributes.py +156 -58
  100. meerschaum/core/Pipe/_bootstrap.py +54 -24
  101. meerschaum/core/Pipe/_data.py +41 -1
  102. meerschaum/core/Pipe/_dtypes.py +29 -14
  103. meerschaum/core/Pipe/_edit.py +12 -4
  104. meerschaum/core/Pipe/_show.py +5 -5
  105. meerschaum/core/Pipe/_sync.py +48 -53
  106. meerschaum/core/Pipe/_verify.py +1 -1
  107. meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
  108. meerschaum/core/Plugin/__init__.py +1 -1
  109. meerschaum/core/Token/_Token.py +221 -0
  110. meerschaum/core/Token/__init__.py +12 -0
  111. meerschaum/core/User/_User.py +34 -8
  112. meerschaum/core/User/__init__.py +9 -1
  113. meerschaum/core/__init__.py +1 -0
  114. meerschaum/jobs/_Job.py +3 -2
  115. meerschaum/jobs/__init__.py +3 -2
  116. meerschaum/jobs/systemd.py +1 -1
  117. meerschaum/models/__init__.py +35 -0
  118. meerschaum/models/pipes.py +247 -0
  119. meerschaum/models/tokens.py +38 -0
  120. meerschaum/models/users.py +26 -0
  121. meerschaum/plugins/__init__.py +22 -7
  122. meerschaum/plugins/bootstrap.py +2 -1
  123. meerschaum/utils/_get_pipes.py +68 -27
  124. meerschaum/utils/daemon/Daemon.py +2 -1
  125. meerschaum/utils/daemon/__init__.py +30 -2
  126. meerschaum/utils/dataframe.py +96 -15
  127. meerschaum/utils/dtypes/__init__.py +93 -21
  128. meerschaum/utils/dtypes/sql.py +44 -0
  129. meerschaum/utils/formatting/__init__.py +1 -1
  130. meerschaum/utils/formatting/_pipes.py +5 -4
  131. meerschaum/utils/formatting/_shell.py +11 -9
  132. meerschaum/utils/misc.py +237 -80
  133. meerschaum/utils/packages/__init__.py +3 -6
  134. meerschaum/utils/packages/_packages.py +34 -32
  135. meerschaum/utils/pipes.py +181 -0
  136. meerschaum/utils/process.py +1 -1
  137. meerschaum/utils/prompt.py +3 -1
  138. meerschaum/utils/schedule.py +1 -0
  139. meerschaum/utils/sql.py +115 -39
  140. meerschaum/utils/typing.py +1 -4
  141. meerschaum/utils/venv/_Venv.py +2 -2
  142. meerschaum/utils/venv/__init__.py +5 -7
  143. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
  144. meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
  145. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
  146. meerschaum/api/models/_interfaces.py +0 -15
  147. meerschaum/api/models/_locations.py +0 -15
  148. meerschaum/api/models/_metrics.py +0 -15
  149. meerschaum/config/static/__init__.py +0 -186
  150. meerschaum-2.9.4.dist-info/RECORD +0 -263
  151. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
  152. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
  153. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
  154. {meerschaum-2.9.4.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
@@ -18,7 +18,7 @@ from meerschaum.utils.packages import attempt_import, import_dcc, import_html, i
18
18
  from meerschaum.utils.sql import get_pd_type
19
19
  from meerschaum.utils.yaml import yaml
20
20
  from meerschaum.utils.warnings import warn
21
- from meerschaum.utils.dataframe import to_json
21
+ from meerschaum.utils.dataframe import to_json, to_simple_lines
22
22
  from meerschaum.connectors.sql._fetch import get_pipe_query
23
23
  from meerschaum.api import CHECK_UPDATE
24
24
  from meerschaum.api.dash import debug, _get_pipes
@@ -58,28 +58,20 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
58
58
 
59
59
  def keys_from_state(
60
60
  state: Dict[str, Any],
61
- with_params: bool = False
61
+ with_tags: bool = False,
62
62
  ) -> Union[
63
63
  Tuple[List[str], List[str], List[str]],
64
- Tuple[List[str], List[str], List[str], str],
64
+ Tuple[List[str], List[str], List[str], List[str]],
65
65
  ]:
66
66
  """
67
67
  Read the current state and return the selected keys lists.
68
68
  """
69
69
  _filters = {
70
- 'ck' : state.get(f"connector-keys-{state['pipes-filter-tabs.active_tab']}.value", None),
71
- 'mk' : state.get(f"metric-keys-{state['pipes-filter-tabs.active_tab']}.value", None),
72
- 'lk' : state.get(f"location-keys-{state['pipes-filter-tabs.active_tab']}.value", None),
70
+ 'ck': state.get("connector-keys-dropdown.value", None),
71
+ 'mk': state.get("metric-keys-dropdown.value", None),
72
+ 'lk': state.get("location-keys-dropdown.value", None),
73
+ 'tags': state.get("tags-dropdown.value", None),
73
74
  }
74
- if state['pipes-filter-tabs.active_tab'] == 'input':
75
- try:
76
- # params = string_to_dict(state['params-textarea.value'])
77
- params = string_to_dict(state['search-parameters-editor.value'])
78
- except Exception:
79
- params = None
80
- else:
81
- params = None
82
-
83
75
  for k in _filters:
84
76
  _filters[k] = [] if _filters[k] is None else _filters[k]
85
77
  if not isinstance(_filters[k], list):
@@ -89,8 +81,8 @@ def keys_from_state(
89
81
  print(e)
90
82
  _filters[k] = []
91
83
  keys = [_filters['ck'], _filters['mk'], _filters['lk']]
92
- if with_params:
93
- keys.append(params)
84
+ if with_tags:
85
+ keys.append(_filters['tags'])
94
86
  return tuple(keys)
95
87
 
96
88
 
@@ -98,12 +90,12 @@ def pipes_from_state(
98
90
  state: Dict[str, Any],
99
91
  **kw
100
92
  ):
101
- _ck, _mk, _lk, _params = keys_from_state(state, with_params=True)
93
+ _ck, _mk, _lk, _tags = keys_from_state(state, with_tags=True)
102
94
  try:
103
95
  _pipes = _get_pipes(
104
96
  _ck, _mk, _lk,
105
- params = _params,
106
- mrsm_instance = get_web_connector(state),
97
+ tags=(_tags or []),
98
+ mrsm_instance=get_web_connector(state),
107
99
  **kw
108
100
  )
109
101
  except Exception as e:
@@ -343,6 +335,7 @@ def accordion_items_from_pipe(
343
335
  items_titles['sql'] = '📃 SQL Query'
344
336
  items_titles.update({
345
337
  'recent-data': '🗃️ Recent Data',
338
+ 'query-data': '🔍 Query Data',
346
339
  'sync-data': '📝 Sync Documents',
347
340
  })
348
341
 
@@ -465,7 +458,7 @@ def accordion_items_from_pipe(
465
458
  overview_rows.append(
466
459
  html.Tr([
467
460
  html.Td("Indices" if len(indices_rows) != 1 else "Index"),
468
- html.Td(indices_table),
461
+ html.Td(html.Div(indices_table, style={'overflowX': 'auto'})),
469
462
  ])
470
463
  )
471
464
 
@@ -535,9 +528,9 @@ def accordion_items_from_pipe(
535
528
  ]
536
529
  columns_body = [html.Tbody(columns_rows)]
537
530
  columns_table = dbc.Table(columns_header + columns_body, bordered=False, hover=True)
531
+ items_bodies['columns'] = html.Div(columns_table, style={'overflowX': 'auto'})
538
532
  except Exception:
539
- columns_table = html.P("Could not retrieve columns ― please try again.")
540
- items_bodies['columns'] = columns_table
533
+ items_bodies['columns'] = html.P("Could not retrieve columns ― please try again.")
541
534
 
542
535
  if 'parameters' in active_items:
543
536
  parameters_editor = dash_ace.DashAceEditor(
@@ -578,21 +571,28 @@ def accordion_items_from_pipe(
578
571
  parameters_editor,
579
572
  html.Br(),
580
573
  dbc.Row([
581
- dbc.Col(html.Span(
582
- (
583
- ([update_parameters_button] if authenticated else []) +
584
- [
585
- as_json_button,
586
- as_yaml_button,
587
- ]
588
- )
589
- ), width=4),
590
- dbc.Col([
591
- html.Div(
592
- id={'type': 'update-parameters-success-div', 'index': json.dumps(pipe.meta)}
593
- )
594
- ],
595
- width=True,
574
+ dbc.Col(
575
+ html.Span(
576
+ (
577
+ ([update_parameters_button] if authenticated else []) +
578
+ [
579
+ as_json_button,
580
+ as_yaml_button,
581
+ ]
582
+ )
583
+ ),
584
+ width=4,
585
+ ),
586
+ dbc.Col(
587
+ [
588
+ html.Div(
589
+ id={
590
+ 'type': 'update-parameters-success-div',
591
+ 'index': json.dumps(pipe.meta),
592
+ }
593
+ )
594
+ ],
595
+ width=True,
596
596
  )
597
597
  ]),
598
598
  ]
@@ -664,29 +664,77 @@ def accordion_items_from_pipe(
664
664
  if 'recent-data' in active_items:
665
665
  try:
666
666
  df = pipe.get_backtrack_data(backtrack_minutes=10, limit=10, debug=debug).astype(str)
667
- table = dbc.Table.from_dataframe(df, bordered=False, hover=True)
667
+ table = dbc.Table.from_dataframe(df, bordered=False, hover=True)
668
+ items_bodies['recent-data'] = html.Div(table, style={'overflowX': 'auto'})
668
669
  except Exception:
669
- table = html.P("Could not retrieve recent data.")
670
- items_bodies['recent-data'] = table
670
+ items_bodies['recent-data'] = html.P("Could not retrieve recent data.")
671
+
672
+ if 'query-data' in active_items:
673
+ query_editor = dash_ace.DashAceEditor(
674
+ value='{\n \n}',
675
+ mode='norm',
676
+ tabSize=4,
677
+ theme='twilight',
678
+ id={'type': 'query-editor', 'index': json.dumps(pipe.meta)},
679
+ width='100%',
680
+ height='200px',
681
+ readOnly=False,
682
+ showGutter=True,
683
+ showPrintMargin=False,
684
+ highlightActiveLine=True,
685
+ wrapEnabled=True,
686
+ style={'min-height': '120px'},
687
+ )
688
+ query_data_button = dbc.Button(
689
+ "Query",
690
+ id={'type': 'query-data-button', 'index': json.dumps(pipe.meta)},
691
+ )
692
+
693
+ begin_end_input_group = dbc.InputGroup(
694
+ [
695
+ dbc.Input(
696
+ id={'type': 'query-data-begin-input', 'index': json.dumps(pipe.meta)},
697
+ placeholder="Begin",
698
+ ),
699
+ dbc.Input(
700
+ id={'type': 'query-data-end-input', 'index': json.dumps(pipe.meta)},
701
+ placeholder="End",
702
+ ),
703
+ ],
704
+ size="sm",
705
+ )
706
+
707
+ limit_input = dbc.Input(
708
+ type="number",
709
+ min=0,
710
+ max=100,
711
+ value=10,
712
+ step=1,
713
+ placeholder="Limit",
714
+ id={'type': 'limit-input', 'index': json.dumps(pipe.meta)},
715
+ )
716
+ query_result_div = html.Div(id={'type': 'query-result-div', 'index': json.dumps(pipe.meta)})
717
+
718
+ items_bodies['query-data'] = html.Div([
719
+ query_editor,
720
+ html.Br(),
721
+ dbc.Row(
722
+ [
723
+ dbc.Col([query_data_button], lg=2, md=3, sm=4, xs=6, width=2),
724
+ dbc.Col([begin_end_input_group], lg=6, md=6, sm=4, width=6),
725
+ dbc.Col(html.Div([limit_input, dbc.FormText("Row Limit")]), lg=2, md=3, sm=4, xs=6, width=2),
726
+ ],
727
+ justify="between",
728
+ ),
729
+ dbc.Row([
730
+ dbc.Col([query_result_div], width=True),
731
+ ]),
732
+ ])
671
733
 
672
734
  if 'sync-data' in active_items:
673
- backtrack_df = pipe.get_backtrack_data(debug=debug, limit=1)
674
- try:
675
- json_text = to_json(
676
- backtrack_df,
677
- orient='records',
678
- date_format='iso',
679
- force_ascii=False,
680
- indent=4,
681
- date_unit='us',
682
- ) if backtrack_df is not None else '[]'
683
- except Exception as e:
684
- warn(e)
685
- json_text = '[]'
686
-
687
- json_text = json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
735
+ backtrack_text = get_backtrack_text(pipe)
688
736
  sync_editor = dash_ace.DashAceEditor(
689
- value = json_text,
737
+ value = backtrack_text,
690
738
  mode = 'norm',
691
739
  tabSize = 4,
692
740
  theme = 'twilight',
@@ -700,6 +748,22 @@ def accordion_items_from_pipe(
700
748
  wrapEnabled = True,
701
749
  style = {'min-height': '120px'},
702
750
  )
751
+
752
+ sync_as_json_button = dbc.Button(
753
+ "JSON",
754
+ id={'type': 'sync-as-json-button', 'index': json.dumps(pipe.meta)},
755
+ color='link',
756
+ size='sm',
757
+ style={'text-decoration': 'none', 'margin-left': '10px'},
758
+ )
759
+ sync_as_lines_button = dbc.Button(
760
+ "Lines",
761
+ id={'type': 'sync-as-lines-button', 'index': json.dumps(pipe.meta)},
762
+ color='link',
763
+ size='sm',
764
+ style={'text-decoration': 'none', 'margin-left': '10px'},
765
+ )
766
+
703
767
  update_sync_button = dbc.Button(
704
768
  "Sync",
705
769
  id = {'type': 'update-sync-button', 'index': json.dumps(pipe.meta)},
@@ -709,7 +773,15 @@ def accordion_items_from_pipe(
709
773
  sync_editor,
710
774
  html.Br(),
711
775
  dbc.Row([
712
- dbc.Col([update_sync_button], width=1),
776
+ dbc.Col(html.Span(
777
+ (
778
+ ([update_sync_button] if authenticated else []) +
779
+ [
780
+ sync_as_json_button,
781
+ sync_as_lines_button,
782
+ ]
783
+ )
784
+ ), width=4),
713
785
  dbc.Col([sync_success_div], width=True),
714
786
  ]),
715
787
  ])
@@ -719,3 +791,29 @@ def accordion_items_from_pipe(
719
791
  for item_id, title in items_titles.items()
720
792
  ]
721
793
 
794
+
795
+ def get_backtrack_text(
796
+ pipe: mrsm.Pipe,
797
+ lines: bool = False,
798
+ limit: int = 5,
799
+ ) -> str:
800
+ """
801
+ Return the backtrack documents as text for the sync editor.
802
+ """
803
+ backtrack_df = pipe.get_backtrack_data(debug=debug, limit=limit)
804
+ if lines:
805
+ return to_simple_lines(backtrack_df)
806
+ try:
807
+ json_text = to_json(
808
+ backtrack_df,
809
+ orient='records',
810
+ date_format='iso',
811
+ force_ascii=False,
812
+ indent=4,
813
+ date_unit='us',
814
+ ) if backtrack_df is not None else '[]'
815
+ except Exception as e:
816
+ warn(e)
817
+ json_text = '[]'
818
+
819
+ return json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
@@ -86,6 +86,18 @@ def get_username_from_session(session_id: Optional[str]) -> Union[str, None]:
86
86
  return session_data.get('username', None)
87
87
 
88
88
 
89
+ def get_user_from_session(session_id: Optional[str]) -> Union[User, None]:
90
+ """
91
+ Return a `User` from the current session.
92
+ """
93
+ username = get_username_from_session(session_id)
94
+ if username is None:
95
+ return None
96
+
97
+ conn = get_api_connector()
98
+ return User(username, instance=conn)
99
+
100
+
89
101
  def is_session_active(session_id: Union[str, None]) -> bool:
90
102
  """
91
103
  Return whether a given `session_id` has been set.