meerschaum 2.1.7__py3-none-any.whl → 2.2.0.dev2__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.
- meerschaum/_internal/arguments/_parser.py +3 -0
- meerschaum/_internal/entry.py +2 -1
- meerschaum/actions/install.py +7 -3
- meerschaum/actions/sync.py +7 -3
- meerschaum/api/dash/callbacks/dashboard.py +88 -13
- meerschaum/api/dash/callbacks/jobs.py +55 -3
- meerschaum/api/dash/jobs.py +34 -8
- meerschaum/api/dash/pipes.py +105 -18
- meerschaum/api/resources/static/js/xterm.js +1 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +0 -1
- meerschaum/connectors/api/_plugins.py +2 -1
- meerschaum/connectors/sql/_create_engine.py +5 -5
- meerschaum/plugins/_Plugin.py +11 -2
- meerschaum/utils/daemon/Daemon.py +11 -3
- meerschaum/utils/dtypes/__init__.py +9 -5
- meerschaum/utils/packages/__init__.py +4 -1
- meerschaum/utils/packages/_packages.py +6 -6
- meerschaum/utils/schedule.py +268 -29
- meerschaum/utils/typing.py +1 -1
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/METADATA +12 -14
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/RECORD +28 -28
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/LICENSE +0 -0
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/NOTICE +0 -0
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/WHEEL +0 -0
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.1.7.dist-info → meerschaum-2.2.0.dev2.dist-info}/zip-safe +0 -0
@@ -346,6 +346,9 @@ groups['misc'].add_argument(
|
|
346
346
|
groups['misc'].add_argument(
|
347
347
|
'--nopretty', action="store_true", help="Print elements without 'pretty' formatting"
|
348
348
|
)
|
349
|
+
groups['misc'].add_argument(
|
350
|
+
'--skip-deps', action="store_true", help="Skip dependencies when installing plugins.",
|
351
|
+
)
|
349
352
|
groups['misc'].add_argument(
|
350
353
|
'-P', '--params', type=string_to_dict, help=(
|
351
354
|
"Parameters dictionary in JSON format or simple format. " +
|
meerschaum/_internal/entry.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#! /usr/bin/env python
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
# vim:fenc=utf-8
|
4
|
+
# type: ignore
|
4
5
|
|
5
6
|
"""
|
6
7
|
The entry point for launching Meerschaum actions.
|
@@ -61,7 +62,7 @@ def entry_with_args(
|
|
61
62
|
"""
|
62
63
|
import sys
|
63
64
|
from meerschaum.plugins import Plugin
|
64
|
-
from meerschaum.actions import
|
65
|
+
from meerschaum.actions import get_action, get_main_action_name
|
65
66
|
from meerschaum._internal.arguments import remove_leading_action
|
66
67
|
from meerschaum.utils.venv import Venv, active_venvs, deactivate_venv
|
67
68
|
if kw.get('trace', None):
|
meerschaum/actions/install.py
CHANGED
@@ -54,6 +54,7 @@ def _complete_install(
|
|
54
54
|
def _install_plugins(
|
55
55
|
action: Optional[List[str]] = None,
|
56
56
|
repository: Optional[str] = None,
|
57
|
+
skip_deps: bool = False,
|
57
58
|
force: bool = False,
|
58
59
|
debug: bool = False,
|
59
60
|
**kw: Any
|
@@ -87,11 +88,14 @@ def _install_plugins(
|
|
87
88
|
|
88
89
|
repo_connector = parse_repo_keys(repository)
|
89
90
|
|
90
|
-
successes = {}
|
91
91
|
for name in action:
|
92
92
|
info(f"Installing plugin '{name}' from Meerschaum repository '{repo_connector}'...")
|
93
|
-
success, msg = repo_connector.install_plugin(
|
94
|
-
|
93
|
+
success, msg = repo_connector.install_plugin(
|
94
|
+
name,
|
95
|
+
force = force,
|
96
|
+
skip_deps = skip_deps,
|
97
|
+
debug = debug,
|
98
|
+
)
|
95
99
|
print_tuple((success, msg))
|
96
100
|
|
97
101
|
reload_plugins(debug=debug)
|
meerschaum/actions/sync.py
CHANGED
@@ -453,16 +453,20 @@ def _wrap_pipe(
|
|
453
453
|
return False, msg
|
454
454
|
return True, "Success"
|
455
455
|
|
456
|
-
|
456
|
+
pre_hook_results, post_hook_results = [], []
|
457
457
|
def apply_hooks(is_pre_sync: bool):
|
458
458
|
_sync_hooks = (_pre_sync_hooks if is_pre_sync else _post_sync_hooks)
|
459
|
+
_hook_results = (pre_hook_results if is_pre_sync else post_hook_results)
|
459
460
|
for module_name, sync_hooks in _sync_hooks.items():
|
460
461
|
plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
|
461
462
|
for sync_hook in sync_hooks:
|
462
463
|
hook_result = pool.apply_async(call_sync_hook, (plugin_name, sync_hook))
|
463
|
-
|
464
|
+
_hook_results.append(hook_result)
|
464
465
|
|
465
466
|
apply_hooks(True)
|
467
|
+
for hook_result in pre_hook_results:
|
468
|
+
hook_success, hook_msg = hook_result.get()
|
469
|
+
mrsm.pprint((hook_success, hook_msg))
|
466
470
|
|
467
471
|
try:
|
468
472
|
with Venv(get_connector_plugin(pipe.connector), debug=debug):
|
@@ -480,7 +484,7 @@ def _wrap_pipe(
|
|
480
484
|
'sync_complete_timestamp': datetime.now(timezone.utc),
|
481
485
|
})
|
482
486
|
apply_hooks(False)
|
483
|
-
for hook_result in
|
487
|
+
for hook_result in post_hook_results:
|
484
488
|
hook_success, hook_msg = hook_result.get()
|
485
489
|
mrsm.pprint((hook_success, hook_msg))
|
486
490
|
|
@@ -488,11 +488,11 @@ def update_keys_options(
|
|
488
488
|
"""
|
489
489
|
ctx = dash.callback_context
|
490
490
|
trigger = ctx.triggered[0]['prop_id'].split('.')[0]
|
491
|
+
instance_click = trigger == 'instance-select'
|
491
492
|
|
492
493
|
### Update the instance first.
|
493
494
|
update_instance_keys = False
|
494
495
|
if not instance_keys:
|
495
|
-
# instance_keys = get_config('meerschaum', 'web_instance')
|
496
496
|
instance_keys = str(get_api_connector())
|
497
497
|
update_instance_keys = True
|
498
498
|
instance_alerts = []
|
@@ -516,20 +516,23 @@ def update_keys_options(
|
|
516
516
|
if location_keys:
|
517
517
|
num_filter += 1
|
518
518
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
_ck_alone = connector_keys and num_filter == 1
|
523
|
-
_mk_alone = metric_keys and num_filter == 1
|
524
|
-
_lk_alone = location_keys and num_filter == 1
|
519
|
+
_ck_filter = connector_keys
|
520
|
+
_mk_filter = metric_keys
|
521
|
+
_lk_filter = location_keys
|
522
|
+
_ck_alone = (connector_keys and num_filter == 1) or instance_click
|
523
|
+
_mk_alone = (metric_keys and num_filter == 1) or instance_click
|
524
|
+
_lk_alone = (location_keys and num_filter == 1) or instance_click
|
525
525
|
|
526
526
|
from meerschaum.utils import fetch_pipes_keys
|
527
527
|
|
528
528
|
try:
|
529
529
|
_all_keys = fetch_pipes_keys('registered', get_web_connector(ctx.states))
|
530
530
|
_keys = fetch_pipes_keys(
|
531
|
-
'registered',
|
532
|
-
|
531
|
+
'registered',
|
532
|
+
get_web_connector(ctx.states),
|
533
|
+
connector_keys = _ck_filter,
|
534
|
+
metric_keys = _mk_filter,
|
535
|
+
location_keys = _lk_filter,
|
533
536
|
)
|
534
537
|
except Exception as e:
|
535
538
|
instance_alerts += [alert_from_success_tuple((False, str(e)))]
|
@@ -545,15 +548,39 @@ def update_keys_options(
|
|
545
548
|
k = locals()[key_type]
|
546
549
|
if k not in _seen_keys[key_type]:
|
547
550
|
_k = 'None' if k in (None, '[None]', 'None', 'null') else k
|
548
|
-
options.append({'label'
|
551
|
+
options.append({'label': _k, 'value': _k})
|
549
552
|
_seen_keys[key_type].add(k)
|
550
553
|
|
551
554
|
add_options(_connectors_options, _all_keys if _ck_alone else _keys, 'ck')
|
552
555
|
add_options(_metrics_options, _all_keys if _mk_alone else _keys, 'mk')
|
553
556
|
add_options(_locations_options, _all_keys if _lk_alone else _keys, 'lk')
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
+
_connectors_options.sort(key=lambda x: str(x).lower())
|
558
|
+
_metrics_options.sort(key=lambda x: str(x).lower())
|
559
|
+
_locations_options.sort(key=lambda x: str(x).lower())
|
560
|
+
connector_keys = [
|
561
|
+
ck
|
562
|
+
for ck in connector_keys
|
563
|
+
if ck in [
|
564
|
+
_ck['value']
|
565
|
+
for _ck in _connectors_options
|
566
|
+
]
|
567
|
+
]
|
568
|
+
metric_keys = [
|
569
|
+
mk
|
570
|
+
for mk in metric_keys
|
571
|
+
if mk in [
|
572
|
+
_mk['value']
|
573
|
+
for _mk in _metrics_options
|
574
|
+
]
|
575
|
+
]
|
576
|
+
location_keys = [
|
577
|
+
lk
|
578
|
+
for lk in location_keys
|
579
|
+
if lk in [
|
580
|
+
_lk['value']
|
581
|
+
for _lk in _locations_options
|
582
|
+
]
|
583
|
+
]
|
557
584
|
_connectors_datalist = [html.Option(value=o['value']) for o in _connectors_options]
|
558
585
|
_metrics_datalist = [html.Option(value=o['value']) for o in _metrics_options]
|
559
586
|
_locations_datalist = [html.Option(value=o['value']) for o in _locations_options]
|
@@ -680,6 +707,9 @@ dash_app.clientside_callback(
|
|
680
707
|
Input({'type': 'pipe-download-csv-button', 'index': ALL}, 'n_clicks'),
|
681
708
|
)
|
682
709
|
def download_pipe_csv(n_clicks):
|
710
|
+
"""
|
711
|
+
Download the most recent chunk as a CSV file.
|
712
|
+
"""
|
683
713
|
if not n_clicks:
|
684
714
|
raise PreventUpdate
|
685
715
|
ctx = dash.callback_context.triggered
|
@@ -818,6 +848,51 @@ def sync_documents_click(n_clicks, sync_editor_text):
|
|
818
848
|
return alert_from_success_tuple((success, msg))
|
819
849
|
|
820
850
|
|
851
|
+
dash_app.clientside_callback(
|
852
|
+
"""
|
853
|
+
function(n_clicks_arr, url){
|
854
|
+
display_block = {"display": "block"};
|
855
|
+
|
856
|
+
var clicked = false;
|
857
|
+
for (var i = 0; i < n_clicks_arr.length; i++){
|
858
|
+
if (n_clicks_arr[i]){
|
859
|
+
clicked = true;
|
860
|
+
break;
|
861
|
+
}
|
862
|
+
}
|
863
|
+
if (!clicked){ return dash_clientside.no_update; }
|
864
|
+
|
865
|
+
const triggered_id = dash_clientside.callback_context.triggered_id;
|
866
|
+
const action = triggered_id["action"];
|
867
|
+
const pipe_meta = JSON.parse(triggered_id["index"]);
|
868
|
+
|
869
|
+
iframe = document.getElementById('webterm-iframe');
|
870
|
+
if (!iframe){ return dash_clientside.no_update; }
|
871
|
+
var location = pipe_meta.location;
|
872
|
+
if (!pipe_meta.location){
|
873
|
+
location = "None";
|
874
|
+
}
|
875
|
+
|
876
|
+
iframe.contentWindow.postMessage(
|
877
|
+
{
|
878
|
+
action: action,
|
879
|
+
subaction: "pipes",
|
880
|
+
connector_keys: [pipe_meta.connector],
|
881
|
+
metric_keys: [pipe_meta.metric],
|
882
|
+
location_keys: [location],
|
883
|
+
instance: pipe_meta.instance,
|
884
|
+
},
|
885
|
+
url
|
886
|
+
);
|
887
|
+
dash_clientside.set_props("webterm-div", {"style": display_block});
|
888
|
+
return [];
|
889
|
+
}
|
890
|
+
""",
|
891
|
+
Output('content-div-right', 'children'),
|
892
|
+
Input({'type': 'manage-pipe-button', 'index': ALL, 'action': ALL}, 'n_clicks'),
|
893
|
+
State('location', 'href'),
|
894
|
+
)
|
895
|
+
|
821
896
|
@dash_app.callback(
|
822
897
|
Output("navbar-collapse", "is_open"),
|
823
898
|
[Input("navbar-toggler", "n_clicks")],
|
@@ -11,6 +11,7 @@ import json
|
|
11
11
|
import functools
|
12
12
|
import time
|
13
13
|
import traceback
|
14
|
+
from datetime import datetime, timezone
|
14
15
|
import meerschaum as mrsm
|
15
16
|
from meerschaum.utils.typing import Optional, Dict, Any
|
16
17
|
from meerschaum.api import get_api_connector, endpoints, CHECK_UPDATE
|
@@ -54,9 +55,14 @@ def download_job_logs(n_clicks):
|
|
54
55
|
component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
|
55
56
|
daemon_id = component_dict['index']
|
56
57
|
daemon = Daemon(daemon_id=daemon_id)
|
58
|
+
now = datetime.now(timezone.utc)
|
59
|
+
filename = (
|
60
|
+
daemon.rotating_log.file_path.name[:(-1 * len('.log'))]
|
61
|
+
+ '_' + str(int(now.timestamp())) + '.log'
|
62
|
+
)
|
57
63
|
return {
|
58
64
|
'content': daemon.log_text,
|
59
|
-
'filename':
|
65
|
+
'filename': filename,
|
60
66
|
}
|
61
67
|
|
62
68
|
|
@@ -74,7 +80,7 @@ def manage_job_button_click(
|
|
74
80
|
session_data: Optional[Dict[str, Any]] = None,
|
75
81
|
):
|
76
82
|
"""
|
77
|
-
Start, stop, or
|
83
|
+
Start, stop, pause, or delete the given job.
|
78
84
|
"""
|
79
85
|
if not n_clicks:
|
80
86
|
raise PreventUpdate
|
@@ -98,12 +104,18 @@ def manage_job_button_click(
|
|
98
104
|
component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
|
99
105
|
daemon_id = component_dict['index']
|
100
106
|
manage_job_action = component_dict['action']
|
101
|
-
|
107
|
+
try:
|
108
|
+
daemon = Daemon(daemon_id=daemon_id)
|
109
|
+
except Exception as e:
|
110
|
+
daemon = None
|
111
|
+
if daemon is None:
|
112
|
+
raise PreventUpdate
|
102
113
|
|
103
114
|
manage_functions = {
|
104
115
|
'start': functools.partial(daemon.run, allow_dirty_run=True),
|
105
116
|
'stop': daemon.quit,
|
106
117
|
'pause': daemon.pause,
|
118
|
+
'delete': daemon.cleanup,
|
107
119
|
}
|
108
120
|
if manage_job_action not in manage_functions:
|
109
121
|
return (
|
@@ -135,6 +147,46 @@ def manage_job_button_click(
|
|
135
147
|
build_process_timestamps_children(daemon),
|
136
148
|
)
|
137
149
|
|
150
|
+
dash_app.clientside_callback(
|
151
|
+
"""
|
152
|
+
function(n_clicks_arr, url){
|
153
|
+
display_block = {"display": "block"};
|
154
|
+
|
155
|
+
var clicked = false;
|
156
|
+
for (var i = 0; i < n_clicks_arr.length; i++){
|
157
|
+
if (n_clicks_arr[i]){
|
158
|
+
clicked = true;
|
159
|
+
break;
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
if (!clicked){
|
164
|
+
return dash_clientside.no_update;
|
165
|
+
}
|
166
|
+
|
167
|
+
const triggered_id = dash_clientside.callback_context.triggered_id;
|
168
|
+
const job_daemon_id = triggered_id["index"];
|
169
|
+
|
170
|
+
iframe = document.getElementById('webterm-iframe');
|
171
|
+
if (!iframe){ return dash_clientside.no_update; }
|
172
|
+
|
173
|
+
iframe.contentWindow.postMessage(
|
174
|
+
{
|
175
|
+
action: "show",
|
176
|
+
subaction: "logs",
|
177
|
+
subaction_text: job_daemon_id,
|
178
|
+
},
|
179
|
+
url
|
180
|
+
);
|
181
|
+
dash_clientside.set_props("webterm-div", {"style": display_block});
|
182
|
+
return [];
|
183
|
+
}
|
184
|
+
""",
|
185
|
+
Output('content-div-right', 'children'),
|
186
|
+
Input({'type': 'follow-logs-button', 'index': ALL}, 'n_clicks'),
|
187
|
+
State('location', 'href'),
|
188
|
+
)
|
189
|
+
|
138
190
|
|
139
191
|
@dash_app.callback(
|
140
192
|
Output({'type': 'manage-job-buttons-div', 'index': ALL}, 'children'),
|
meerschaum/api/dash/jobs.py
CHANGED
@@ -49,6 +49,17 @@ def get_jobs_cards(state: WebState):
|
|
49
49
|
build_process_timestamps_children(d),
|
50
50
|
id = {'type': 'process-timestamps-div', 'index': d.daemon_id},
|
51
51
|
)
|
52
|
+
follow_logs_button = dbc.DropdownMenuItem(
|
53
|
+
"Follow logs",
|
54
|
+
id = {'type': 'follow-logs-button', 'index': d.daemon_id},
|
55
|
+
)
|
56
|
+
download_logs_button = dbc.DropdownMenuItem(
|
57
|
+
"Download logs",
|
58
|
+
id = {'type': 'job-download-logs-button', 'index': d.daemon_id},
|
59
|
+
)
|
60
|
+
logs_menu_children = (
|
61
|
+
([follow_logs_button] if is_authenticated else []) + [download_logs_button]
|
62
|
+
)
|
52
63
|
header_children = [
|
53
64
|
html.Div(
|
54
65
|
build_status_children(d),
|
@@ -56,11 +67,13 @@ def get_jobs_cards(state: WebState):
|
|
56
67
|
style = {'float': 'left'},
|
57
68
|
),
|
58
69
|
html.Div(
|
59
|
-
dbc.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
70
|
+
dbc.DropdownMenu(
|
71
|
+
logs_menu_children,
|
72
|
+
label = "Logs",
|
73
|
+
size = "sm",
|
74
|
+
align_end = True,
|
75
|
+
color = "secondary",
|
76
|
+
menu_variant = 'dark',
|
64
77
|
),
|
65
78
|
style = {'float': 'right'},
|
66
79
|
),
|
@@ -74,7 +87,7 @@ def get_jobs_cards(state: WebState):
|
|
74
87
|
className = "card-text job-card-text",
|
75
88
|
style = {"word-wrap": "break-word"}
|
76
89
|
),
|
77
|
-
style={"white-space": "pre-wrap"},
|
90
|
+
style = {"white-space": "pre-wrap"},
|
78
91
|
),
|
79
92
|
html.Div(
|
80
93
|
(
|
@@ -82,7 +95,7 @@ def get_jobs_cards(state: WebState):
|
|
82
95
|
if is_authenticated
|
83
96
|
else []
|
84
97
|
),
|
85
|
-
id={'type': 'manage-job-buttons-div', 'index': d.daemon_id}
|
98
|
+
id = {'type': 'manage-job-buttons-div', 'index': d.daemon_id},
|
86
99
|
),
|
87
100
|
html.Div(id={'type': 'manage-job-alert-div', 'index': d.daemon_id}),
|
88
101
|
]
|
@@ -108,7 +121,7 @@ def build_manage_job_buttons_div_children(daemon: Daemon):
|
|
108
121
|
return [
|
109
122
|
html.Br(),
|
110
123
|
dbc.Row([
|
111
|
-
dbc.Col(button, width=
|
124
|
+
dbc.Col(button, width=6)
|
112
125
|
for button in buttons
|
113
126
|
])
|
114
127
|
]
|
@@ -153,9 +166,22 @@ def build_manage_job_buttons(daemon: Daemon):
|
|
153
166
|
'index': daemon.daemon_id,
|
154
167
|
},
|
155
168
|
)
|
169
|
+
delete_button = dbc.Button(
|
170
|
+
'Delete',
|
171
|
+
size = 'sm',
|
172
|
+
color = 'danger',
|
173
|
+
style = {'width': '100%'},
|
174
|
+
id = {
|
175
|
+
'type': 'manage-job-button',
|
176
|
+
'action': 'delete',
|
177
|
+
'index': daemon.daemon_id,
|
178
|
+
},
|
179
|
+
)
|
156
180
|
buttons = []
|
157
181
|
if daemon.status in ('stopped', 'paused'):
|
158
182
|
buttons.append(start_button)
|
183
|
+
if daemon.status == 'stopped':
|
184
|
+
buttons.append(delete_button)
|
159
185
|
if daemon.status in ('running',):
|
160
186
|
buttons.append(pause_button)
|
161
187
|
if daemon.status in ('running', 'paused'):
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -115,16 +115,77 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
115
115
|
if not isinstance(_pipes, list):
|
116
116
|
_pipes = []
|
117
117
|
for p in _pipes:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
118
|
+
meta_str = json.dumps(p.meta)
|
119
|
+
footer_children = dbc.Row(
|
120
|
+
[
|
121
|
+
dbc.Col(
|
122
|
+
(
|
123
|
+
dbc.DropdownMenu(
|
124
|
+
label = "Manage",
|
125
|
+
children = [
|
126
|
+
dbc.DropdownMenuItem(
|
127
|
+
'Delete',
|
128
|
+
id = {
|
129
|
+
'type': 'manage-pipe-button',
|
130
|
+
'index': meta_str,
|
131
|
+
'action': 'delete',
|
132
|
+
},
|
133
|
+
),
|
134
|
+
dbc.DropdownMenuItem(
|
135
|
+
'Drop',
|
136
|
+
id = {
|
137
|
+
'type': 'manage-pipe-button',
|
138
|
+
'index': meta_str,
|
139
|
+
'action': 'drop',
|
140
|
+
},
|
141
|
+
),
|
142
|
+
dbc.DropdownMenuItem(
|
143
|
+
'Clear',
|
144
|
+
id = {
|
145
|
+
'type': 'manage-pipe-button',
|
146
|
+
'index': meta_str,
|
147
|
+
'action': 'clear',
|
148
|
+
},
|
149
|
+
),
|
150
|
+
dbc.DropdownMenuItem(
|
151
|
+
'Verify',
|
152
|
+
id = {
|
153
|
+
'type': 'manage-pipe-button',
|
154
|
+
'index': meta_str,
|
155
|
+
'action': 'verify',
|
156
|
+
},
|
157
|
+
),
|
158
|
+
dbc.DropdownMenuItem(
|
159
|
+
'Sync',
|
160
|
+
id = {
|
161
|
+
'type': 'manage-pipe-button',
|
162
|
+
'index': meta_str,
|
163
|
+
'action': 'sync',
|
164
|
+
},
|
165
|
+
),
|
166
|
+
],
|
167
|
+
direction = "up",
|
168
|
+
menu_variant = "dark",
|
169
|
+
size = 'sm',
|
170
|
+
color = 'secondary',
|
171
|
+
)
|
172
|
+
) if authenticated else [],
|
173
|
+
width = 2,
|
174
|
+
),
|
175
|
+
dbc.Col(width=6),
|
176
|
+
dbc.Col(
|
177
|
+
dbc.Button(
|
178
|
+
'Download CSV',
|
179
|
+
size = 'sm',
|
180
|
+
color = 'link',
|
181
|
+
style = {'float': 'right'},
|
182
|
+
id = {'type': 'pipe-download-csv-button', 'index': meta_str},
|
183
|
+
),
|
184
|
+
width = 4,
|
185
|
+
),
|
186
|
+
],
|
187
|
+
justify = 'start',
|
188
|
+
)
|
128
189
|
card_body_children = [
|
129
190
|
html.H5(
|
130
191
|
html.B(str(p)),
|
@@ -136,7 +197,7 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
136
197
|
accordion_items_from_pipe(p, authenticated=authenticated),
|
137
198
|
flush = True,
|
138
199
|
start_collapsed = True,
|
139
|
-
id = {'type': 'pipe-accordion', 'index':
|
200
|
+
id = {'type': 'pipe-accordion', 'index': meta_str},
|
140
201
|
)
|
141
202
|
)
|
142
203
|
|
@@ -188,14 +249,40 @@ def accordion_items_from_pipe(
|
|
188
249
|
overview_header = [html.Thead(html.Tr([html.Th("Attribute"), html.Th("Value")]))]
|
189
250
|
dt_name, id_name, val_name = pipe.get_columns('datetime', 'id', 'value', error=False)
|
190
251
|
overview_rows = [
|
191
|
-
html.Tr([html.Td("Connector"), html.Td(f"{pipe.connector_keys}")]),
|
192
|
-
html.Tr([html.Td("Metric"), html.Td(f"{pipe.metric_key}")]),
|
193
|
-
html.Tr([html.Td("Location"), html.Td(f"{pipe.location_key}")]),
|
194
|
-
html.Tr([html.Td("Instance"), html.Td(f"{pipe.instance_keys}")]),
|
195
|
-
html.Tr([html.Td("Target Table"), html.Td(f"{pipe.target}")]),
|
252
|
+
html.Tr([html.Td("Connector"), html.Td(html.Pre(f"{pipe.connector_keys}"))]),
|
253
|
+
html.Tr([html.Td("Metric"), html.Td(html.Pre(f"{pipe.metric_key}"))]),
|
254
|
+
html.Tr([html.Td("Location"), html.Td(html.Pre(f"{pipe.location_key}"))]),
|
255
|
+
html.Tr([html.Td("Instance"), html.Td(html.Pre(f"{pipe.instance_keys}"))]),
|
256
|
+
html.Tr([html.Td("Target Table"), html.Td(html.Pre(f"{pipe.target}"))]),
|
196
257
|
]
|
197
|
-
|
198
|
-
|
258
|
+
columns = pipe.columns.copy()
|
259
|
+
if columns:
|
260
|
+
datetime_index = columns.pop('datetime', None)
|
261
|
+
columns_items = []
|
262
|
+
if datetime_index:
|
263
|
+
columns_items.append(html.Li(f"{datetime_index} (datetime)"))
|
264
|
+
columns_items.extend([
|
265
|
+
html.Li(f"{col}")
|
266
|
+
for col_key, col in columns.items()
|
267
|
+
])
|
268
|
+
overview_rows.append(
|
269
|
+
html.Tr([
|
270
|
+
html.Td("Indices" if len(columns_items) != 1 else "Index"),
|
271
|
+
html.Td(html.Pre(html.Ul(columns_items))),
|
272
|
+
])
|
273
|
+
)
|
274
|
+
tags = pipe.tags
|
275
|
+
if tags:
|
276
|
+
tags_items = html.Ul([
|
277
|
+
html.Li(tag)
|
278
|
+
for tag in tags
|
279
|
+
])
|
280
|
+
overview_rows.append(
|
281
|
+
html.Tr([
|
282
|
+
html.Td("Tags"),
|
283
|
+
html.Td(html.Pre(tags_items)),
|
284
|
+
])
|
285
|
+
)
|
199
286
|
|
200
287
|
items_bodies['overview'] = dbc.Table(
|
201
288
|
overview_header + [html.Tbody(overview_rows)],
|