meerschaum 2.3.6__py3-none-any.whl → 2.4.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 (94) hide show
  1. meerschaum/_internal/arguments/_parse_arguments.py +2 -5
  2. meerschaum/_internal/docs/index.py +3 -2
  3. meerschaum/_internal/entry.py +13 -7
  4. meerschaum/_internal/shell/Shell.py +38 -44
  5. meerschaum/_internal/term/TermPageHandler.py +2 -3
  6. meerschaum/_internal/term/__init__.py +13 -11
  7. meerschaum/actions/api.py +10 -7
  8. meerschaum/actions/bootstrap.py +38 -11
  9. meerschaum/actions/copy.py +3 -3
  10. meerschaum/actions/delete.py +4 -1
  11. meerschaum/actions/register.py +1 -3
  12. meerschaum/actions/stack.py +24 -19
  13. meerschaum/actions/start.py +38 -40
  14. meerschaum/actions/sync.py +53 -52
  15. meerschaum/api/__init__.py +48 -14
  16. meerschaum/api/_events.py +15 -10
  17. meerschaum/api/_oauth2.py +2 -2
  18. meerschaum/api/_websockets.py +5 -4
  19. meerschaum/api/dash/__init__.py +7 -16
  20. meerschaum/api/dash/callbacks/__init__.py +1 -0
  21. meerschaum/api/dash/callbacks/dashboard.py +52 -58
  22. meerschaum/api/dash/callbacks/jobs.py +15 -16
  23. meerschaum/api/dash/callbacks/login.py +16 -10
  24. meerschaum/api/dash/callbacks/pipes.py +41 -0
  25. meerschaum/api/dash/callbacks/plugins.py +1 -1
  26. meerschaum/api/dash/callbacks/register.py +15 -11
  27. meerschaum/api/dash/components.py +54 -59
  28. meerschaum/api/dash/jobs.py +5 -9
  29. meerschaum/api/dash/pages/__init__.py +1 -0
  30. meerschaum/api/dash/pages/pipes.py +19 -0
  31. meerschaum/api/dash/pipes.py +86 -58
  32. meerschaum/api/dash/plugins.py +6 -4
  33. meerschaum/api/dash/sessions.py +176 -0
  34. meerschaum/api/dash/users.py +3 -41
  35. meerschaum/api/dash/webterm.py +12 -17
  36. meerschaum/api/resources/static/js/terminado.js +1 -1
  37. meerschaum/api/routes/_actions.py +4 -20
  38. meerschaum/api/routes/_jobs.py +8 -7
  39. meerschaum/api/routes/_login.py +4 -4
  40. meerschaum/api/routes/_pipes.py +3 -3
  41. meerschaum/api/routes/_webterm.py +5 -6
  42. meerschaum/config/_default.py +15 -2
  43. meerschaum/config/_version.py +1 -1
  44. meerschaum/config/stack/__init__.py +64 -19
  45. meerschaum/config/static/__init__.py +4 -0
  46. meerschaum/connectors/{Connector.py → _Connector.py} +19 -13
  47. meerschaum/connectors/__init__.py +24 -14
  48. meerschaum/connectors/api/{APIConnector.py → _APIConnector.py} +3 -1
  49. meerschaum/connectors/api/__init__.py +2 -1
  50. meerschaum/connectors/parse.py +18 -16
  51. meerschaum/connectors/poll.py +30 -24
  52. meerschaum/connectors/sql/__init__.py +3 -1
  53. meerschaum/connectors/sql/_pipes.py +172 -197
  54. meerschaum/connectors/sql/_plugins.py +45 -43
  55. meerschaum/connectors/sql/_users.py +46 -38
  56. meerschaum/connectors/valkey/_ValkeyConnector.py +535 -0
  57. meerschaum/connectors/valkey/__init__.py +10 -0
  58. meerschaum/connectors/valkey/_fetch.py +75 -0
  59. meerschaum/connectors/valkey/_pipes.py +844 -0
  60. meerschaum/connectors/valkey/_plugins.py +265 -0
  61. meerschaum/connectors/valkey/_users.py +305 -0
  62. meerschaum/core/Pipe/__init__.py +3 -0
  63. meerschaum/core/Pipe/_attributes.py +1 -2
  64. meerschaum/core/Pipe/_clear.py +16 -13
  65. meerschaum/core/Pipe/_copy.py +106 -0
  66. meerschaum/core/Pipe/_data.py +165 -101
  67. meerschaum/core/Pipe/_drop.py +4 -4
  68. meerschaum/core/Pipe/_dtypes.py +14 -14
  69. meerschaum/core/Pipe/_edit.py +15 -14
  70. meerschaum/core/Pipe/_sync.py +134 -53
  71. meerschaum/core/Pipe/_verify.py +11 -11
  72. meerschaum/core/User/_User.py +14 -12
  73. meerschaum/jobs/_Job.py +1 -6
  74. meerschaum/jobs/__init__.py +7 -2
  75. meerschaum/plugins/_Plugin.py +17 -13
  76. meerschaum/utils/_get_pipes.py +14 -20
  77. meerschaum/utils/dataframe.py +291 -101
  78. meerschaum/utils/dtypes/__init__.py +31 -6
  79. meerschaum/utils/dtypes/sql.py +4 -4
  80. meerschaum/utils/formatting/_shell.py +5 -6
  81. meerschaum/utils/misc.py +3 -3
  82. meerschaum/utils/packages/__init__.py +14 -9
  83. meerschaum/utils/packages/_packages.py +2 -0
  84. meerschaum/utils/schedule.py +1 -0
  85. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/METADATA +7 -1
  86. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/RECORD +93 -84
  87. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/WHEEL +1 -1
  88. meerschaum/api/dash/actions.py +0 -255
  89. /meerschaum/connectors/sql/{SQLConnector.py → _SQLConnector.py} +0 -0
  90. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/LICENSE +0 -0
  91. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/NOTICE +0 -0
  92. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/entry_points.txt +0 -0
  93. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/top_level.txt +0 -0
  94. {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dist-info}/zip-safe +0 -0
