meerschaum 3.0.0rc2__py3-none-any.whl → 3.0.0rc3__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/shell/Shell.py +5 -4
- meerschaum/actions/bootstrap.py +1 -1
- meerschaum/actions/edit.py +6 -3
- meerschaum/actions/start.py +1 -1
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/dashboard.py +19 -18
- meerschaum/api/dash/callbacks/jobs.py +11 -5
- meerschaum/api/dash/callbacks/pipes.py +106 -5
- meerschaum/api/dash/callbacks/settings/__init__.py +0 -1
- meerschaum/api/dash/callbacks/{settings/tokens.py → tokens.py} +1 -1
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/pipes.py +4 -3
- meerschaum/api/dash/pages/settings/__init__.py +0 -1
- meerschaum/api/dash/pages/{settings/tokens.py → tokens.py} +6 -8
- meerschaum/api/dash/pipes.py +131 -0
- meerschaum/api/dash/tokens.py +26 -29
- meerschaum/config/_default.py +5 -4
- meerschaum/config/_paths.py +1 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/instance/_tokens.py +6 -2
- meerschaum/connectors/sql/_SQLConnector.py +14 -0
- meerschaum/connectors/sql/_pipes.py +57 -22
- meerschaum/connectors/sql/tables/__init__.py +237 -122
- meerschaum/core/Pipe/_attributes.py +5 -2
- meerschaum/core/Token/_Token.py +1 -1
- meerschaum/plugins/bootstrap.py +508 -3
- meerschaum/utils/_get_pipes.py +1 -1
- meerschaum/utils/dataframe.py +8 -2
- meerschaum/utils/dtypes/__init__.py +2 -3
- meerschaum/utils/dtypes/sql.py +11 -11
- meerschaum/utils/sql.py +1 -1
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/METADATA +1 -1
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/RECORD +41 -41
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.0rc2.dist-info → meerschaum-3.0.0rc3.dist-info}/zip-safe +0 -0
meerschaum/plugins/bootstrap.py
CHANGED
@@ -16,7 +16,8 @@ from meerschaum.utils.formatting._shell import clear_screen
|
|
16
16
|
|
17
17
|
FEATURE_CHOICES: Dict[str, str] = {
|
18
18
|
'fetch' : 'Fetch data\n (e.g. extracting from a remote API)\n',
|
19
|
-
'connector': '
|
19
|
+
'connector': 'Connector\n (fetch data & manage credentials)\n',
|
20
|
+
'instance-connector': 'Instance connector\n (implement the pipes interface)\n',
|
20
21
|
'action' : 'New actions\n (e.g. `mrsm sing song`)\n',
|
21
22
|
'api' : 'New API endpoints\n (e.g. `POST /my/new/endpoint`)\n',
|
22
23
|
'web' : 'New web console page\n (e.g. `/dash/my-web-app`)\n',
|
@@ -25,6 +26,7 @@ FEATURE_CHOICES: Dict[str, str] = {
|
|
25
26
|
IMPORTS_LINES: Dict[str, str] = {
|
26
27
|
'stdlib': (
|
27
28
|
"from datetime import datetime, timedelta, timezone\n"
|
29
|
+
"from typing import Any, Union, List, Dict\n"
|
28
30
|
),
|
29
31
|
'default': (
|
30
32
|
"import meerschaum as mrsm\n"
|
@@ -33,6 +35,9 @@ IMPORTS_LINES: Dict[str, str] = {
|
|
33
35
|
'connector': (
|
34
36
|
"from meerschaum.connectors import Connector, make_connector\n"
|
35
37
|
),
|
38
|
+
'instance-connector': (
|
39
|
+
"from meerschaum.connectors import InstanceConnector, make_connector\n"
|
40
|
+
),
|
36
41
|
'action': (
|
37
42
|
"from meerschaum.actions import make_action\n"
|
38
43
|
),
|
@@ -106,6 +111,501 @@ FEATURE_LINES: Dict[str, str] = {
|
|
106
111
|
" # populate docs with dictionaries (rows).\n"
|
107
112
|
" return docs\n\n\n"
|
108
113
|
),
|
114
|
+
'instance-connector': (
|
115
|
+
"@make_connector\n"
|
116
|
+
"class {plugin_name_capitalized}Connector(InstanceConnector):\n"
|
117
|
+
" \"\"\"Implement '{plugin_name_lower}' connectors.\"\"\"\n\n"
|
118
|
+
" REQUIRED_ATTRIBUTES: list[str] = []\n"
|
119
|
+
"\n"
|
120
|
+
" def fetch(\n"
|
121
|
+
" self,\n"
|
122
|
+
" pipe: mrsm.Pipe,\n"
|
123
|
+
" begin: datetime | None = None,\n"
|
124
|
+
" end: datetime | None = None,\n"
|
125
|
+
" **kwargs\n"
|
126
|
+
" ):\n"
|
127
|
+
" \"\"\"Return or yield dataframes.\"\"\"\n"
|
128
|
+
" docs = []\n"
|
129
|
+
" # populate docs with dictionaries (rows).\n"
|
130
|
+
" return docs\n"
|
131
|
+
"""
|
132
|
+
def register_pipe(
|
133
|
+
self,
|
134
|
+
pipe: mrsm.Pipe,
|
135
|
+
debug: bool = False,
|
136
|
+
**kwargs: Any
|
137
|
+
) -> mrsm.SuccessTuple:
|
138
|
+
\"\"\"
|
139
|
+
Insert the pipe's attributes into the internal `pipes` table.
|
140
|
+
|
141
|
+
Parameters
|
142
|
+
----------
|
143
|
+
pipe: mrsm.Pipe
|
144
|
+
The pipe to be registered.
|
145
|
+
|
146
|
+
Returns
|
147
|
+
-------
|
148
|
+
A `SuccessTuple` of the result.
|
149
|
+
\"\"\"
|
150
|
+
attributes = {{
|
151
|
+
'connector_keys': str(pipe.connector_keys),
|
152
|
+
'metric_key': str(pipe.metric_key),
|
153
|
+
'location_key': str(pipe.location_key),
|
154
|
+
'parameters': pipe._attributes.get('parameters', {{}}),
|
155
|
+
}}
|
156
|
+
|
157
|
+
### TODO insert `attributes` as a row in the pipes table.
|
158
|
+
# self.pipes_collection.insert_one(attributes)
|
159
|
+
|
160
|
+
return True, \"Success\"
|
161
|
+
|
162
|
+
def get_pipe_attributes(
|
163
|
+
self,
|
164
|
+
pipe: mrsm.Pipe,
|
165
|
+
debug: bool = False,
|
166
|
+
**kwargs: Any
|
167
|
+
) -> dict[str, Any]:
|
168
|
+
\"\"\"
|
169
|
+
Return the pipe's document from the internal `pipes` collection.
|
170
|
+
|
171
|
+
Parameters
|
172
|
+
----------
|
173
|
+
pipe: mrsm.Pipe
|
174
|
+
The pipe whose attributes should be retrieved.
|
175
|
+
|
176
|
+
Returns
|
177
|
+
-------
|
178
|
+
The document that matches the keys of the pipe.
|
179
|
+
\"\"\"
|
180
|
+
query = {{
|
181
|
+
'connector_keys': str(pipe.connector_keys),
|
182
|
+
'metric_key': str(pipe.metric_key),
|
183
|
+
'location_key': str(pipe.location_key),
|
184
|
+
}}
|
185
|
+
### TODO query the `pipes` table either using these keys or `get_pipe_id()`.
|
186
|
+
result = {{}}
|
187
|
+
# result = self.pipes_collection.find_one(query) or {{}}
|
188
|
+
return result
|
189
|
+
|
190
|
+
def get_pipe_id(
|
191
|
+
self,
|
192
|
+
pipe: mrsm.Pipe,
|
193
|
+
debug: bool = False,
|
194
|
+
**kwargs: Any
|
195
|
+
) -> str | int | None:
|
196
|
+
\"\"\"
|
197
|
+
Return the ID for the pipe if it exists.
|
198
|
+
|
199
|
+
Parameters
|
200
|
+
----------
|
201
|
+
pipe: mrsm.Pipe
|
202
|
+
The pipe whose ID to fetch.
|
203
|
+
|
204
|
+
Returns
|
205
|
+
-------
|
206
|
+
The ID for the pipe or `None`.
|
207
|
+
\"\"\"
|
208
|
+
query = {{
|
209
|
+
'connector_keys': str(pipe.connector_keys),
|
210
|
+
'metric_key': str(pipe.metric_key),
|
211
|
+
'location_key': str(pipe.location_key),
|
212
|
+
}}
|
213
|
+
### TODO fetch the ID mapped to this pipe.
|
214
|
+
return None
|
215
|
+
|
216
|
+
def edit_pipe(
|
217
|
+
self,
|
218
|
+
pipe: mrsm.Pipe,
|
219
|
+
debug: bool = False,
|
220
|
+
**kwargs: Any
|
221
|
+
) -> mrsm.SuccessTuple:
|
222
|
+
\"\"\"
|
223
|
+
Edit the attributes of the pipe.
|
224
|
+
|
225
|
+
Parameters
|
226
|
+
----------
|
227
|
+
pipe: mrsm.Pipe
|
228
|
+
The pipe whose in-memory parameters must be persisted.
|
229
|
+
|
230
|
+
Returns
|
231
|
+
-------
|
232
|
+
A `SuccessTuple` indicating success.
|
233
|
+
\"\"\"
|
234
|
+
query = {{
|
235
|
+
'connector_keys': str(pipe.connector_keys),
|
236
|
+
'metric_key': str(pipe.metric_key),
|
237
|
+
'location_key': str(pipe.location_key),
|
238
|
+
}}
|
239
|
+
pipe_parameters = pipe._attributes.get('parameters', {{}})
|
240
|
+
### TODO Update the row with new parameters.
|
241
|
+
# self.pipes_collection.update_one(query, {{'$set': {{'parameters': pipe_parameters}}}})
|
242
|
+
return True, "Success"
|
243
|
+
|
244
|
+
def delete_pipe(
|
245
|
+
self,
|
246
|
+
pipe: mrsm.Pipe,
|
247
|
+
debug: bool = False,
|
248
|
+
**kwargs: Any
|
249
|
+
) -> mrsm.SuccessTuple:
|
250
|
+
\"\"\"
|
251
|
+
Delete a pipe's registration from the `pipes` collection.
|
252
|
+
|
253
|
+
Parameters
|
254
|
+
----------
|
255
|
+
pipe: mrsm.Pipe
|
256
|
+
The pipe to be deleted.
|
257
|
+
|
258
|
+
Returns
|
259
|
+
-------
|
260
|
+
A `SuccessTuple` indicating success.
|
261
|
+
\"\"\"
|
262
|
+
### TODO Delete the pipe's row from the pipes table.
|
263
|
+
# self.pipes_collection.delete_one({{'_id': pipe_id}})
|
264
|
+
return True, "Success"
|
265
|
+
|
266
|
+
def fetch_pipes_keys(
|
267
|
+
self,
|
268
|
+
connector_keys: list[str] | None = None,
|
269
|
+
metric_keys: list[str] | None = None,
|
270
|
+
location_keys: list[str] | None = None,
|
271
|
+
tags: list[str] | None = None,
|
272
|
+
debug: bool = False,
|
273
|
+
**kwargs: Any
|
274
|
+
) -> list[tuple[str, str, str]]:
|
275
|
+
\"\"\"
|
276
|
+
Return a list of tuples for the registered pipes' keys according to the provided filters.
|
277
|
+
|
278
|
+
Parameters
|
279
|
+
----------
|
280
|
+
connector_keys: list[str] | None, default None
|
281
|
+
The keys passed via `-c`.
|
282
|
+
|
283
|
+
metric_keys: list[str] | None, default None
|
284
|
+
The keys passed via `-m`.
|
285
|
+
|
286
|
+
location_keys: list[str] | None, default None
|
287
|
+
The keys passed via `-l`.
|
288
|
+
|
289
|
+
tags: List[str] | None, default None
|
290
|
+
Tags passed via `--tags` which are stored under `parameters:tags`.
|
291
|
+
|
292
|
+
Returns
|
293
|
+
-------
|
294
|
+
A list of connector, metric, and location keys in tuples.
|
295
|
+
You may return the string "None" for location keys in place of nulls.
|
296
|
+
|
297
|
+
Examples
|
298
|
+
--------
|
299
|
+
>>> import meerschaum as mrsm
|
300
|
+
>>> conn = mrsm.get_connector('example:demo')
|
301
|
+
>>>
|
302
|
+
>>> pipe_a = mrsm.Pipe('a', 'demo', tags=['foo'], instance=conn)
|
303
|
+
>>> pipe_b = mrsm.Pipe('b', 'demo', tags=['bar'], instance=conn)
|
304
|
+
>>> pipe_a.register()
|
305
|
+
>>> pipe_b.register()
|
306
|
+
>>>
|
307
|
+
>>> conn.fetch_pipes_keys(['a', 'b'])
|
308
|
+
[('a', 'demo', 'None'), ('b', 'demo', 'None')]
|
309
|
+
>>> conn.fetch_pipes_keys(metric_keys=['demo'])
|
310
|
+
[('a', 'demo', 'None'), ('b', 'demo', 'None')]
|
311
|
+
>>> conn.fetch_pipes_keys(tags=['foo'])
|
312
|
+
[('a', 'demo', 'None')]
|
313
|
+
>>> conn.fetch_pipes_keys(location_keys=[None])
|
314
|
+
[('a', 'demo', 'None'), ('b', 'demo', 'None')]
|
315
|
+
|
316
|
+
\"\"\"
|
317
|
+
from meerschaum.utils.misc import separate_negation_values
|
318
|
+
|
319
|
+
in_ck, nin_ck = separate_negation_values([str(val) for val in (connector_keys or [])])
|
320
|
+
in_mk, nin_mk = separate_negation_values([str(val) for val in (metric_keys or [])])
|
321
|
+
in_lk, nin_lk = separate_negation_values([str(val) for val in (location_keys or [])])
|
322
|
+
in_tags, nin_tags = separate_negation_values([str(val) for val in (tags or [])])
|
323
|
+
|
324
|
+
### TODO build a query like so, only including clauses if the given list is not empty.
|
325
|
+
### The `tags` clause is an OR ("?|"), meaning any of the tags may match.
|
326
|
+
###
|
327
|
+
###
|
328
|
+
### SELECT connector_keys, metric_key, location_key
|
329
|
+
### FROM pipes
|
330
|
+
### WHERE connector_keys IN ({{in_ck}})
|
331
|
+
### AND connector_keys NOT IN ({{nin_ck}})
|
332
|
+
### AND metric_key IN ({{in_mk}})
|
333
|
+
### AND metric_key NOT IN ({{nin_mk}})
|
334
|
+
### AND location_key IN ({{in_lk}})
|
335
|
+
### AND location_key NOT IN ({{nin_lk}})
|
336
|
+
### AND (parameters->'tags')::JSONB ?| ARRAY[{{tags}}]
|
337
|
+
### AND NOT (parameters->'tags')::JSONB ?| ARRAY[{{nin_tags}}]
|
338
|
+
return []
|
339
|
+
|
340
|
+
def pipe_exists(
|
341
|
+
self,
|
342
|
+
pipe: mrsm.Pipe,
|
343
|
+
debug: bool = False,
|
344
|
+
**kwargs: Any
|
345
|
+
) -> bool:
|
346
|
+
\"\"\"
|
347
|
+
Check whether a pipe's target table exists.
|
348
|
+
|
349
|
+
Parameters
|
350
|
+
----------
|
351
|
+
pipe: mrsm.Pipe
|
352
|
+
The pipe to check whether its table exists.
|
353
|
+
|
354
|
+
Returns
|
355
|
+
-------
|
356
|
+
A `bool` indicating the table exists.
|
357
|
+
\"\"\"
|
358
|
+
table_name = pipe.target
|
359
|
+
### TODO write a query to determine the existence of `table_name`.
|
360
|
+
table_exists = False
|
361
|
+
return table_exists
|
362
|
+
|
363
|
+
def drop_pipe(
|
364
|
+
self,
|
365
|
+
pipe: mrsm.Pipe,
|
366
|
+
debug: bool = False,
|
367
|
+
**kwargs: Any
|
368
|
+
) -> mrsm.SuccessTuple:
|
369
|
+
\"\"\"
|
370
|
+
Drop a pipe's collection if it exists.
|
371
|
+
|
372
|
+
Parameters
|
373
|
+
----------
|
374
|
+
pipe: mrsm.Pipe
|
375
|
+
The pipe to be dropped.
|
376
|
+
|
377
|
+
Returns
|
378
|
+
-------
|
379
|
+
A `SuccessTuple` indicating success.
|
380
|
+
\"\"\"
|
381
|
+
### TODO write a query to drop `table_name`.
|
382
|
+
table_name = pipe.target
|
383
|
+
return True, \"Success\"
|
384
|
+
|
385
|
+
def sync_pipe(
|
386
|
+
self,
|
387
|
+
pipe: mrsm.Pipe,
|
388
|
+
df: 'pd.DataFrame',
|
389
|
+
debug: bool = False,
|
390
|
+
**kwargs: Any
|
391
|
+
) -> mrsm.SuccessTuple:
|
392
|
+
\"\"\"
|
393
|
+
Upsert new documents into the pipe's target table.
|
394
|
+
|
395
|
+
Parameters
|
396
|
+
----------
|
397
|
+
pipe: mrsm.Pipe
|
398
|
+
The pipe to which the data should be upserted.
|
399
|
+
|
400
|
+
df: pd.DataFrame
|
401
|
+
The data to be synced.
|
402
|
+
|
403
|
+
Returns
|
404
|
+
-------
|
405
|
+
A `SuccessTuple` indicating success.
|
406
|
+
\"\"\"
|
407
|
+
### TODO Write the upsert logic for the target table.
|
408
|
+
### `pipe.filter_existing()` is provided for your convenience to
|
409
|
+
### remove duplicates and separate inserts from updates.
|
410
|
+
|
411
|
+
unseen_df, update_df, delta_df = pipe.filter_existing(df, debug=debug)
|
412
|
+
return True, \"Success\"
|
413
|
+
|
414
|
+
def clear_pipe(
|
415
|
+
self,
|
416
|
+
pipe: mrsm.Pipe,
|
417
|
+
begin: datetime | int | None = None,
|
418
|
+
end: datetime | int | None = None,
|
419
|
+
params: dict[str, Any] | None = None,
|
420
|
+
debug: bool = False,
|
421
|
+
) -> mrsm.SuccessTuple:
|
422
|
+
\"\"\"
|
423
|
+
Delete rows within `begin`, `end`, and `params`.
|
424
|
+
|
425
|
+
Parameters
|
426
|
+
----------
|
427
|
+
pipe: mrsm.Pipe
|
428
|
+
The pipe whose rows to clear.
|
429
|
+
|
430
|
+
begin: datetime | int | None, default None
|
431
|
+
If provided, remove rows >= `begin`.
|
432
|
+
|
433
|
+
end: datetime | int | None, default None
|
434
|
+
If provided, remove rows < `end`.
|
435
|
+
|
436
|
+
params: dict[str, Any] | None, default None
|
437
|
+
If provided, only remove rows which match the `params` filter.
|
438
|
+
|
439
|
+
Returns
|
440
|
+
-------
|
441
|
+
A `SuccessTuple` indicating success.
|
442
|
+
\"\"\"
|
443
|
+
### TODO Write a query to remove rows which match `begin`, `end`, and `params`.
|
444
|
+
return True, \"Success\"
|
445
|
+
|
446
|
+
def get_pipe_data(
|
447
|
+
self,
|
448
|
+
pipe: mrsm.Pipe,
|
449
|
+
select_columns: list[str] | None = None,
|
450
|
+
omit_columns: list[str] | None = None,
|
451
|
+
begin: datetime | int | None = None,
|
452
|
+
end: datetime | int | None = None,
|
453
|
+
params: dict[str, Any] | None = None,
|
454
|
+
debug: bool = False,
|
455
|
+
**kwargs: Any
|
456
|
+
) -> Union['pd.DataFrame', None]:
|
457
|
+
\"\"\"
|
458
|
+
Query a pipe's target table and return the DataFrame.
|
459
|
+
|
460
|
+
Parameters
|
461
|
+
----------
|
462
|
+
pipe: mrsm.Pipe
|
463
|
+
The pipe with the target table from which to read.
|
464
|
+
|
465
|
+
select_columns: list[str] | None, default None
|
466
|
+
If provided, only select these given columns.
|
467
|
+
Otherwise select all available columns (i.e. `SELECT *`).
|
468
|
+
|
469
|
+
omit_columns: list[str] | None, default None
|
470
|
+
If provided, remove these columns from the selection.
|
471
|
+
|
472
|
+
begin: datetime | int | None, default None
|
473
|
+
The earliest `datetime` value to search from (inclusive).
|
474
|
+
|
475
|
+
end: datetime | int | None, default None
|
476
|
+
The lastest `datetime` value to search from (exclusive).
|
477
|
+
|
478
|
+
params: dict[str | str] | None, default None
|
479
|
+
Additional filters to apply to the query.
|
480
|
+
|
481
|
+
Returns
|
482
|
+
-------
|
483
|
+
The target table's data as a DataFrame.
|
484
|
+
\"\"\"
|
485
|
+
if not pipe.exists(debug=debug):
|
486
|
+
return None
|
487
|
+
|
488
|
+
table_name = pipe.target
|
489
|
+
dt_col = pipe.columns.get(\"datetime\", None)
|
490
|
+
|
491
|
+
### TODO Write a query to fetch from `table_name`
|
492
|
+
### and apply the filters `begin`, `end`, and `params`.
|
493
|
+
###
|
494
|
+
### To improve performance, add logic to only read from
|
495
|
+
### `select_columns` and not `omit_columns` (if provided).
|
496
|
+
###
|
497
|
+
### SELECT {{', '.join(cols_to_select)}}
|
498
|
+
### FROM \"{{table_name}}\"
|
499
|
+
### WHERE \"{{dt_col}}\" >= '{{begin}}'
|
500
|
+
### AND \"{{dt_col}}\" < '{{end}}'
|
501
|
+
|
502
|
+
### The function `parse_df_datetimes()` is a convenience function
|
503
|
+
### to cast a list of dictionaries into a DataFrame and convert datetime columns.
|
504
|
+
from meerschaum.utils.dataframe import parse_df_datetimes
|
505
|
+
rows = []
|
506
|
+
return parse_df_datetimes(rows)
|
507
|
+
|
508
|
+
def get_sync_time(
|
509
|
+
self,
|
510
|
+
pipe: mrsm.Pipe,
|
511
|
+
params: dict[str, Any] | None = None,
|
512
|
+
newest: bool = True,
|
513
|
+
debug: bool = False,
|
514
|
+
**kwargs: Any
|
515
|
+
) -> datetime | int | None:
|
516
|
+
\"\"\"
|
517
|
+
Return the most recent value for the `datetime` axis.
|
518
|
+
|
519
|
+
Parameters
|
520
|
+
----------
|
521
|
+
pipe: mrsm.Pipe
|
522
|
+
The pipe whose collection contains documents.
|
523
|
+
|
524
|
+
params: dict[str, Any] | None, default None
|
525
|
+
Filter certain parameters when determining the sync time.
|
526
|
+
|
527
|
+
newest: bool, default True
|
528
|
+
If `True`, return the maximum value for the column.
|
529
|
+
|
530
|
+
Returns
|
531
|
+
-------
|
532
|
+
The largest `datetime` or `int` value of the `datetime` axis.
|
533
|
+
\"\"\"
|
534
|
+
### TODO write a query to get the largest value for `dt_col`.
|
535
|
+
### If `newest` is `False`, return the smallest value.
|
536
|
+
### Apply the `params` filter in case of multiplexing.
|
537
|
+
return None
|
538
|
+
|
539
|
+
def get_pipe_columns_types(
|
540
|
+
self,
|
541
|
+
pipe: mrsm.Pipe,
|
542
|
+
debug: bool = False,
|
543
|
+
**kwargs: Any
|
544
|
+
) -> dict[str, str]:
|
545
|
+
\"\"\"
|
546
|
+
Return the data types for the columns in the target table for data type enforcement.
|
547
|
+
|
548
|
+
Parameters
|
549
|
+
----------
|
550
|
+
pipe: mrsm.Pipe
|
551
|
+
The pipe whose target table contains columns and data types.
|
552
|
+
|
553
|
+
Returns
|
554
|
+
-------
|
555
|
+
A dictionary mapping columns to data types.
|
556
|
+
\"\"\"
|
557
|
+
table_name = pipe.target
|
558
|
+
### TODO write a query to fetch the columns contained in `table_name`.
|
559
|
+
columns_types = {{}}
|
560
|
+
|
561
|
+
### Return a dictionary mapping the columns
|
562
|
+
### to their Pandas dtypes, e.g.:
|
563
|
+
### `{{'foo': 'int64'`}}`
|
564
|
+
### or to SQL-style dtypes, e.g.:
|
565
|
+
### `{{'bar': 'INT'}}`
|
566
|
+
return columns_types
|
567
|
+
|
568
|
+
def get_pipe_rowcount(
|
569
|
+
self,
|
570
|
+
pipe: mrsm.Pipe,
|
571
|
+
begin: datetime | int | None = None,
|
572
|
+
end: datetime | int | None = None,
|
573
|
+
params: dict[str, Any] | None = None,
|
574
|
+
remote: bool = False,
|
575
|
+
debug: bool = False,
|
576
|
+
**kwargs: Any
|
577
|
+
) -> int:
|
578
|
+
\"\"\"
|
579
|
+
Return the rowcount for the pipe's table.
|
580
|
+
|
581
|
+
Parameters
|
582
|
+
----------
|
583
|
+
pipe: mrsm.Pipe
|
584
|
+
The pipe whose table should be counted.
|
585
|
+
|
586
|
+
begin: datetime | int | None, default None
|
587
|
+
If provided, only count rows >= `begin`.
|
588
|
+
|
589
|
+
end: datetime | int | None, default None
|
590
|
+
If provided, only count rows < `end`.
|
591
|
+
|
592
|
+
params: dict[str, Any] | None
|
593
|
+
If provided, only count rows othat match the `params` filter.
|
594
|
+
|
595
|
+
remote: bool, default False
|
596
|
+
If `True`, return the rowcount for the pipe's fetch definition.
|
597
|
+
In this case, `self` refers to `Pipe.connector`, not `Pipe.instance_connector`.
|
598
|
+
|
599
|
+
Returns
|
600
|
+
-------
|
601
|
+
The rowcount for this pipe's table according the given parameters.
|
602
|
+
\"\"\"
|
603
|
+
### TODO write a query to count how many rows exist in `table_name` according to the filters.
|
604
|
+
table_name = pipe.target
|
605
|
+
count = 0
|
606
|
+
return count
|
607
|
+
"""
|
608
|
+
),
|
109
609
|
'action': (
|
110
610
|
"@make_action\n"
|
111
611
|
"def {action_name}(**kwargs) -> mrsm.SuccessTuple:\n"
|
@@ -212,8 +712,10 @@ def bootstrap_plugin(
|
|
212
712
|
body_text += FEATURE_LINES['header'].format(**plugin_labels)
|
213
713
|
body_text += IMPORTS_LINES['stdlib'].format(**plugin_labels)
|
214
714
|
body_text += IMPORTS_LINES['default'].format(**plugin_labels)
|
215
|
-
if 'connector' in features:
|
715
|
+
if 'connector' in features and 'instance-connector' not in features:
|
216
716
|
body_text += IMPORTS_LINES['connector'].format(**plugin_labels)
|
717
|
+
if 'instance-connector' in features:
|
718
|
+
body_text += IMPORTS_LINES['instance-connector'].format(**plugin_labels)
|
217
719
|
if 'action' in features:
|
218
720
|
body_text += IMPORTS_LINES['action'].format(**plugin_labels)
|
219
721
|
if 'api' in features and 'web' in features:
|
@@ -231,9 +733,12 @@ def bootstrap_plugin(
|
|
231
733
|
body_text += FEATURE_LINES['register'].format(**plugin_labels)
|
232
734
|
body_text += FEATURE_LINES['fetch'].format(**plugin_labels)
|
233
735
|
|
234
|
-
if 'connector' in features:
|
736
|
+
if 'connector' in features and 'instance-connector' not in features:
|
235
737
|
body_text += FEATURE_LINES['connector'].format(**plugin_labels)
|
236
738
|
|
739
|
+
if 'instance-connector' in features:
|
740
|
+
body_text += FEATURE_LINES['instance-connector'].format(**plugin_labels)
|
741
|
+
|
237
742
|
if 'action' in features:
|
238
743
|
body_text += FEATURE_LINES['action'].format(**plugin_labels)
|
239
744
|
|
meerschaum/utils/_get_pipes.py
CHANGED
meerschaum/utils/dataframe.py
CHANGED
@@ -1508,7 +1508,7 @@ def enforce_dtypes(
|
|
1508
1508
|
)
|
1509
1509
|
)
|
1510
1510
|
|
1511
|
-
if debug:
|
1511
|
+
if debug and (explicitly_numeric or df_numeric_cols or mixed_numeric_types):
|
1512
1512
|
from meerschaum.utils.formatting import make_header
|
1513
1513
|
msg = (
|
1514
1514
|
make_header(f"Coercing column '{col}' to numeric:", left_pad=0)
|
@@ -1517,7 +1517,13 @@ def enforce_dtypes(
|
|
1517
1517
|
+ f" Current type: {typ if col not in df_numeric_cols else 'Decimal'}"
|
1518
1518
|
+ ("\n Column is explicitly numeric." if explicitly_numeric else "")
|
1519
1519
|
) if cast_to_numeric else (
|
1520
|
-
f"Will not coerce column '{col}' to numeric
|
1520
|
+
f"Will not coerce column '{col}' to numeric.\n"
|
1521
|
+
f" Numeric columns in dataframe: {df_numeric_cols}\n"
|
1522
|
+
f" Mixed numeric types: {mixed_numeric_types}\n"
|
1523
|
+
f" Explicitly float: {explicitly_float}\n"
|
1524
|
+
f" Explicitly int: {explicitly_int}\n"
|
1525
|
+
f" All NaN: {all_nan}\n"
|
1526
|
+
f" Coerce numeric: {coerce_numeric}"
|
1521
1527
|
)
|
1522
1528
|
dprint(msg)
|
1523
1529
|
|
@@ -233,8 +233,8 @@ def are_dtypes_equal(
|
|
233
233
|
return True
|
234
234
|
|
235
235
|
date_dtypes = (
|
236
|
-
'date', 'date32[pyarrow]', 'date32[day][pyarrow]',
|
237
|
-
'date64[pyarrow]', 'date64[ms][pyarrow]',
|
236
|
+
'date', 'date32', 'date32[pyarrow]', 'date32[day][pyarrow]',
|
237
|
+
'date64', 'date64[pyarrow]', 'date64[ms][pyarrow]',
|
238
238
|
)
|
239
239
|
if ldtype in date_dtypes and rdtype in date_dtypes:
|
240
240
|
return True
|
@@ -1141,7 +1141,6 @@ def round_time(
|
|
1141
1141
|
) -> datetime:
|
1142
1142
|
"""
|
1143
1143
|
Round a datetime object to a multiple of a timedelta.
|
1144
|
-
http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python
|
1145
1144
|
|
1146
1145
|
Parameters
|
1147
1146
|
----------
|