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
@@ -5,22 +5,35 @@
5
5
  Define callbacks for the `/dash/pipes/` page.
6
6
  """
7
7
 
8
- from urllib.parse import parse_qs
8
+ from urllib.parse import parse_qs, quote_plus
9
+ from typing import List, Optional, Dict, Any
9
10
 
11
+ import dash
10
12
  from dash.dependencies import Input, Output, State
11
13
  from dash import no_update
14
+ from dash.exceptions import PreventUpdate
15
+ import dash_bootstrap_components as dbc
12
16
 
13
17
  import meerschaum as mrsm
14
18
  from meerschaum.api.dash import dash_app
15
- from meerschaum.api.dash.pipes import build_pipe_card
16
- from meerschaum.api import CHECK_UPDATE
19
+ from meerschaum.api.dash.components import (
20
+ alert_from_success_tuple,
21
+ build_cards_grid,
22
+ )
23
+ from meerschaum.api.dash.pipes import (
24
+ build_pipe_card,
25
+ build_pipes_dropdown_keys_row,
26
+ build_pipes_tags_dropdown,
27
+ build_pipes_navbar,
28
+ )
29
+ from meerschaum.api import CHECK_UPDATE, get_api_connector
17
30
  from meerschaum.utils.packages import import_html, import_dcc
18
31
  from meerschaum.api.dash.sessions import is_session_authenticated
19
- from meerschaum.utils.typing import Optional, Dict, Any
20
32
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
21
33
 
22
34
 
23
35
  @dash_app.callback(
36
+ Output('pipes-navbar-div', 'children'),
24
37
  Output('pipe-output-div', 'children'),
25
38
  Input('pipes-location', 'pathname'),
26
39
  State('pipes-location', 'search'),
@@ -32,24 +45,191 @@ def render_pipe_page_from_url(
32
45
  session_data: Optional[Dict[str, Any]],
33
46
  ):
34
47
  if not str(pathname).startswith('/dash/pipes'):
35
- return no_update
48
+ raise PreventUpdate
36
49
 
37
50
  session_id = (session_data or {}).get('session-id', None)
38
51
  authenticated = is_session_authenticated(str(session_id))
52
+ query_params = parse_qs(pipe_search.lstrip('?')) if pipe_search else {}
53
+ instance = query_params.get('instance', [None])[0] or str(get_api_connector())
54
+ tags = query_params.get('tags', [None])[0] or []
55
+ if isinstance(tags, str):
56
+ tags = tags.split(',')
57
+
58
+ connector_keys = query_params.get('connector_keys', [None])[0] or []
59
+ if isinstance(connector_keys, str):
60
+ connector_keys = connector_keys.split(',')
61
+
62
+ metric_keys = query_params.get('metric_keys', [None])[0] or []
63
+ if isinstance(metric_keys, str):
64
+ metric_keys = metric_keys.split(',')
65
+
66
+ location_keys = query_params.get('location_keys', [None])[0] or []
67
+ if isinstance(location_keys, str):
68
+ location_keys = location_keys.split(',')
39
69
 
40
70
  keys = pathname.replace('/dash/pipes', '').lstrip('/').rstrip('/').split('/')
41
- if len(keys) not in (2, 3):
42
- return no_update
71
+ instance_connector = mrsm.get_connector(instance)
72
+ viewing_single_pipe = len(keys) in (2, 3)
73
+ if instance_connector is None:
74
+ return (
75
+ build_pipes_navbar(instance, with_instance_select=(not viewing_single_pipe)),
76
+ [
77
+ html.Br(),
78
+ alert_from_success_tuple((False, f"Invalid instance keys '{instance}'.")),
79
+ html.Br(),
80
+ ]
81
+ )
82
+
83
+ if not viewing_single_pipe:
84
+ try:
85
+ pipes = mrsm.get_pipes(
86
+ as_list=True,
87
+ connector_keys=connector_keys,
88
+ metric_keys=metric_keys,
89
+ location_keys=location_keys,
90
+ tags=tags,
91
+ instance=instance_connector,
92
+ )
93
+ except Exception as e:
94
+ return (
95
+ build_pipes_navbar(instance, with_instance_select=False),
96
+ [
97
+ html.Br(),
98
+ alert_from_success_tuple(
99
+ (False, f"Failed to get pipes for instance '{instance}':\n{e}")
100
+ ),
101
+ html.Br(),
102
+ dbc.Row(
103
+ [
104
+ dbc.Button(
105
+ "Reload",
106
+ id='pipes-reload-button',
107
+ size='lg',
108
+ href=(
109
+ "/dash/pipes"
110
+ if pathname.startswith('/dash/pipes/')
111
+ else "/dash/pipes/"
112
+ )
113
+ ),
114
+ ],
115
+ justify='center',
116
+ align='center',
117
+ className='h-50',
118
+ ),
119
+ ]
120
+ )
121
+
122
+ cards = [
123
+ build_pipe_card(pipe, authenticated=authenticated, include_manage=False)
124
+ for pipe in pipes
125
+ ]
126
+ return (
127
+ build_pipes_navbar(instance, with_instance_select=True),
128
+ [
129
+ html.Div([
130
+ html.Br(),
131
+ build_pipes_dropdown_keys_row(
132
+ connector_keys,
133
+ metric_keys,
134
+ location_keys,
135
+ tags,
136
+ pipes,
137
+ instance_connector,
138
+ ),
139
+ html.Br(),
140
+ build_pipes_tags_dropdown(
141
+ connector_keys,
142
+ metric_keys,
143
+ location_keys,
144
+ tags,
145
+ instance,
146
+ ),
147
+ ]),
148
+ html.Br(),
149
+ build_cards_grid(cards, 1),
150
+ html.Br(),
151
+ ]
152
+ )
43
153
 
44
154
  ck = keys[0]
45
155
  mk = keys[1]
46
156
  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
157
 
50
158
  pipe = mrsm.Pipe(ck, mk, lk, instance=instance)
51
- return [
52
- html.Br(),
53
- build_pipe_card(pipe, authenticated=authenticated, include_manage=False),
54
- html.Br(),
55
- ]
159
+ return (
160
+ build_pipes_navbar(instance, with_instance_select=False),
161
+ [
162
+ html.Br(),
163
+ build_pipe_card(pipe, authenticated=authenticated, include_manage=False),
164
+ html.Br(),
165
+ ]
166
+ )
167
+
168
+
169
+ @dash_app.callback(
170
+ Output('pipes-location', 'search'),
171
+ Input('pipes-connector-keys-dropdown', 'value'),
172
+ Input('pipes-metric-keys-dropdown', 'value'),
173
+ Input('pipes-location-keys-dropdown', 'value'),
174
+ Input('pipes-tags-dropdown', 'value'),
175
+ Input('instance-select', 'value'),
176
+ Input('pipes-clear-all-button', 'n_clicks'),
177
+ )
178
+ def update_location_on_pipes_filter_change(
179
+ connector_keys: Optional[List[str]],
180
+ metric_keys: Optional[List[str]],
181
+ location_keys: Optional[List[str]],
182
+ tags: Optional[List[str]],
183
+ instance_keys: str,
184
+ clear_all_button_n_clicks: Optional[int],
185
+ ):
186
+ """
187
+ Update the URL parameters when clicking the dropdowns.
188
+ """
189
+ ctx = dash.callback_context.triggered
190
+ if len(ctx) != 1:
191
+ raise PreventUpdate
192
+
193
+ if not any(
194
+ (connector_keys or [])
195
+ + (metric_keys or [])
196
+ + (location_keys or [])
197
+ + (tags or [])
198
+ + ([instance_keys] if instance_keys else [])
199
+ ):
200
+ return ''
201
+
202
+ if ctx[0].get('prop_id', None) == 'pipes-clear-all-button.n_clicks':
203
+ connector_keys = []
204
+ metric_keys = []
205
+ location_keys = []
206
+ tags = []
207
+
208
+ include_instance_keys = instance_keys and instance_keys != str(get_api_connector())
209
+ search_str = ""
210
+
211
+ if connector_keys:
212
+ search_str += "connector_keys=" + ','.join((quote_plus(ck) for ck in connector_keys))
213
+ if metric_keys or location_keys or tags or include_instance_keys:
214
+ search_str += '&'
215
+
216
+ if metric_keys:
217
+ search_str += "metric_keys=" + ','.join((quote_plus(mk) for mk in metric_keys))
218
+ if location_keys or tags or include_instance_keys:
219
+ search_str += '&'
220
+
221
+ if location_keys:
222
+ search_str += "location_keys=" + ','.join((quote_plus(str(lk)) for lk in location_keys))
223
+ if tags or include_instance_keys:
224
+ search_str += '&'
225
+
226
+ if tags:
227
+ search_str += "tags=" + ','.join((quote_plus(tag) for tag in tags))
228
+ if include_instance_keys:
229
+ search_str += '&'
230
+
231
+ if instance_keys:
232
+ if include_instance_keys:
233
+ search_str += "instance=" + quote_plus(instance_keys)
234
+
235
+ return ('?' + search_str) if search_str else ''
@@ -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
@@ -54,7 +54,8 @@ def refresh_tokens_button_click(
54
54
  html.H4('No tokens registered.'),
55
55
  html.P('Click the `+` button to register a new token.'),
56
56
  ],
57
- alerts
57
+ build_tokens_register_input_modal(),
58
+ alerts,
58
59
  )
59
60
 
60
61
  return tokens_table, build_tokens_register_input_modal(), alerts
@@ -163,7 +164,7 @@ def register_token_click(
163
164
  token = Token(
164
165
  label=(name or None),
165
166
  user=get_user_from_session(session_id),
166
- expiration=(datetime.fromisoformat(f"{expiration}T00:00:00Z") if expiration is not None else None),
167
+ expiration=(datetime.fromisoformat(f"{expiration}") if expiration is not None else None),
167
168
  )
168
169
  return False, True, build_tokens_register_output_modal(token)
169
170
 
@@ -13,7 +13,7 @@ from meerschaum.utils.typing import SuccessTuple, List
13
13
  from meerschaum._internal.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
- from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled
16
+ from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled, get_api_connector
17
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
@@ -104,7 +104,7 @@ instance_select = dbc.Select(
104
104
  id='instance-select',
105
105
  size='sm',
106
106
  options=[
107
- {'label': i, 'value': i}
107
+ {'label': (i[:32] + '…') if len(i) > 32 else i, 'value': i}
108
108
  for i in get_connector_labels(*instance_types)
109
109
  ],
110
110
  class_name='dbc_dark custom-select custom-select-sm',
@@ -163,6 +163,7 @@ pages_navbar = html.Div(
163
163
  id='pages-navbar-div',
164
164
  )
165
165
 
166
+
166
167
  navbar = dbc.Navbar(
167
168
  dbc.Container(
168
169
  [
@@ -171,13 +172,11 @@ navbar = dbc.Navbar(
171
172
  dbc.Collapse(
172
173
  dbc.Row(
173
174
  [
174
- dbc.Col(instance_select),
175
- dbc.Col(
176
- sign_out_button,
177
- className="ms-auto",
178
- ),
175
+ dbc.Col(instance_select, width="auto"),
176
+ dbc.Col(sign_out_button, width="auto"),
179
177
  ],
180
178
  className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
179
+ align="center",
181
180
  ),
182
181
  id='navbar-collapse',
183
182
  is_open=False,
@@ -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',
@@ -227,7 +227,23 @@ dropdown_tab_content = html.Div([
227
227
  [
228
228
  dropdown_keys_row,
229
229
  html.Br(),
230
- tags_dropdown,
230
+ dbc.Row(
231
+ [
232
+ dbc.Col(tags_dropdown, width=True),
233
+ dbc.Col(
234
+ dbc.Button(
235
+ "Clear all",
236
+ id='clear-all-keys-button',
237
+ color='link',
238
+ size='sm',
239
+ style={'text-decoration': 'none'},
240
+ ),
241
+ width='auto',
242
+ ),
243
+ ],
244
+ className='g-0',
245
+ align='center',
246
+ ),
231
247
  ], ### end of card children
232
248
  className='card-text',
233
249
  )
@@ -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,27 @@ 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 (
11
+ download_dataframe,
12
+ )
11
13
 
12
14
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
13
15
  import dash_bootstrap_components as dbc
14
16
 
15
17
  layout = [
16
- pages_navbar,
18
+ html.Div(id='pipes-navbar-div'),
19
+ dcc.Location('pipes-location'),
20
+ download_dataframe,
17
21
  dbc.Container([
18
- dcc.Location('pipes-location'),
19
- download_dataframe,
20
- html.Div(id='pipe-output-div'),
22
+ dcc.Loading(
23
+ html.Div(id='pipe-output-div'),
24
+ id='pipes-loading',
25
+ type='circle',
26
+ delay_hide=1000,
27
+ delay_show=1000,
28
+ style={
29
+ 'padding-top': '100px',
30
+ },
31
+ ),
21
32
  ])
22
33
  ]
@@ -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,18 +12,26 @@ 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
- from meerschaum.utils.misc import string_to_dict
17
+ from meerschaum.utils.misc import get_connector_labels
18
+ from meerschaum.connectors import instance_types
17
19
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html, import_pandas
18
20
  from meerschaum.utils.sql import get_pd_type
19
21
  from meerschaum.utils.yaml import yaml
20
22
  from meerschaum.utils.warnings import warn
21
23
  from meerschaum.utils.dataframe import to_json, to_simple_lines
22
24
  from meerschaum.connectors.sql._fetch import get_pipe_query
23
- from meerschaum.api import CHECK_UPDATE
25
+ from meerschaum.api import CHECK_UPDATE, get_api_connector
24
26
  from meerschaum.api.dash import debug, _get_pipes
25
27
  from meerschaum.api.dash.connectors import get_web_connector
26
- from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
28
+ from meerschaum.api.dash.components import (
29
+ alert_from_success_tuple,
30
+ build_cards_grid,
31
+ sign_out_button,
32
+ logo_row,
33
+ pages_offcanvas,
34
+ )
27
35
  from meerschaum.api.dash.sessions import is_session_authenticated
28
36
  from meerschaum.config import get_config
29
37
  import meerschaum as mrsm
@@ -817,3 +825,211 @@ def get_backtrack_text(
817
825
  json_text = '[]'
818
826
 
819
827
  return json.dumps(json.loads(json_text), indent=4, separators=(',', ': '))
828
+
829
+
830
+ def build_pipes_dropdown_keys_row(
831
+ connector_keys: List[str],
832
+ metric_keys: List[str],
833
+ location_keys: List[str],
834
+ tags: List[str],
835
+ pipes: List[mrsm.Pipe],
836
+ instance_connector: mrsm.connectors.InstanceConnector,
837
+ ) -> dbc.Row:
838
+ """
839
+ Return the dropdown keys row for the dedicated pipes page.
840
+ """
841
+ ck_alone = connector_keys and not any([str(x) for x in (tags + metric_keys + location_keys)])
842
+ mk_alone = metric_keys and not any([str(x) for x in (connector_keys + tags + location_keys)])
843
+ lk_alone = location_keys and not any([str(x) for x in (connector_keys + metric_keys + tags)])
844
+ all_keys = fetch_pipes_keys('registered', instance_connector)
845
+
846
+ ck_options_source = (
847
+ {keys_tuple[0] for keys_tuple in all_keys}
848
+ if ck_alone
849
+ else {p.connector_keys for p in pipes}
850
+ )
851
+ ck_options = sorted(ck_options_source.union(connector_keys))
852
+
853
+ mk_options_source = (
854
+ {keys_tuple[1] for keys_tuple in all_keys}
855
+ if mk_alone
856
+ else {p.metric_key for p in pipes}
857
+ )
858
+ mk_options = sorted(mk_options_source.union(metric_keys))
859
+
860
+ lk_options_source = (
861
+ {str(keys_tuple[2]) for keys_tuple in all_keys}
862
+ if lk_alone
863
+ else {str(p.location_key) for p in pipes}
864
+ )
865
+ lk_options = sorted(lk_options_source.union({str(lk) for lk in location_keys}))
866
+
867
+ return dbc.Row(
868
+ [
869
+ dbc.Col(
870
+ html.Div(
871
+ [
872
+ dcc.Dropdown(
873
+ id='pipes-connector-keys-dropdown',
874
+ options=ck_options,
875
+ value=[str(ck) for ck in connector_keys],
876
+ placeholder='Connectors',
877
+ multi=True,
878
+ ),
879
+ ],
880
+ className='dbc_dark',
881
+ ),
882
+ lg=4,
883
+ md=12,
884
+ sm=12,
885
+ ),
886
+ dbc.Col(
887
+ html.Div(
888
+ [
889
+ dcc.Dropdown(
890
+ id='pipes-metric-keys-dropdown',
891
+ options=mk_options,
892
+ value=[str(mk) for mk in metric_keys],
893
+ placeholder='Metrics',
894
+ multi=True,
895
+ ),
896
+ ],
897
+ className='dbc_dark'
898
+ ),
899
+ lg=4,
900
+ md=12,
901
+ sm=12,
902
+ ),
903
+ dbc.Col(
904
+ html.Div(
905
+ [
906
+ dcc.Dropdown(
907
+ id='pipes-location-keys-dropdown',
908
+ options=lk_options,
909
+ value=[str(lk) for lk in location_keys],
910
+ placeholder='Locations',
911
+ multi=True,
912
+ ),
913
+ ],
914
+ className='dbc_dark'
915
+ ),
916
+ lg=4,
917
+ md=12,
918
+ sm=12,
919
+ ),
920
+ ] ### end of filters row children
921
+ )
922
+
923
+
924
+ def build_pipes_tags_dropdown(
925
+ connector_keys: List[str],
926
+ metric_keys: List[str],
927
+ location_keys: List[str],
928
+ tags: List[str],
929
+ instance: str,
930
+ ) -> dbc.Row:
931
+ """
932
+ Build the tags dropdown for the dedicated pipes page.
933
+ """
934
+ _tags_alone = tags and not any([str(x) for x in (connector_keys + metric_keys + location_keys)])
935
+ _tags_pipes = mrsm.get_pipes(
936
+ connector_keys=connector_keys,
937
+ metric_keys=metric_keys,
938
+ location_keys=location_keys,
939
+ tags=tags,
940
+ instance=instance,
941
+ as_tags_dict=True,
942
+ )
943
+
944
+ _all_tags = list(
945
+ mrsm.get_pipes(
946
+ instance=instance,
947
+ as_tags_dict=True,
948
+ )
949
+ ) if _tags_alone else []
950
+
951
+ tags_options = [
952
+ str(tag)
953
+ for tag in (_all_tags if _tags_alone else _tags_pipes)
954
+ ]
955
+ if tags:
956
+ tags_options += [tag for tag in tags if tag not in tags_options]
957
+
958
+ return dbc.Row(
959
+ [
960
+ dbc.Col(
961
+ html.Div(
962
+ dcc.Dropdown(
963
+ id='pipes-tags-dropdown',
964
+ options=tags_options,
965
+ value=tags,
966
+ placeholder='Tags',
967
+ multi=True,
968
+ searchable=True,
969
+ ),
970
+ className="dbc_dark",
971
+ id="pipes-tags-dropdown-div",
972
+ ),
973
+ width=True,
974
+ ),
975
+ dbc.Col(
976
+ dbc.Button(
977
+ "Clear all",
978
+ color='link',
979
+ size='sm',
980
+ style={'text-decoration': 'none'},
981
+ id='pipes-clear-all-button',
982
+ ),
983
+ width='auto',
984
+ ),
985
+ ],
986
+ className='g-0',
987
+ align='center',
988
+ )
989
+
990
+
991
+ def build_pipes_navbar(instance_keys: Optional[str] = None, with_instance_select: bool = True):
992
+ """
993
+ Build the navbar from the selected instance keys.
994
+ """
995
+ instance_select = dbc.Select(
996
+ id='instance-select',
997
+ size='sm',
998
+ value=instance_keys or str(get_api_connector()),
999
+ options=[
1000
+ {'label': (i[:32] + '…') if len(i) > 32 else i, 'value': i}
1001
+ for i in get_connector_labels(*instance_types)
1002
+ ],
1003
+ class_name='dbc_dark custom-select custom-select-sm',
1004
+ )
1005
+ instance_select_div_style = {} if with_instance_select else {'visibility': 'hidden'}
1006
+ instance_select_div = html.Div(instance_select, style=instance_select_div_style)
1007
+ return html.Div(
1008
+ [
1009
+ pages_offcanvas,
1010
+ dbc.Navbar(
1011
+ dbc.Container(
1012
+ [
1013
+ logo_row,
1014
+ dbc.NavbarToggler(id="navbar-toggler", n_clicks=0),
1015
+ dbc.Collapse(
1016
+ dbc.Row(
1017
+ [
1018
+ dbc.Col(instance_select_div, width='auto'),
1019
+ dbc.Col(sign_out_button, width='auto'),
1020
+ ],
1021
+ className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
1022
+ align='center',
1023
+ ),
1024
+ id='navbar-collapse',
1025
+ is_open=False,
1026
+ navbar=True,
1027
+ ),
1028
+ ]
1029
+ ),
1030
+ dark=True,
1031
+ color='dark'
1032
+ ),
1033
+ ],
1034
+ id='pages-navbar-div',
1035
+ )