@@ -7,27 +7,24 @@ Custom components are defined here.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from meerschaum.utils.venv import Venv
10
+
11
11
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html
12
12
  from meerschaum.utils.typing import SuccessTuple, List
13
13
  from meerschaum.config.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
16
  from meerschaum.api import endpoints, CHECK_UPDATE
17
- from meerschaum.connectors import instance_types
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
20
20
  dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
21
21
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
22
22
  dex = attempt_import('dash_extensions', lazy=False, check_update=CHECK_UPDATE)
23
23
  dash_ace = attempt_import('dash_ace', lazy=False, check_update=CHECK_UPDATE)
24
-
25
- component_ids = {
26
-
27
- }
24
+ _load_builtin_custom_connectors()
28
25
 
29
26
  go_button = dbc.Button('Execute', id='go-button', color='primary', style={'width': '100%'})
30
- test_button = dbc.Button('Test', id='test-button', color='danger', style={'display' : 'none'})
27
+ test_button = dbc.Button('Test', id='test-button', color='danger', style={'display': 'none'})
31
28
  get_items_menu = dbc.DropdownMenu(
32
29
  label='More', id='get-items-menu', children=[
33
30
  dbc.DropdownMenuItem("Plugins", id='get-plugins-button'),
@@ -42,21 +39,21 @@ get_items_menu = dbc.DropdownMenu(
42
39
  )
43
40
  show_pipes_button = dbc.Button(
44
41
  'Pipes',
45
- id = 'get-pipes-button',
46
- color = 'info',
47
- style = {'width': '100%'},
42
+ id='get-pipes-button',
43
+ color='info',
44
+ style={'width': '100%'},
48
45
  )
49
46
  show_jobs_button = dbc.Button(
50
47
  'Jobs',
51
- id = 'get-jobs-button',
52
- color = 'success',
53
- style = {'width': '100%'},
48
+ id='get-jobs-button',
49
+ color='success',
50
+ style={'width': '100%'},
54
51
  )
55
52
  cancel_button = dbc.Button(
56
53
  'Terminal',
57
- id = 'cancel-button',
58
- color = 'dark',
59
- style = {'width': '100%', 'background-color': 'black', 'display': 'none'},
54
+ id='cancel-button',
55
+ color='dark',
56
+ style={'width': '100%', 'background-color': 'black', 'display': 'none'},
60
57
  )
61
58
  bottom_buttons_content = dbc.Card(
62
59
  dbc.CardBody(
@@ -70,28 +67,26 @@ bottom_buttons_content = dbc.Card(
70
67
  )
71
68
  )
72
69
  console_div = html.Div(
73
- id = 'console-div',
74
- children = [html.Pre(get_shell_intro(), id='console-pre')],
70
+ id='console-div',
71
+ children=[html.Pre(get_shell_intro(), id='console-pre')],
75
72
  )
76
73
 
77
- location = dcc.Location(id='location', refresh=False)
74
+ location = dcc.Location(id='mrsm-location', refresh=False)
78
75
 
79
76
  search_parameters_editor = dash_ace.DashAceEditor(
80
- id = 'search-parameters-editor',
81
- theme = 'monokai',
82
- mode = 'json',
83
- tabSize = 2,
84
- placeholder = (
77
+ id='search-parameters-editor',
78
+ theme='monokai',
79
+ mode='json',
80
+ tabSize=2,
81
+ placeholder=(
85
82
  'Additional search parameters. ' +
86
83
  'Simple dictionary format or JSON accepted.'
87
84
  ),
88
- style = {'height' : 100},
85
+ style={'height': 100},
89
86
  )
90
87
 
91
88
  sidebar = dbc.Offcanvas(
92
- children=[
93
-
94
- ],
89
+ children=[],
95
90
  title='Pages',
96
91
  )
97
92
 
@@ -99,13 +94,13 @@ download_dataframe = dcc.Download(id='download-dataframe-csv')
99
94
  download_logs = dcc.Download(id='download-logs')
100
95
 
101
96
  instance_select = dbc.Select(
102
- id = 'instance-select',
103
- size = 'sm',
104
- options = [
97
+ id='instance-select',
98
+ size='sm',
99
+ options=[
105
100
  {'label': i, 'value': i}
106
101
  for i in get_connector_labels(*instance_types)
107
102
  ],
108
- class_name = 'dbc_dark custom-select custom-select-sm',
103
+ class_name='dbc_dark custom-select custom-select-sm',
109
104
  )
110
105
 
111
106
 
@@ -116,17 +111,17 @@ navbar = dbc.Navbar(
116
111
  dbc.Row(
117
112
  [
118
113
  dbc.Col(
119
- html.Img(
120
- src = endpoints['dash'] + "/assets/logo_48x48.png",
121
- title = doc,
122
- ),
114
+ html.Img(
115
+ src=endpoints['dash'] + "/assets/logo_48x48.png",
116
+ title=doc,
117
+ ),
123
118
  ),
124
119
  ],
125
- align = 'center',
126
- className = 'g-0 navbar-logo-row',
120
+ align='center',
121
+ className='g-0 navbar-logo-row',
127
122
  ),
128
- href = '/docs',
129
- style = {"textDecoration": "none"},
123
+ href='/docs',
124
+ style={"textDecoration": "none"},
130
125
  ),
131
126
  dbc.NavbarToggler(id="navbar-toggler", n_clicks=0),
132
127
  dbc.Collapse(
@@ -136,30 +131,30 @@ navbar = dbc.Navbar(
136
131
  dbc.Col(
137
132
  dbc.Button(
138
133
  "Sign out",
139
- color = 'link',
140
- style = {'margin-left': '30px'},
141
- id = 'sign-out-button',
134
+ color='link',
135
+ style={'margin-left': '30px'},
136
+ id='sign-out-button',
142
137
  ),
143
138
  ),
144
139
  ],
145
- className = "g-0 ms-auto flex-nowrap mt-3 mt-md-0",
140
+ className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
146
141
  ),
147
- id = 'navbar-collapse',
148
- is_open = False,
149
- navbar = True,
142
+ id='navbar-collapse',
143
+ is_open=False,
144
+ navbar=True,
150
145
  ),
151
146
  ],
152
- style = {'max-width': '96%'},
147
+ style={'max-width': '96%'},
153
148
  ),
154
- color = 'dark', dark=True,
155
- style = {'width': '100% !important'},
149
+ color='dark', dark=True,
150
+ style={'width': '100% !important'},
156
151
  )
157
152
 
158
153
  refresh_jobs_interval = dcc.Interval(
159
- id = 'refresh-jobs-interval',
160
- interval = 1 * 1000,
161
- n_intervals = 0,
162
- disabled = False,
154
+ id='refresh-jobs-interval',
155
+ interval=(1 * 1000),
156
+ n_intervals=0,
157
+ disabled=False,
163
158
  )
164
159
 
165
160
  def alert_from_success_tuple(success: SuccessTuple) -> dbc.Alert:
@@ -169,11 +164,11 @@ def alert_from_success_tuple(success: SuccessTuple) -> dbc.Alert:
169
164
  return dbc.Alert('', is_open=False) if not isinstance(success, tuple) else (
170
165
  dbc.Alert(
171
166
  remove_ansi(success[1]),
172
- id = 'success-alert',
173
- dismissable = True,
174
- fade = True,
175
- is_open = not (success[1] in STATIC_CONFIG['system']['success']['ignore']),
176
- color = 'success' if success[0] else 'danger',
167
+ id='success-alert',
168
+ dismissable=True,
169
+ fade=True,
170
+ is_open=(success[1] not in STATIC_CONFIG['system']['success']['ignore']),
171
+ color='success' if success[0] else 'danger',
177
172
  )
178
173
  )
179
174
 
@@ -7,24 +7,20 @@ Functions for interacting with jobs via the web interface.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from dash.dependencies import Input, Output, State
11
- from meerschaum.utils.typing import List, Optional, Dict, Any, Tuple, Union, WebState
10
+ from meerschaum.utils.typing import List, Dict, WebState
12
11
  from meerschaum.utils.packages import attempt_import, import_html, import_dcc
13
- from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
14
- from meerschaum.api.dash.users import is_session_authenticated
12
+ from meerschaum.api.dash.sessions import is_session_authenticated
15
13
  from meerschaum.api import CHECK_UPDATE
16
- dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
17
- html, dcc = import_html(), import_dcc()
18
14
  from meerschaum.jobs import (
19
15
  get_jobs,
20
- get_running_jobs,
21
- get_paused_jobs,
22
- get_stopped_jobs,
23
16
  get_executor_keys_from_context,
24
17
  Job,
25
18
  )
26
19
  from meerschaum.config import get_config
27
20
  from meerschaum.utils.misc import sorted_dict
21
+ from dash.dependencies import Input, Output, State
22
+ dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
23
+ html, dcc = import_html(), import_dcc()
28
24
 
29
25
  STATUS_EMOJI: Dict[str, str] = {
30
26
  'running': get_config('formatting', 'emoji', 'running'),
@@ -10,3 +10,4 @@ import meerschaum.api.dash.pages.login
10
10
  import meerschaum.api.dash.pages.dashboard
11
11
  import meerschaum.api.dash.pages.plugins
12
12
  import meerschaum.api.dash.pages.register
13
+ import meerschaum.api.dash.pages.pipes
@@ -0,0 +1,19 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Display pipes via a shareable URL.
6
+ """
7
+
8
+ from meerschaum.api import CHECK_UPDATE
9
+ from meerschaum.utils.packages import import_html, import_dcc
10
+ from meerschaum.api.dash.components import download_dataframe
11
+
12
+ html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
13
+ import dash_bootstrap_components as dbc
14
+
15
+ layout = dbc.Container([
16
+ dcc.Location('pipes-location'),
17
+ download_dataframe,
18
+ html.Div(id='pipe-output-div'),
19
+ ])
@@ -12,18 +12,16 @@ import shlex
12
12
  from textwrap import dedent
13
13
  from dash.dependencies import Input, Output, State
14
14
  from meerschaum.utils.typing import List, Optional, Dict, Any, Tuple, Union
15
- from meerschaum.utils.misc import string_to_dict, json_serialize_datetime
15
+ from meerschaum.utils.misc import string_to_dict
16
16
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html, import_pandas
17
17
  from meerschaum.utils.sql import get_pd_type
18
18
  from meerschaum.utils.yaml import yaml
19
19
  from meerschaum.connectors.sql._fetch import get_pipe_query
20
- from meerschaum.api import endpoints, CHECK_UPDATE
21
- from meerschaum.api.dash import (
22
- dash_app, debug, _get_pipes
23
- )
20
+ from meerschaum.api import CHECK_UPDATE
21
+ from meerschaum.api.dash import debug, _get_pipes
24
22
  from meerschaum.api.dash.connectors import get_web_connector
25
23
  from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
26
- from meerschaum.api.dash.users import is_session_authenticated
24
+ from meerschaum.api.dash.sessions import is_session_authenticated
27
25
  from meerschaum.config import get_config
28
26
  import meerschaum as mrsm
29
27
  dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
@@ -48,13 +46,14 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
48
46
  return None
49
47
  return mrsm.Pipe(**meta)
50
48
 
49
+
51
50
  def keys_from_state(
52
- state: Dict[str, Any],
53
- with_params: bool = False
54
- ) -> Union[
55
- Tuple[List[str], List[str], List[str]],
56
- Tuple[List[str], List[str], List[str], str],
57
- ]:
51
+ state: Dict[str, Any],
52
+ with_params: bool = False
53
+ ) -> Union[
54
+ Tuple[List[str], List[str], List[str]],
55
+ Tuple[List[str], List[str], List[str], str],
56
+ ]:
58
57
  """
59
58
  Read the current state and return the selected keys lists.
60
59
  """
@@ -85,10 +84,11 @@ def keys_from_state(
85
84
  keys.append(params)
86
85
  return tuple(keys)
87
86
 
87
+
88
88
  def pipes_from_state(
89
- state: Dict[str, Any],
90
- **kw
91
- ):
89
+ state: Dict[str, Any],
90
+ **kw
91
+ ):
92
92
  _ck, _mk, _lk, _params = keys_from_state(state, with_params=True)
93
93
  try:
94
94
  _pipes = _get_pipes(
@@ -103,10 +103,10 @@ def pipes_from_state(
103
103
 
104
104
 
105
105
  def build_pipe_card(
106
- pipe: mrsm.Pipe,
107
- authenticated: bool = False,
108
- _build_children_num: int = 10,
109
- ) -> 'dbc.Card':
106
+ pipe: mrsm.Pipe,
107
+ authenticated: bool = False,
108
+ _build_children_num: int = 10,
109
+ ) -> 'dbc.Card':
110
110
  """
111
111
  Return a card for the given pipe.
112
112
 
@@ -128,11 +128,11 @@ def build_pipe_card(
128
128
  dbc.Col(
129
129
  (
130
130
  dbc.DropdownMenu(
131
- label = "Manage",
132
- children = [
131
+ label="Manage",
132
+ children=[
133
133
  dbc.DropdownMenuItem(
134
134
  'Open in Python',
135
- id = {
135
+ id={
136
136
  'type': 'manage-pipe-button',
137
137
  'index': meta_str,
138
138
  'action': 'python',
@@ -140,7 +140,7 @@ def build_pipe_card(
140
140
  ),
141
141
  dbc.DropdownMenuItem(
142
142
  'Delete',
143
- id = {
143
+ id={
144
144
  'type': 'manage-pipe-button',
145
145
  'index': meta_str,
146
146
  'action': 'delete',
@@ -148,7 +148,7 @@ def build_pipe_card(
148
148
  ),
149
149
  dbc.DropdownMenuItem(
150
150
  'Drop',
151
- id = {
151
+ id={
152
152
  'type': 'manage-pipe-button',
153
153
  'index': meta_str,
154
154
  'action': 'drop',
@@ -156,7 +156,7 @@ def build_pipe_card(
156
156
  ),
157
157
  dbc.DropdownMenuItem(
158
158
  'Clear',
159
- id = {
159
+ id={
160
160
  'type': 'manage-pipe-button',
161
161
  'index': meta_str,
162
162
  'action': 'clear',
@@ -164,7 +164,7 @@ def build_pipe_card(
164
164
  ),
165
165
  dbc.DropdownMenuItem(
166
166
  'Verify',
167
- id = {
167
+ id={
168
168
  'type': 'manage-pipe-button',
169
169
  'index': meta_str,
170
170
  'action': 'verify',
@@ -172,56 +172,84 @@ def build_pipe_card(
172
172
  ),
173
173
  dbc.DropdownMenuItem(
174
174
  'Sync',
175
- id = {
175
+ id={
176
176
  'type': 'manage-pipe-button',
177
177
  'index': meta_str,
178
178
  'action': 'sync',
179
179
  },
180
180
  ),
181
181
  ],
182
- direction = "up",
183
- menu_variant = "dark",
184
- size = 'sm',
185
- color = 'secondary',
182
+ direction="up",
183
+ menu_variant="dark",
184
+ size='sm',
185
+ color='secondary',
186
186
  )
187
187
  ) if authenticated else [],
188
- width = 2,
188
+ width=2,
189
189
  ),
190
190
  dbc.Col(width=6),
191
191
  dbc.Col(
192
192
  dbc.Button(
193
193
  'Download CSV',
194
- size = 'sm',
195
- color = 'link',
196
- style = {'float': 'right'},
197
- id = {'type': 'pipe-download-csv-button', 'index': meta_str},
194
+ size='sm',
195
+ color='link',
196
+ style={'float': 'right'},
197
+ id={'type': 'pipe-download-csv-button', 'index': meta_str},
198
198
  ),
199
- width = 4,
199
+ width=4,
200
200
  ),
201
201
  ],
202
- justify = 'start',
202
+ justify='start',
203
203
  )
204
204
  card_body_children = [
205
- html.H5(
206
- html.B(str(pipe)),
207
- className = 'card-title',
208
- style = {'font-family': ['monospace']}
209
- ),
210
205
  html.Div(
211
206
  dbc.Accordion(
212
207
  accordion_items_from_pipe(
213
208
  pipe,
214
- authenticated = authenticated,
215
- _build_children_num = _build_children_num,
209
+ authenticated=authenticated,
210
+ _build_children_num=_build_children_num,
216
211
  ),
217
- flush = True,
218
- start_collapsed = True,
219
- id = {'type': 'pipe-accordion', 'index': meta_str},
212
+ flush=True,
213
+ start_collapsed=True,
214
+ id={'type': 'pipe-accordion', 'index': meta_str},
220
215
  )
221
216
  )
222
217
 
223
218
  ]
219
+
220
+ pipe_url = (
221
+ f"/dash/pipes/{pipe.connector_keys}/{pipe.metric_key}/{pipe.location_key}"
222
+ )
223
+
224
+ card_header_children = dbc.Row(
225
+ [
226
+ dbc.Col(
227
+ html.H5(
228
+ html.B(str(pipe)),
229
+ className='card-title',
230
+ style={'font-family': ['monospace']}
231
+ ),
232
+ width=11,
233
+ ),
234
+ dbc.Col(
235
+ dbc.Button(
236
+ "🔗",
237
+ href=pipe_url,
238
+ external_link=True,
239
+ target="_blank",
240
+ style={'float': 'right'},
241
+ outline=True,
242
+ color='link',
243
+ id={'type': 'share-pipe-button', 'index': meta_str},
244
+ ),
245
+ width=1,
246
+ ),
247
+ ],
248
+ justify='start',
249
+ )
250
+
224
251
  return dbc.Card([
252
+ dbc.CardHeader(children=card_header_children),
225
253
  dbc.CardBody(children=card_body_children),
226
254
  dbc.CardFooter(children=footer_children),
227
255
  ])
@@ -270,11 +298,11 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
270
298
 
271
299
 
272
300
  def accordion_items_from_pipe(
273
- pipe: mrsm.Pipe,
274
- active_items: Optional[List[str]] = None,
275
- authenticated: bool = False,
276
- _build_children_num: int = 10,
277
- ) -> 'List[dbc.AccordionItem]':
301
+ pipe: mrsm.Pipe,
302
+ active_items: Optional[List[str]] = None,
303
+ authenticated: bool = False,
304
+ _build_children_num: int = 10,
305
+ ) -> 'List[dbc.AccordionItem]':
278
306
  """
279
307
  Build the accordion items for a given pipe.
280
308
  """
@@ -521,11 +549,11 @@ def accordion_items_from_pipe(
521
549
  backtrack_df = pipe.get_backtrack_data(debug=debug, limit=1)
522
550
  try:
523
551
  json_text = backtrack_df.fillna(pd.NA).to_json(
524
- orient = 'records',
525
- date_format = 'iso',
526
- force_ascii = False,
527
- indent = 4,
528
- date_unit = 'ns',
552
+ orient='records',
553
+ date_format='iso',
554
+ force_ascii=False,
555
+ indent=4,
556
+ date_unit='ns',
529
557
  ) if backtrack_df is not None else '[]'
530
558
  except Exception as e:
531
559
  warn(e)
@@ -13,7 +13,8 @@ from meerschaum.api import get_api_connector, endpoints, CHECK_UPDATE
13
13
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
14
14
  import dash_bootstrap_components as dbc
15
15
  from meerschaum.core import Plugin
16
- from meerschaum.api.dash import dash_app, debug, active_sessions
16
+ from meerschaum.api.dash import dash_app, debug
17
+ from meerschaum.api.dash.sessions import get_username_from_session
17
18
 
18
19
 
19
20
  def get_plugins_cards(
@@ -88,9 +89,10 @@ def is_plugin_owner(plugin_name: str, session_data: Dict['str', Any]) -> bool:
88
89
  Check whether the currently logged in user is the owner of a plugin.
89
90
  """
90
91
  plugin = Plugin(plugin_name)
91
- _username = active_sessions.get(
92
- session_data.get('session-id', None), {}
93
- ).get('username', None)
92
+ session_id = session_data.get('session-id', None)
93
+ if session_id is None:
94
+ return False
95
+ _username = get_username_from_session(session_id)
94
96
  _plugin_username = get_api_connector().get_plugin_username(plugin, debug=debug)
95
97
  return (
96
98
  _username is not None