meerschaum 3.0.0rc1__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.
Files changed (66) hide show
  1. meerschaum/_internal/arguments/_parser.py +2 -1
  2. meerschaum/_internal/docs/index.py +49 -2
  3. meerschaum/_internal/shell/Shell.py +5 -4
  4. meerschaum/_internal/static.py +8 -24
  5. meerschaum/actions/bootstrap.py +1 -1
  6. meerschaum/actions/edit.py +6 -3
  7. meerschaum/actions/start.py +1 -1
  8. meerschaum/actions/verify.py +5 -8
  9. meerschaum/api/__init__.py +2 -1
  10. meerschaum/api/dash/__init__.py +0 -2
  11. meerschaum/api/dash/callbacks/__init__.py +1 -0
  12. meerschaum/api/dash/callbacks/dashboard.py +20 -19
  13. meerschaum/api/dash/callbacks/jobs.py +11 -5
  14. meerschaum/api/dash/callbacks/pipes.py +106 -5
  15. meerschaum/api/dash/callbacks/settings/__init__.py +0 -1
  16. meerschaum/api/dash/callbacks/{settings/tokens.py → tokens.py} +1 -1
  17. meerschaum/api/dash/jobs.py +1 -1
  18. meerschaum/api/dash/pages/__init__.py +2 -1
  19. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  20. meerschaum/api/dash/pages/pipes.py +4 -3
  21. meerschaum/api/dash/pages/settings/__init__.py +0 -1
  22. meerschaum/api/dash/pages/{settings/tokens.py → tokens.py} +6 -8
  23. meerschaum/api/dash/pipes.py +131 -0
  24. meerschaum/api/dash/tokens.py +28 -31
  25. meerschaum/api/routes/_pipes.py +47 -37
  26. meerschaum/config/_default.py +13 -2
  27. meerschaum/config/_paths.py +1 -0
  28. meerschaum/config/_version.py +1 -1
  29. meerschaum/config/stack/__init__.py +9 -8
  30. meerschaum/connectors/api/_pipes.py +2 -18
  31. meerschaum/connectors/api/_tokens.py +2 -2
  32. meerschaum/connectors/instance/_tokens.py +10 -6
  33. meerschaum/connectors/sql/_SQLConnector.py +14 -0
  34. meerschaum/connectors/sql/_create_engine.py +3 -14
  35. meerschaum/connectors/sql/_pipes.py +175 -185
  36. meerschaum/connectors/sql/_sql.py +38 -20
  37. meerschaum/connectors/sql/tables/__init__.py +237 -122
  38. meerschaum/connectors/valkey/_pipes.py +44 -16
  39. meerschaum/core/Pipe/__init__.py +28 -5
  40. meerschaum/core/Pipe/_attributes.py +273 -46
  41. meerschaum/core/Pipe/_data.py +55 -17
  42. meerschaum/core/Pipe/_dtypes.py +19 -4
  43. meerschaum/core/Pipe/_edit.py +2 -0
  44. meerschaum/core/Pipe/_fetch.py +1 -1
  45. meerschaum/core/Pipe/_sync.py +90 -160
  46. meerschaum/core/Pipe/_verify.py +3 -3
  47. meerschaum/core/Token/_Token.py +4 -5
  48. meerschaum/plugins/bootstrap.py +508 -3
  49. meerschaum/utils/_get_pipes.py +1 -1
  50. meerschaum/utils/dataframe.py +385 -68
  51. meerschaum/utils/debug.py +15 -15
  52. meerschaum/utils/dtypes/__init__.py +387 -22
  53. meerschaum/utils/dtypes/sql.py +327 -31
  54. meerschaum/utils/misc.py +9 -68
  55. meerschaum/utils/packages/__init__.py +7 -21
  56. meerschaum/utils/packages/_packages.py +7 -2
  57. meerschaum/utils/schedule.py +1 -1
  58. meerschaum/utils/sql.py +8 -8
  59. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/METADATA +5 -17
  60. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/RECORD +66 -65
  61. meerschaum-3.0.0rc3.dist-info/licenses/NOTICE +2 -0
  62. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/WHEEL +0 -0
  63. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/entry_points.txt +0 -0
  64. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/licenses/LICENSE +0 -0
  65. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/top_level.txt +0 -0
  66. {meerschaum-3.0.0rc1.dist-info → meerschaum-3.0.0rc3.dist-info}/zip-safe +0 -0
@@ -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': 'Custom connector\n (e.g. manage credentials)\n',
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
 
@@ -188,7 +188,7 @@ def get_pipes(
188
188
  debug = debug
189
189
  )
190
190
  if result is None:
191
- error(f"Unable to build pipes!")
191
+ error("Unable to build pipes!")
192
192
 
193
193
  ### Populate the `pipes` dictionary with Pipes based on the keys
194
194
  ### obtained from the chosen `method`.