meerschaum 2.8.3__py3-none-any.whl → 2.9.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.
- meerschaum/_internal/arguments/_parser.py +5 -0
- meerschaum/actions/drop.py +1 -1
- meerschaum/actions/start.py +14 -6
- meerschaum/actions/sync.py +9 -0
- meerschaum/api/__init__.py +9 -3
- meerschaum/api/_chunks.py +67 -0
- meerschaum/api/dash/callbacks/__init__.py +5 -2
- meerschaum/api/dash/callbacks/custom.py +21 -8
- meerschaum/api/dash/callbacks/dashboard.py +26 -4
- meerschaum/api/dash/callbacks/settings/__init__.py +8 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +76 -0
- meerschaum/api/dash/components.py +136 -25
- meerschaum/api/dash/pages/__init__.py +1 -0
- meerschaum/api/dash/pages/dashboard.py +11 -9
- meerschaum/api/dash/pages/plugins.py +31 -27
- meerschaum/api/dash/pages/settings/__init__.py +8 -0
- meerschaum/api/dash/pages/settings/password_reset.py +63 -0
- meerschaum/api/dash/webterm.py +6 -3
- meerschaum/api/resources/static/css/dash.css +8 -1
- meerschaum/api/resources/templates/termpage.html +4 -0
- meerschaum/api/routes/_pipes.py +234 -82
- meerschaum/config/_default.py +4 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/__init__.py +1 -0
- meerschaum/connectors/api/_APIConnector.py +12 -1
- meerschaum/connectors/api/_pipes.py +106 -45
- meerschaum/connectors/api/_plugins.py +51 -45
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/parse.py +1 -2
- meerschaum/connectors/sql/_SQLConnector.py +4 -1
- meerschaum/connectors/sql/_cli.py +1 -0
- meerschaum/connectors/sql/_create_engine.py +51 -4
- meerschaum/connectors/sql/_pipes.py +38 -6
- meerschaum/connectors/sql/_sql.py +35 -4
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -0
- meerschaum/connectors/valkey/_pipes.py +51 -39
- meerschaum/core/Pipe/__init__.py +1 -0
- meerschaum/core/Pipe/_data.py +1 -2
- meerschaum/core/Pipe/_sync.py +64 -4
- meerschaum/core/Pipe/_verify.py +23 -8
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/plugins/_Plugin.py +21 -5
- meerschaum/plugins/__init__.py +32 -8
- meerschaum/utils/dataframe.py +139 -2
- meerschaum/utils/dtypes/__init__.py +211 -1
- meerschaum/utils/dtypes/sql.py +296 -5
- meerschaum/utils/formatting/_shell.py +1 -4
- meerschaum/utils/misc.py +1 -1
- meerschaum/utils/packages/_packages.py +8 -2
- meerschaum/utils/process.py +27 -3
- meerschaum/utils/schedule.py +3 -3
- meerschaum/utils/sql.py +140 -12
- meerschaum/utils/venv/__init__.py +10 -2
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/METADATA +17 -3
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/RECORD +61 -61
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/WHEEL +1 -1
- meerschaum/_internal/gui/__init__.py +0 -43
- meerschaum/_internal/gui/app/__init__.py +0 -50
- meerschaum/_internal/gui/app/_windows.py +0 -74
- meerschaum/_internal/gui/app/actions.py +0 -30
- meerschaum/_internal/gui/app/pipes.py +0 -47
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.8.3.dist-info → meerschaum-2.9.0.dist-info}/zip-safe +0 -0
meerschaum/api/routes/_pipes.py
CHANGED
@@ -27,8 +27,10 @@ from meerschaum.api import (
|
|
27
27
|
debug,
|
28
28
|
no_auth,
|
29
29
|
)
|
30
|
+
from meerschaum.api._chunks import generate_chunks_cursor_token
|
30
31
|
from meerschaum.utils.packages import attempt_import
|
31
32
|
from meerschaum.utils.dataframe import to_json
|
33
|
+
from meerschaum.utils.dtypes import are_dtypes_equal, json_serialize_value
|
32
34
|
from meerschaum.utils.misc import (
|
33
35
|
is_pipe_registered,
|
34
36
|
is_int,
|
@@ -38,14 +40,16 @@ from meerschaum.connectors.sql.tables import get_tables
|
|
38
40
|
|
39
41
|
fastapi_responses = attempt_import('fastapi.responses', lazy=False)
|
40
42
|
StreamingResponse = fastapi_responses.StreamingResponse
|
41
|
-
dateutil_parser = attempt_import('dateutil.parser', lazy=False)
|
42
43
|
pipes_endpoint = endpoints['pipes']
|
43
44
|
pd = attempt_import('pandas', lazy=False)
|
44
45
|
|
45
46
|
MAX_RESPONSE_ROW_LIMIT: int = mrsm.get_config('system', 'api', 'data', 'max_response_row_limit')
|
46
47
|
|
47
48
|
|
48
|
-
@app.post(
|
49
|
+
@app.post(
|
50
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/register',
|
51
|
+
tags=['Pipes: Attributes'],
|
52
|
+
)
|
49
53
|
def register_pipe(
|
50
54
|
connector_keys: str,
|
51
55
|
metric_key: str,
|
@@ -80,18 +84,23 @@ def register_pipe(
|
|
80
84
|
return results
|
81
85
|
|
82
86
|
|
83
|
-
@app.
|
84
|
-
|
87
|
+
@app.patch(
|
88
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/edit',
|
89
|
+
tags=['Pipes: Attributes'],
|
90
|
+
)
|
91
|
+
def edit_pipe(
|
85
92
|
connector_keys: str,
|
86
93
|
metric_key: str,
|
87
94
|
location_key: str,
|
95
|
+
parameters: dict,
|
88
96
|
instance_keys: Optional[str] = None,
|
97
|
+
patch: bool = False,
|
89
98
|
curr_user = (
|
90
99
|
fastapi.Depends(manager) if not no_auth else None
|
91
100
|
),
|
92
101
|
):
|
93
102
|
"""
|
94
|
-
|
103
|
+
Edit an existing pipe's parameters.
|
95
104
|
"""
|
96
105
|
allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
|
97
106
|
if not allow_actions:
|
@@ -107,54 +116,27 @@ def delete_pipe(
|
|
107
116
|
raise fastapi.HTTPException(
|
108
117
|
status_code=409, detail=f"{pipe} is not registered."
|
109
118
|
)
|
110
|
-
|
111
|
-
|
112
|
-
return results
|
113
|
-
|
114
|
-
|
115
|
-
@app.delete(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/drop', tags=['Pipes'])
|
116
|
-
def drop_pipe(
|
117
|
-
connector_keys: str,
|
118
|
-
metric_key: str,
|
119
|
-
location_key: str,
|
120
|
-
instance_keys: Optional[str] = None,
|
121
|
-
curr_user = (
|
122
|
-
fastapi.Depends(manager) if not no_auth else None
|
123
|
-
),
|
124
|
-
):
|
125
|
-
"""
|
126
|
-
Drop a pipes' underlying target table.
|
127
|
-
"""
|
128
|
-
allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
|
129
|
-
if not allow_actions:
|
130
|
-
return False, (
|
131
|
-
"The administrator for this server has not allowed actions.\n\n"
|
132
|
-
"Please contact the system administrator, or if you are running this server, "
|
133
|
-
"open the configuration file with `edit config system` and search for 'permissions'."
|
134
|
-
" Under the keys `api:permissions:actions`, " +
|
135
|
-
"you can toggle non-admin actions."
|
136
|
-
)
|
137
|
-
pipe_object = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
138
|
-
results = get_api_connector(instance_keys=instance_keys).drop_pipe(pipe_object, debug=debug)
|
119
|
+
pipe.parameters = parameters
|
120
|
+
results = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
|
139
121
|
pipes(instance_keys, refresh=True)
|
140
122
|
return results
|
141
123
|
|
142
124
|
|
143
|
-
|
144
|
-
|
145
|
-
|
125
|
+
@app.delete(
|
126
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/delete',
|
127
|
+
tags=['Pipes: Attributes'],
|
128
|
+
)
|
129
|
+
def delete_pipe(
|
146
130
|
connector_keys: str,
|
147
131
|
metric_key: str,
|
148
132
|
location_key: str,
|
149
|
-
parameters: dict,
|
150
133
|
instance_keys: Optional[str] = None,
|
151
|
-
patch: bool = False,
|
152
134
|
curr_user = (
|
153
135
|
fastapi.Depends(manager) if not no_auth else None
|
154
136
|
),
|
155
137
|
):
|
156
138
|
"""
|
157
|
-
|
139
|
+
Delete a Pipe (without dropping its table).
|
158
140
|
"""
|
159
141
|
allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
|
160
142
|
if not allow_actions:
|
@@ -170,13 +152,12 @@ def edit_pipe(
|
|
170
152
|
raise fastapi.HTTPException(
|
171
153
|
status_code=409, detail=f"{pipe} is not registered."
|
172
154
|
)
|
173
|
-
pipe
|
174
|
-
results = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
|
155
|
+
results = get_api_connector(instance_keys).delete_pipe(pipe, debug=debug)
|
175
156
|
pipes(instance_keys, refresh=True)
|
176
157
|
return results
|
177
158
|
|
178
159
|
|
179
|
-
@app.get(pipes_endpoint + '/keys', tags=['Pipes'])
|
160
|
+
@app.get(pipes_endpoint + '/keys', tags=['Pipes: Attributes'])
|
180
161
|
async def fetch_pipes_keys(
|
181
162
|
connector_keys: str = "[]",
|
182
163
|
metric_keys: str = "[]",
|
@@ -201,7 +182,7 @@ async def fetch_pipes_keys(
|
|
201
182
|
return keys
|
202
183
|
|
203
184
|
|
204
|
-
@app.get(pipes_endpoint, tags=['Pipes'])
|
185
|
+
@app.get(pipes_endpoint, tags=['Pipes: Attributes'])
|
205
186
|
async def get_pipes(
|
206
187
|
connector_keys: str = "",
|
207
188
|
metric_keys: str = "",
|
@@ -225,7 +206,7 @@ async def get_pipes(
|
|
225
206
|
return replace_pipes_in_dict(_get_pipes(**kw), lambda p: p.attributes)
|
226
207
|
|
227
208
|
|
228
|
-
@app.get(pipes_endpoint + '/{connector_keys}', tags=['Pipes'])
|
209
|
+
@app.get(pipes_endpoint + '/{connector_keys}', tags=['Pipes: Attributes'])
|
229
210
|
async def get_pipes_by_connector(
|
230
211
|
connector_keys: str,
|
231
212
|
instance_keys: Optional[str] = None,
|
@@ -243,7 +224,7 @@ async def get_pipes_by_connector(
|
|
243
224
|
return replace_pipes_in_dict(pipes(instance_keys)[connector_keys], lambda p: p.attributes)
|
244
225
|
|
245
226
|
|
246
|
-
@app.get(pipes_endpoint + '/{connector_keys}/{metric_key}', tags=['Pipes'])
|
227
|
+
@app.get(pipes_endpoint + '/{connector_keys}/{metric_key}', tags=['Pipes: Attributes'])
|
247
228
|
async def get_pipes_by_connector_and_metric(
|
248
229
|
connector_keys: str,
|
249
230
|
metric_key: str,
|
@@ -271,8 +252,11 @@ async def get_pipes_by_connector_and_metric(
|
|
271
252
|
)
|
272
253
|
|
273
254
|
|
274
|
-
@app.get(
|
275
|
-
|
255
|
+
@app.get(
|
256
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}',
|
257
|
+
tags=['Pipes: Attributes'],
|
258
|
+
)
|
259
|
+
async def get_pipe_by_connector_and_metric_and_location(
|
276
260
|
connector_keys: str,
|
277
261
|
metric_key: str,
|
278
262
|
location_key: str,
|
@@ -299,10 +283,13 @@ async def get_pipes_by_connector_and_metric_and_location(
|
|
299
283
|
detail=f"location_key '{location_key}' not found."
|
300
284
|
)
|
301
285
|
|
302
|
-
return
|
286
|
+
return pipes(instance_keys)[connector_keys][metric_key][location_key].attributes
|
303
287
|
|
304
288
|
|
305
|
-
@app.get(
|
289
|
+
@app.get(
|
290
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/sync_time',
|
291
|
+
tags=['Pipes: Data'],
|
292
|
+
)
|
306
293
|
def get_sync_time(
|
307
294
|
connector_keys: str,
|
308
295
|
metric_key: str,
|
@@ -333,7 +320,10 @@ def get_sync_time(
|
|
333
320
|
return sync_time
|
334
321
|
|
335
322
|
|
336
|
-
@app.post(
|
323
|
+
@app.post(
|
324
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data',
|
325
|
+
tags=['Pipes: Data'],
|
326
|
+
)
|
337
327
|
def sync_pipe(
|
338
328
|
connector_keys: str,
|
339
329
|
metric_key: str,
|
@@ -377,7 +367,10 @@ def sync_pipe(
|
|
377
367
|
return list((success, msg))
|
378
368
|
|
379
369
|
|
380
|
-
@app.get(
|
370
|
+
@app.get(
|
371
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data',
|
372
|
+
tags=['Pipes: Data'],
|
373
|
+
)
|
381
374
|
def get_pipe_data(
|
382
375
|
connector_keys: str,
|
383
376
|
metric_key: str,
|
@@ -389,6 +382,11 @@ def get_pipe_data(
|
|
389
382
|
end: Union[str, int, None] = None,
|
390
383
|
params: Optional[str] = None,
|
391
384
|
limit: int = MAX_RESPONSE_ROW_LIMIT,
|
385
|
+
order: str = 'asc',
|
386
|
+
date_format: str = 'iso',
|
387
|
+
date_unit: str = 'us',
|
388
|
+
double_precision: int = 15,
|
389
|
+
geometry_format: str = 'wkb_hex',
|
392
390
|
curr_user = (
|
393
391
|
fastapi.Depends(manager) if not no_auth else None
|
394
392
|
),
|
@@ -398,11 +396,28 @@ def get_pipe_data(
|
|
398
396
|
See [`Pipe.get_data()`](https://docs.meerschaum.io/meerschaum.html#Pipe.get_data).
|
399
397
|
|
400
398
|
Note that `select_columns`, `omit_columns`, and `params` are JSON-encoded strings.
|
399
|
+
|
400
|
+
Parameters
|
401
|
+
----------
|
402
|
+
instance_keys: Optional[str], default None
|
403
|
+
The connector key to the instance on which the pipe is registered.
|
404
|
+
Defaults to the configured value for `meerschaum:api_instance`.
|
405
|
+
|
406
|
+
date_format: str, default 'iso'
|
407
|
+
Serialzation format for datetime values.
|
408
|
+
Accepted values are `'iso`' (ISO8601) and `'epoch'` (epoch milliseconds).
|
409
|
+
|
410
|
+
date_unit: str, default 'us'
|
411
|
+
Timestamp precision for serialization. Accepted values are `'s'` (seconds),
|
412
|
+
`'ms'` (milliseconds), `'us'` (microseconds), and `'ns'`.
|
413
|
+
|
414
|
+
double_precision: int, default 15
|
415
|
+
The number of decimal places to use when encoding floating point values (maximum `15`).
|
416
|
+
|
417
|
+
geometry_format: str, default 'wkb_hex'
|
418
|
+
The serialization format for geometry data.
|
419
|
+
Accepted values are `geojson`, `wkb_hex`, and `wkt`.
|
401
420
|
"""
|
402
|
-
if is_int(begin):
|
403
|
-
begin = int(begin)
|
404
|
-
if is_int(end):
|
405
|
-
end = int(end)
|
406
421
|
if limit > MAX_RESPONSE_ROW_LIMIT:
|
407
422
|
raise fastapi.HTTPException(
|
408
423
|
status_code=413,
|
@@ -455,13 +470,14 @@ def get_pipe_data(
|
|
455
470
|
)
|
456
471
|
|
457
472
|
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
473
|
+
begin, end = pipe.parse_date_bounds(begin, end)
|
458
474
|
if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
|
459
475
|
raise fastapi.HTTPException(
|
460
476
|
status_code=409,
|
461
477
|
detail="Pipe must be registered with the datetime column specified."
|
462
478
|
)
|
463
479
|
|
464
|
-
if pipe.target in ('
|
480
|
+
if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes'):
|
465
481
|
raise fastapi.HTTPException(
|
466
482
|
status_code=409,
|
467
483
|
detail=f"Cannot retrieve data from protected table '{pipe.target}'.",
|
@@ -474,6 +490,7 @@ def get_pipe_data(
|
|
474
490
|
end=end,
|
475
491
|
params=_params,
|
476
492
|
limit=min(limit, MAX_RESPONSE_ROW_LIMIT),
|
493
|
+
order=order,
|
477
494
|
debug=debug,
|
478
495
|
)
|
479
496
|
if df is None:
|
@@ -482,14 +499,152 @@ def get_pipe_data(
|
|
482
499
|
detail="Could not fetch data with the given parameters.",
|
483
500
|
)
|
484
501
|
|
485
|
-
json_content = to_json(
|
502
|
+
json_content = to_json(
|
503
|
+
df,
|
504
|
+
date_format=date_format,
|
505
|
+
date_unit=date_unit,
|
506
|
+
geometry_format=geometry_format,
|
507
|
+
double_precision=double_precision,
|
508
|
+
)
|
486
509
|
return fastapi.Response(
|
487
510
|
json_content,
|
488
511
|
media_type='application/json',
|
489
512
|
)
|
490
513
|
|
491
514
|
|
492
|
-
@app.get(
|
515
|
+
@app.get(
|
516
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/chunk_bounds',
|
517
|
+
tags=['Pipes: Data'],
|
518
|
+
)
|
519
|
+
def get_pipe_chunk_bounds(
|
520
|
+
connector_keys: str,
|
521
|
+
metric_key: str,
|
522
|
+
location_key: str,
|
523
|
+
instance_keys: Optional[str] = None,
|
524
|
+
begin: Union[str, int, None] = None,
|
525
|
+
end: Union[str, int, None] = None,
|
526
|
+
bounded: bool = True,
|
527
|
+
chunk_interval_minutes: Union[int, None] = None,
|
528
|
+
) -> List[List[Union[str, int, None]]]:
|
529
|
+
"""
|
530
|
+
Return a list of request boundaries between `begin` and `end` (or the pipe's sync times).
|
531
|
+
Optionally specify the interval between chunk bounds
|
532
|
+
(defaults to the pipe's configured chunk interval).
|
533
|
+
"""
|
534
|
+
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
535
|
+
begin, end = pipe.parse_date_bounds(begin, end)
|
536
|
+
dt_col = pipe.columns.get('datetime', None)
|
537
|
+
dt_typ = pipe.dtypes.get(dt_col, 'datetime')
|
538
|
+
chunk_interval = None if chunk_interval_minutes is None else (
|
539
|
+
chunk_interval_minutes
|
540
|
+
if are_dtypes_equal(dt_typ, 'int')
|
541
|
+
else timedelta(minutes=chunk_interval_minutes)
|
542
|
+
)
|
543
|
+
|
544
|
+
chunk_bounds = pipe.get_chunk_bounds(
|
545
|
+
begin=begin,
|
546
|
+
end=end,
|
547
|
+
bounded=bounded,
|
548
|
+
chunk_interval=chunk_interval,
|
549
|
+
debug=debug,
|
550
|
+
)
|
551
|
+
|
552
|
+
return fastapi.Response(
|
553
|
+
json.dumps(chunk_bounds, default=json_serialize_value),
|
554
|
+
media_type='application/json',
|
555
|
+
)
|
556
|
+
|
557
|
+
|
558
|
+
@app.delete(
|
559
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/drop',
|
560
|
+
tags=['Pipes: Data'],
|
561
|
+
)
|
562
|
+
def drop_pipe(
|
563
|
+
connector_keys: str,
|
564
|
+
metric_key: str,
|
565
|
+
location_key: str,
|
566
|
+
instance_keys: Optional[str] = None,
|
567
|
+
curr_user = (
|
568
|
+
fastapi.Depends(manager) if not no_auth else None
|
569
|
+
),
|
570
|
+
):
|
571
|
+
"""
|
572
|
+
Drop a pipe's target table.
|
573
|
+
"""
|
574
|
+
allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
|
575
|
+
if not allow_actions:
|
576
|
+
return False, (
|
577
|
+
"The administrator for this server has not allowed actions.\n\n"
|
578
|
+
"Please contact the system administrator, or if you are running this server, "
|
579
|
+
"open the configuration file with `edit config system` and search for 'permissions'."
|
580
|
+
" Under the keys `api:permissions:actions`, " +
|
581
|
+
"you can toggle non-admin actions."
|
582
|
+
)
|
583
|
+
pipe_object = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
584
|
+
results = get_api_connector(instance_keys=instance_keys).drop_pipe(pipe_object, debug=debug)
|
585
|
+
pipes(instance_keys, refresh=True)
|
586
|
+
return results
|
587
|
+
|
588
|
+
|
589
|
+
@app.delete(
|
590
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/clear',
|
591
|
+
tags=['Pipes: Data'],
|
592
|
+
)
|
593
|
+
def clear_pipe(
|
594
|
+
connector_keys: str,
|
595
|
+
metric_key: str,
|
596
|
+
location_key: str,
|
597
|
+
instance_keys: Optional[str] = None,
|
598
|
+
begin: Union[str, int, None] = None,
|
599
|
+
end: Union[str, int, None] = None,
|
600
|
+
params: Optional[str] = None,
|
601
|
+
curr_user = (
|
602
|
+
fastapi.Depends(manager) if not no_auth else None
|
603
|
+
),
|
604
|
+
):
|
605
|
+
"""
|
606
|
+
Delete rows from a pipe's target table.
|
607
|
+
"""
|
608
|
+
_params = {}
|
609
|
+
if params == 'null':
|
610
|
+
params = None
|
611
|
+
if params is not None:
|
612
|
+
try:
|
613
|
+
_params = json.loads(params)
|
614
|
+
except Exception:
|
615
|
+
_params = None
|
616
|
+
if not isinstance(_params, dict):
|
617
|
+
raise fastapi.HTTPException(
|
618
|
+
status_code=409,
|
619
|
+
detail="Params must be a valid JSON-encoded dictionary.",
|
620
|
+
)
|
621
|
+
|
622
|
+
allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
|
623
|
+
if not allow_actions:
|
624
|
+
return False, (
|
625
|
+
"The administrator for this server has not allowed actions.\n\n"
|
626
|
+
"Please contact the system administrator, or if you are running this server, "
|
627
|
+
"open the configuration file with `edit config system` and search for 'permissions'."
|
628
|
+
" Under the keys `api:permissions:actions`, " +
|
629
|
+
"you can toggle non-admin actions."
|
630
|
+
)
|
631
|
+
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
632
|
+
begin, end = pipe.parse_date_bounds(begin, end)
|
633
|
+
results = get_api_connector(instance_keys=instance_keys).clear_pipe(
|
634
|
+
pipe,
|
635
|
+
begin=begin,
|
636
|
+
end=end,
|
637
|
+
params=_params,
|
638
|
+
debug=debug,
|
639
|
+
)
|
640
|
+
pipes(instance_keys, refresh=True)
|
641
|
+
return results
|
642
|
+
|
643
|
+
|
644
|
+
@app.get(
|
645
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/csv',
|
646
|
+
tags=['Pipes: Data'],
|
647
|
+
)
|
493
648
|
def get_pipe_csv(
|
494
649
|
connector_keys: str,
|
495
650
|
metric_key: str,
|
@@ -505,24 +660,11 @@ def get_pipe_csv(
|
|
505
660
|
"""
|
506
661
|
Get a pipe's data as a CSV file. Optionally set query boundaries.
|
507
662
|
"""
|
508
|
-
if begin is not None:
|
509
|
-
begin = (
|
510
|
-
int(begin)
|
511
|
-
if is_int(begin)
|
512
|
-
else dateutil_parser.parse(begin)
|
513
|
-
)
|
514
|
-
if end is not None:
|
515
|
-
end = (
|
516
|
-
int(end)
|
517
|
-
if is_int(end)
|
518
|
-
else dateutil_parser.parse(end)
|
519
|
-
)
|
520
663
|
|
521
664
|
_params = {}
|
522
665
|
if params == 'null':
|
523
666
|
params = None
|
524
667
|
if params is not None:
|
525
|
-
import json
|
526
668
|
try:
|
527
669
|
_params = json.loads(params)
|
528
670
|
except Exception:
|
@@ -541,6 +683,7 @@ def get_pipe_csv(
|
|
541
683
|
detail="Pipe must be registered."
|
542
684
|
)
|
543
685
|
|
686
|
+
begin, end = pipe.parse_date_bounds(begin, end)
|
544
687
|
dt_col = pipe.columns.get('datetime', None)
|
545
688
|
if dt_col:
|
546
689
|
if begin is None:
|
@@ -568,7 +711,10 @@ def get_pipe_csv(
|
|
568
711
|
return response
|
569
712
|
|
570
713
|
|
571
|
-
@app.get(
|
714
|
+
@app.get(
|
715
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/id',
|
716
|
+
tags=['Pipes: Attributes'],
|
717
|
+
)
|
572
718
|
def get_pipe_id(
|
573
719
|
connector_keys: str,
|
574
720
|
metric_key: str,
|
@@ -589,7 +735,7 @@ def get_pipe_id(
|
|
589
735
|
|
590
736
|
@app.get(
|
591
737
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/attributes',
|
592
|
-
tags=['Pipes']
|
738
|
+
tags=['Pipes: Attributes'],
|
593
739
|
)
|
594
740
|
def get_pipe_attributes(
|
595
741
|
connector_keys: str,
|
@@ -610,7 +756,10 @@ def get_pipe_attributes(
|
|
610
756
|
).attributes
|
611
757
|
|
612
758
|
|
613
|
-
@app.get(
|
759
|
+
@app.get(
|
760
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/exists',
|
761
|
+
tags=['Pipes: Data'],
|
762
|
+
)
|
614
763
|
def get_pipe_exists(
|
615
764
|
connector_keys: str,
|
616
765
|
metric_key: str,
|
@@ -624,7 +773,7 @@ def get_pipe_exists(
|
|
624
773
|
return get_pipe(connector_keys, metric_key, location_key, instance_keys).exists(debug=debug)
|
625
774
|
|
626
775
|
|
627
|
-
@app.post(endpoints['metadata'], tags=['
|
776
|
+
@app.post(endpoints['metadata'], tags=['Misc'])
|
628
777
|
def create_metadata(
|
629
778
|
instance_keys: Optional[str] = None,
|
630
779
|
curr_user = (
|
@@ -642,7 +791,10 @@ def create_metadata(
|
|
642
791
|
return True
|
643
792
|
|
644
793
|
|
645
|
-
@app.get(
|
794
|
+
@app.get(
|
795
|
+
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/rowcount',
|
796
|
+
tags=['Pipes: Data'],
|
797
|
+
)
|
646
798
|
def get_pipe_rowcount(
|
647
799
|
connector_keys: str,
|
648
800
|
metric_key: str,
|
@@ -693,7 +845,7 @@ def get_pipe_rowcount(
|
|
693
845
|
|
694
846
|
@app.get(
|
695
847
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/columns/types',
|
696
|
-
tags=['Pipes']
|
848
|
+
tags=['Pipes: Data'],
|
697
849
|
)
|
698
850
|
def get_pipe_columns_types(
|
699
851
|
connector_keys: str,
|
@@ -721,7 +873,7 @@ def get_pipe_columns_types(
|
|
721
873
|
|
722
874
|
@app.get(
|
723
875
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/columns/indices',
|
724
|
-
tags=['Pipes']
|
876
|
+
tags=['Pipes: Data'],
|
725
877
|
)
|
726
878
|
def get_pipe_columns_indices(
|
727
879
|
connector_keys: str,
|
@@ -775,7 +927,7 @@ def get_pipe_columns_indices(
|
|
775
927
|
|
776
928
|
@app.get(
|
777
929
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/indices/names',
|
778
|
-
tags=['Pipes']
|
930
|
+
tags=['Pipes: Data']
|
779
931
|
)
|
780
932
|
def get_pipe_index_names(
|
781
933
|
connector_keys: str,
|
@@ -785,7 +937,7 @@ def get_pipe_index_names(
|
|
785
937
|
curr_user=(
|
786
938
|
fastapi.Depends(manager) if not no_auth else None
|
787
939
|
),
|
788
|
-
) -> Dict[str, List[Dict[str, str]]]:
|
940
|
+
) -> Dict[str, Union[str, Dict[str, str], List[Dict[str, str]]]]:
|
789
941
|
"""
|
790
942
|
Return a dictionary of index keys and index names.
|
791
943
|
|
@@ -796,4 +948,4 @@ def get_pipe_index_names(
|
|
796
948
|
metric_key,
|
797
949
|
location_key,
|
798
950
|
instance_keys,
|
799
|
-
).get_indices(
|
951
|
+
).get_indices()
|
meerschaum/config/_default.py
CHANGED
@@ -70,6 +70,7 @@ default_system_config = {
|
|
70
70
|
'sql': {
|
71
71
|
'bulk_insert': {
|
72
72
|
'postgresql': True,
|
73
|
+
'postgis': True,
|
73
74
|
'citus': True,
|
74
75
|
'timescaledb': True,
|
75
76
|
'mssql': True,
|
@@ -113,6 +114,9 @@ default_system_config = {
|
|
113
114
|
},
|
114
115
|
'data': {
|
115
116
|
'max_response_row_limit': 100_000,
|
117
|
+
'chunks': {
|
118
|
+
'ttl_seconds': 1800,
|
119
|
+
},
|
116
120
|
},
|
117
121
|
'endpoints': {
|
118
122
|
'docs_in_production': True,
|
meerschaum/config/_version.py
CHANGED
@@ -6,8 +6,10 @@
|
|
6
6
|
Interact with Meerschaum APIs. May be chained together (see 'meerschaum:api_instance' in your config.yaml).
|
7
7
|
"""
|
8
8
|
|
9
|
+
from __future__ import annotations
|
10
|
+
|
9
11
|
from datetime import datetime, timedelta, timezone
|
10
|
-
from meerschaum.utils.typing import Optional, List
|
12
|
+
from meerschaum.utils.typing import Optional, List, Union
|
11
13
|
from meerschaum.connectors import Connector
|
12
14
|
from meerschaum.utils.warnings import warn, error
|
13
15
|
from meerschaum.utils.packages import attempt_import
|
@@ -43,6 +45,7 @@ class APIConnector(Connector):
|
|
43
45
|
)
|
44
46
|
from ._misc import get_mrsm_version, get_chaining_status
|
45
47
|
from ._pipes import (
|
48
|
+
get_pipe_instance_keys,
|
46
49
|
register_pipe,
|
47
50
|
fetch_pipes_keys,
|
48
51
|
edit_pipe,
|
@@ -143,6 +146,7 @@ class APIConnector(Connector):
|
|
143
146
|
self._token = None
|
144
147
|
self._expires = None
|
145
148
|
self._session = None
|
149
|
+
self._instance_keys = self.__dict__.get('instance_keys', None)
|
146
150
|
|
147
151
|
|
148
152
|
@property
|
@@ -195,3 +199,10 @@ class APIConnector(Connector):
|
|
195
199
|
warn(msg, stack=False)
|
196
200
|
self._emitted_warning = True
|
197
201
|
return self._token
|
202
|
+
|
203
|
+
@property
|
204
|
+
def instance_keys(self) -> Union[str, None]:
|
205
|
+
"""
|
206
|
+
Return the instance keys to be sent alongside pipe requests.
|
207
|
+
"""
|
208
|
+
return self._instance_keys
|