meerschaum 2.9.0rc2__py3-none-any.whl → 2.9.1__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/api/dash/callbacks/__init__.py +5 -2
- meerschaum/api/dash/callbacks/custom.py +17 -25
- meerschaum/api/dash/callbacks/dashboard.py +5 -21
- meerschaum/api/dash/callbacks/settings/__init__.py +8 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +76 -0
- meerschaum/api/dash/components.py +110 -7
- meerschaum/api/dash/pages/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/__init__.py +8 -0
- meerschaum/api/dash/pages/settings/password_reset.py +63 -0
- meerschaum/api/resources/static/css/dash.css +7 -0
- meerschaum/api/routes/_pipes.py +76 -36
- meerschaum/config/_version.py +1 -1
- meerschaum/connectors/__init__.py +1 -0
- meerschaum/connectors/api/_pipes.py +79 -30
- meerschaum/connectors/sql/_pipes.py +38 -5
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -0
- meerschaum/connectors/valkey/_pipes.py +51 -39
- meerschaum/core/Pipe/__init__.py +1 -0
- meerschaum/core/Pipe/_sync.py +64 -4
- meerschaum/plugins/__init__.py +26 -4
- meerschaum/utils/dataframe.py +58 -3
- meerschaum/utils/dtypes/__init__.py +45 -17
- meerschaum/utils/dtypes/sql.py +182 -3
- meerschaum/utils/misc.py +1 -1
- meerschaum/utils/packages/_packages.py +6 -3
- meerschaum/utils/sql.py +122 -6
- meerschaum/utils/venv/__init__.py +4 -1
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/METADATA +14 -9
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/RECORD +35 -36
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.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.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/LICENSE +0 -0
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/NOTICE +0 -0
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.0rc2.dist-info → meerschaum-2.9.1.dist-info}/zip-safe +0 -0
@@ -414,19 +414,24 @@ def get_pipe_id(
|
|
414
414
|
self,
|
415
415
|
pipe: mrsm.Pipe,
|
416
416
|
debug: bool = False,
|
417
|
-
) -> int:
|
417
|
+
) -> Union[int, str, None]:
|
418
418
|
"""Get a Pipe's ID from the API."""
|
419
419
|
from meerschaum.utils.misc import is_int
|
420
420
|
r_url = pipe_r_url(pipe)
|
421
421
|
response = self.get(
|
422
422
|
r_url + '/id',
|
423
|
-
|
423
|
+
params={
|
424
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
425
|
+
},
|
426
|
+
debug=debug,
|
424
427
|
)
|
425
428
|
if debug:
|
426
429
|
dprint(f"Got pipe ID: {response.text}")
|
427
430
|
try:
|
428
431
|
if is_int(response.text):
|
429
432
|
return int(response.text)
|
433
|
+
if response.text and response.text[0] != '{':
|
434
|
+
return response.text
|
430
435
|
except Exception as e:
|
431
436
|
warn(f"Failed to get the ID for {pipe}:\n{e}")
|
432
437
|
return None
|
@@ -450,7 +455,13 @@ def get_pipe_attributes(
|
|
450
455
|
If the pipe does not exist, return an empty dictionary.
|
451
456
|
"""
|
452
457
|
r_url = pipe_r_url(pipe)
|
453
|
-
response = self.get(
|
458
|
+
response = self.get(
|
459
|
+
r_url + '/attributes',
|
460
|
+
params={
|
461
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
462
|
+
},
|
463
|
+
debug=debug
|
464
|
+
)
|
454
465
|
try:
|
455
466
|
return json.loads(response.text)
|
456
467
|
except Exception as e:
|
@@ -489,9 +500,13 @@ def get_sync_time(
|
|
489
500
|
r_url = pipe_r_url(pipe)
|
490
501
|
response = self.get(
|
491
502
|
r_url + '/sync_time',
|
492
|
-
json
|
493
|
-
params
|
494
|
-
|
503
|
+
json=params,
|
504
|
+
params={
|
505
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
506
|
+
'newest': newest,
|
507
|
+
'debug': debug,
|
508
|
+
},
|
509
|
+
debug=debug,
|
495
510
|
)
|
496
511
|
if not response:
|
497
512
|
warn(f"Failed to get the sync time for {pipe}:\n" + response.text)
|
@@ -532,7 +547,13 @@ def pipe_exists(
|
|
532
547
|
from meerschaum.utils.debug import dprint
|
533
548
|
from meerschaum.utils.warnings import warn
|
534
549
|
r_url = pipe_r_url(pipe)
|
535
|
-
response = self.get(
|
550
|
+
response = self.get(
|
551
|
+
r_url + '/exists',
|
552
|
+
params={
|
553
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
554
|
+
},
|
555
|
+
debug=debug,
|
556
|
+
)
|
536
557
|
if not response:
|
537
558
|
warn(f"Failed to check if {pipe} exists:\n{response.text}")
|
538
559
|
return False
|
@@ -570,8 +591,8 @@ def create_metadata(
|
|
570
591
|
def get_pipe_rowcount(
|
571
592
|
self,
|
572
593
|
pipe: mrsm.Pipe,
|
573
|
-
begin:
|
574
|
-
end:
|
594
|
+
begin: Union[str, datetime, int, None] = None,
|
595
|
+
end: Union[str, datetime, int, None] = None,
|
575
596
|
params: Optional[Dict[str, Any]] = None,
|
576
597
|
remote: bool = False,
|
577
598
|
debug: bool = False,
|
@@ -583,10 +604,10 @@ def get_pipe_rowcount(
|
|
583
604
|
pipe: 'meerschaum.Pipe':
|
584
605
|
The pipe whose row count we are counting.
|
585
606
|
|
586
|
-
begin:
|
607
|
+
begin: Union[str, datetime, int, None], default None
|
587
608
|
If provided, bound the count by this datetime.
|
588
609
|
|
589
|
-
end:
|
610
|
+
end: Union[str, datetime, int, None], default None
|
590
611
|
If provided, bound the count by this datetime.
|
591
612
|
|
592
613
|
params: Optional[Dict[str, Any]], default None
|
@@ -608,6 +629,7 @@ def get_pipe_rowcount(
|
|
608
629
|
'begin': begin,
|
609
630
|
'end': end,
|
610
631
|
'remote': remote,
|
632
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
611
633
|
},
|
612
634
|
debug = debug
|
613
635
|
)
|
@@ -645,7 +667,10 @@ def drop_pipe(
|
|
645
667
|
r_url = pipe_r_url(pipe)
|
646
668
|
response = self.delete(
|
647
669
|
r_url + '/drop',
|
648
|
-
|
670
|
+
params={
|
671
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
672
|
+
},
|
673
|
+
debug=debug,
|
649
674
|
)
|
650
675
|
if debug:
|
651
676
|
dprint(response.text)
|
@@ -668,6 +693,9 @@ def drop_pipe(
|
|
668
693
|
def clear_pipe(
|
669
694
|
self,
|
670
695
|
pipe: mrsm.Pipe,
|
696
|
+
begin: Union[str, datetime, int, None] = None,
|
697
|
+
end: Union[str, datetime, int, None] = None,
|
698
|
+
params: Optional[Dict[str, Any]] = None,
|
671
699
|
debug: bool = False,
|
672
700
|
**kw
|
673
701
|
) -> SuccessTuple:
|
@@ -683,20 +711,33 @@ def clear_pipe(
|
|
683
711
|
-------
|
684
712
|
A success tuple.
|
685
713
|
"""
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
location_keys=pipe.location_key,
|
696
|
-
force=True,
|
714
|
+
r_url = pipe_r_url(pipe)
|
715
|
+
response = self.delete(
|
716
|
+
r_url + '/clear',
|
717
|
+
params={
|
718
|
+
'begin': begin,
|
719
|
+
'end': end,
|
720
|
+
'params': json.dumps(params),
|
721
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
722
|
+
},
|
697
723
|
debug=debug,
|
698
|
-
**kw
|
699
724
|
)
|
725
|
+
if debug:
|
726
|
+
dprint(response.text)
|
727
|
+
|
728
|
+
try:
|
729
|
+
data = response.json()
|
730
|
+
except Exception as e:
|
731
|
+
return False, f"Failed to clear {pipe} with constraints {begin=}, {end=}, {params=}."
|
732
|
+
|
733
|
+
if isinstance(data, list):
|
734
|
+
response_tuple = data[0], data[1]
|
735
|
+
elif 'detail' in response.json():
|
736
|
+
response_tuple = response.__bool__(), data['detail']
|
737
|
+
else:
|
738
|
+
response_tuple = response.__bool__(), response.text
|
739
|
+
|
740
|
+
return response_tuple
|
700
741
|
|
701
742
|
|
702
743
|
def get_pipe_columns_types(
|
@@ -728,7 +769,10 @@ def get_pipe_columns_types(
|
|
728
769
|
r_url = pipe_r_url(pipe) + '/columns/types'
|
729
770
|
response = self.get(
|
730
771
|
r_url,
|
731
|
-
|
772
|
+
params={
|
773
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
774
|
+
},
|
775
|
+
debug=debug,
|
732
776
|
)
|
733
777
|
j = response.json()
|
734
778
|
if isinstance(j, dict) and 'detail' in j and len(j.keys()) == 1:
|
@@ -760,7 +804,10 @@ def get_pipe_columns_indices(
|
|
760
804
|
r_url = pipe_r_url(pipe) + '/columns/indices'
|
761
805
|
response = self.get(
|
762
806
|
r_url,
|
763
|
-
|
807
|
+
params={
|
808
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
809
|
+
},
|
810
|
+
debug=debug,
|
764
811
|
)
|
765
812
|
j = response.json()
|
766
813
|
if isinstance(j, dict) and 'detail' in j and len(j.keys()) == 1:
|
@@ -779,14 +826,16 @@ def get_pipe_index_names(self, pipe: mrsm.Pipe, debug: bool = False) -> Dict[str
|
|
779
826
|
r_url = pipe_r_url(pipe) + '/indices/names'
|
780
827
|
response = self.get(
|
781
828
|
r_url,
|
782
|
-
|
829
|
+
params={
|
830
|
+
'instance': self.get_pipe_instance_keys(pipe),
|
831
|
+
},
|
832
|
+
debug=debug,
|
783
833
|
)
|
784
834
|
j = response.json()
|
785
835
|
if isinstance(j, dict) and 'detail' in j and len(j.keys()) == 1:
|
786
836
|
warn(j['detail'])
|
787
|
-
return
|
837
|
+
return {}
|
788
838
|
if not isinstance(j, dict):
|
789
839
|
warn(response.text)
|
790
|
-
return
|
840
|
+
return {}
|
791
841
|
return j
|
792
|
-
|
@@ -499,6 +499,7 @@ def get_create_index_queries(
|
|
499
499
|
get_rename_table_queries,
|
500
500
|
COALESCE_UNIQUE_INDEX_FLAVORS,
|
501
501
|
)
|
502
|
+
from meerschaum.utils.dtypes import are_dtypes_equal
|
502
503
|
from meerschaum.utils.dtypes.sql import (
|
503
504
|
get_db_type_from_pd_type,
|
504
505
|
get_pd_type_from_db_type,
|
@@ -793,8 +794,19 @@ def get_create_index_queries(
|
|
793
794
|
cols_names = [sql_item_name(col, self.flavor, None) for col in cols if col]
|
794
795
|
if not cols_names:
|
795
796
|
continue
|
797
|
+
|
796
798
|
cols_names_str = ", ".join(cols_names)
|
797
|
-
|
799
|
+
index_query_params_clause = f" ({cols_names_str})"
|
800
|
+
if self.flavor == 'postgis':
|
801
|
+
for col in cols:
|
802
|
+
col_typ = existing_cols_pd_types.get(cols[0], 'object')
|
803
|
+
if col_typ != 'object' and are_dtypes_equal(col_typ, 'geometry'):
|
804
|
+
index_query_params_clause = f" USING GIST ({cols_names_str})"
|
805
|
+
break
|
806
|
+
|
807
|
+
index_queries[ix_key] = [
|
808
|
+
f"CREATE INDEX {ix_name} ON {_pipe_name}{index_query_params_clause}"
|
809
|
+
]
|
798
810
|
|
799
811
|
indices_cols_str = ', '.join(
|
800
812
|
list({
|
@@ -1544,7 +1556,11 @@ def create_pipe_table_from_df(
|
|
1544
1556
|
get_datetime_cols,
|
1545
1557
|
get_bytes_cols,
|
1546
1558
|
)
|
1547
|
-
from meerschaum.utils.sql import
|
1559
|
+
from meerschaum.utils.sql import (
|
1560
|
+
get_create_table_queries,
|
1561
|
+
sql_item_name,
|
1562
|
+
get_create_schema_if_not_exists_queries,
|
1563
|
+
)
|
1548
1564
|
from meerschaum.utils.dtypes.sql import get_db_type_from_pd_type
|
1549
1565
|
primary_key = pipe.columns.get('primary', None)
|
1550
1566
|
primary_key_typ = (
|
@@ -1601,15 +1617,21 @@ def create_pipe_table_from_df(
|
|
1601
1617
|
if autoincrement:
|
1602
1618
|
_ = new_dtypes.pop(primary_key, None)
|
1603
1619
|
|
1620
|
+
schema = self.get_pipe_schema(pipe)
|
1604
1621
|
create_table_queries = get_create_table_queries(
|
1605
1622
|
new_dtypes,
|
1606
1623
|
pipe.target,
|
1607
1624
|
self.flavor,
|
1608
|
-
schema=
|
1625
|
+
schema=schema,
|
1609
1626
|
primary_key=primary_key,
|
1610
1627
|
primary_key_db_type=primary_key_db_type,
|
1611
1628
|
datetime_column=dt_col,
|
1612
1629
|
)
|
1630
|
+
if schema:
|
1631
|
+
create_table_queries = (
|
1632
|
+
get_create_schema_if_not_exists_queries(schema, self.flavor)
|
1633
|
+
+ create_table_queries
|
1634
|
+
)
|
1613
1635
|
success = all(
|
1614
1636
|
self.exec_queries(create_table_queries, break_on_error=True, rollback=True, debug=debug)
|
1615
1637
|
)
|
@@ -2085,6 +2107,7 @@ def sync_pipe_inplace(
|
|
2085
2107
|
get_update_queries,
|
2086
2108
|
get_null_replacement,
|
2087
2109
|
get_create_table_queries,
|
2110
|
+
get_create_schema_if_not_exists_queries,
|
2088
2111
|
get_table_cols_types,
|
2089
2112
|
session_execute,
|
2090
2113
|
dateadd_str,
|
@@ -2164,18 +2187,28 @@ def sync_pipe_inplace(
|
|
2164
2187
|
warn(drop_stale_msg)
|
2165
2188
|
return drop_stale_success, drop_stale_msg
|
2166
2189
|
|
2167
|
-
sqlalchemy, sqlalchemy_orm = mrsm.attempt_import(
|
2190
|
+
sqlalchemy, sqlalchemy_orm = mrsm.attempt_import(
|
2191
|
+
'sqlalchemy',
|
2192
|
+
'sqlalchemy.orm',
|
2193
|
+
)
|
2168
2194
|
if not pipe.exists(debug=debug):
|
2195
|
+
schema = self.get_pipe_schema(pipe)
|
2169
2196
|
create_pipe_queries = get_create_table_queries(
|
2170
2197
|
metadef,
|
2171
2198
|
pipe.target,
|
2172
2199
|
self.flavor,
|
2173
|
-
schema=
|
2200
|
+
schema=schema,
|
2174
2201
|
primary_key=primary_key,
|
2175
2202
|
primary_key_db_type=primary_key_db_type,
|
2176
2203
|
autoincrement=autoincrement,
|
2177
2204
|
datetime_column=dt_col,
|
2178
2205
|
)
|
2206
|
+
if schema:
|
2207
|
+
create_pipe_queries = (
|
2208
|
+
get_create_schema_if_not_exists_queries(schema, self.flavor)
|
2209
|
+
+ create_pipe_queries
|
2210
|
+
)
|
2211
|
+
|
2179
2212
|
results = self.exec_queries(create_pipe_queries, debug=debug)
|
2180
2213
|
if not all(results):
|
2181
2214
|
_ = clean_up_temp_tables()
|
@@ -10,8 +10,9 @@ from datetime import datetime, timezone
|
|
10
10
|
|
11
11
|
import meerschaum as mrsm
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, Any, Union, Optional, Dict, List, Tuple
|
13
|
-
from meerschaum.utils.misc import
|
14
|
-
from meerschaum.utils.
|
13
|
+
from meerschaum.utils.misc import string_to_dict
|
14
|
+
from meerschaum.utils.dtypes import json_serialize_value
|
15
|
+
from meerschaum.utils.warnings import warn, dprint
|
15
16
|
from meerschaum.config.static import STATIC_CONFIG
|
16
17
|
|
17
18
|
PIPES_TABLE: str = 'mrsm_pipes'
|
@@ -46,25 +47,15 @@ def serialize_document(doc: Dict[str, Any]) -> str:
|
|
46
47
|
-------
|
47
48
|
A serialized string for the document.
|
48
49
|
"""
|
49
|
-
from meerschaum.utils.dtypes import serialize_bytes
|
50
50
|
return json.dumps(
|
51
51
|
doc,
|
52
|
-
default=
|
53
|
-
lambda x: (
|
54
|
-
json_serialize_datetime(x)
|
55
|
-
if hasattr(x, 'tzinfo')
|
56
|
-
else (
|
57
|
-
serialize_bytes(x)
|
58
|
-
if isinstance(x, bytes)
|
59
|
-
else str(x)
|
60
|
-
)
|
61
|
-
)
|
62
|
-
),
|
52
|
+
default=json_serialize_value,
|
63
53
|
separators=(',', ':'),
|
64
54
|
sort_keys=True,
|
65
55
|
)
|
66
56
|
|
67
57
|
|
58
|
+
@staticmethod
|
68
59
|
def get_document_key(
|
69
60
|
doc: Dict[str, Any],
|
70
61
|
indices: List[str],
|
@@ -91,25 +82,39 @@ def get_document_key(
|
|
91
82
|
from meerschaum.utils.dtypes import coerce_timezone
|
92
83
|
index_vals = {
|
93
84
|
key: (
|
94
|
-
str(val)
|
85
|
+
str(val).replace(':', COLON)
|
95
86
|
if not isinstance(val, datetime)
|
96
87
|
else str(int(coerce_timezone(val).replace(tzinfo=timezone.utc).timestamp()))
|
97
88
|
)
|
98
89
|
for key, val in doc.items()
|
99
|
-
if key in indices
|
100
|
-
}
|
101
|
-
indices_str = (
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
90
|
+
if ((key in indices) if indices else True)
|
91
|
+
}
|
92
|
+
indices_str = (
|
93
|
+
(
|
94
|
+
(
|
95
|
+
(
|
96
|
+
table_name
|
97
|
+
+ ':'
|
98
|
+
+ ('indices:' if True else '')
|
99
|
+
)
|
100
|
+
)
|
101
|
+
if table_name
|
102
|
+
else ''
|
103
|
+
) + ','.join(
|
104
|
+
sorted(
|
105
|
+
[
|
106
|
+
f'{key}{COLON}{val}'
|
107
|
+
for key, val in index_vals.items()
|
108
|
+
]
|
109
|
+
)
|
107
110
|
)
|
108
|
-
)
|
111
|
+
)
|
109
112
|
return indices_str
|
110
113
|
|
111
114
|
|
115
|
+
@classmethod
|
112
116
|
def get_table_quoted_doc_key(
|
117
|
+
cls,
|
113
118
|
table_name: str,
|
114
119
|
doc: Dict[str, Any],
|
115
120
|
indices: List[str],
|
@@ -120,7 +125,7 @@ def get_table_quoted_doc_key(
|
|
120
125
|
"""
|
121
126
|
return json.dumps(
|
122
127
|
{
|
123
|
-
get_document_key(doc, indices, table_name): serialize_document(doc),
|
128
|
+
cls.get_document_key(doc, indices, table_name): serialize_document(doc),
|
124
129
|
**(
|
125
130
|
{datetime_column: doc.get(datetime_column, 0)}
|
126
131
|
if datetime_column
|
@@ -129,7 +134,7 @@ def get_table_quoted_doc_key(
|
|
129
134
|
},
|
130
135
|
sort_keys=True,
|
131
136
|
separators=(',', ':'),
|
132
|
-
default=
|
137
|
+
default=json_serialize_value,
|
133
138
|
)
|
134
139
|
|
135
140
|
|
@@ -377,7 +382,7 @@ def delete_pipe(
|
|
377
382
|
doc = docs[0]
|
378
383
|
doc_str = json.dumps(
|
379
384
|
doc,
|
380
|
-
default=
|
385
|
+
default=json_serialize_value,
|
381
386
|
separators=(',', ':'),
|
382
387
|
sort_keys=True,
|
383
388
|
)
|
@@ -443,11 +448,16 @@ def get_pipe_data(
|
|
443
448
|
debug=debug,
|
444
449
|
)
|
445
450
|
]
|
451
|
+
print(f"{ix_docs=}")
|
446
452
|
try:
|
447
453
|
docs_strings = [
|
448
|
-
self.get(
|
449
|
-
|
450
|
-
|
454
|
+
self.get(
|
455
|
+
self.get_document_key(
|
456
|
+
doc,
|
457
|
+
indices,
|
458
|
+
table_name,
|
459
|
+
)
|
460
|
+
)
|
451
461
|
for doc in ix_docs
|
452
462
|
]
|
453
463
|
except Exception as e:
|
@@ -535,7 +545,7 @@ def sync_pipe(
|
|
535
545
|
def _serialize_indices_docs(_docs):
|
536
546
|
return [
|
537
547
|
{
|
538
|
-
'ix': get_document_key(doc, indices),
|
548
|
+
'ix': self.get_document_key(doc, indices),
|
539
549
|
**(
|
540
550
|
{
|
541
551
|
dt_col: doc.get(dt_col, 0)
|
@@ -594,7 +604,7 @@ def sync_pipe(
|
|
594
604
|
unseen_docs = unseen_df.to_dict(orient='records') if unseen_df is not None else []
|
595
605
|
unseen_indices_docs = _serialize_indices_docs(unseen_docs)
|
596
606
|
unseen_ix_vals = {
|
597
|
-
get_document_key(doc, indices, table_name): serialize_document(doc)
|
607
|
+
self.get_document_key(doc, indices, table_name): serialize_document(doc)
|
598
608
|
for doc in unseen_docs
|
599
609
|
}
|
600
610
|
for key, val in unseen_ix_vals.items():
|
@@ -615,7 +625,7 @@ def sync_pipe(
|
|
615
625
|
|
616
626
|
update_docs = update_df.to_dict(orient='records') if update_df is not None else []
|
617
627
|
update_ix_docs = {
|
618
|
-
get_document_key(doc, indices, table_name): doc
|
628
|
+
self.get_document_key(doc, indices, table_name): doc
|
619
629
|
for doc in update_docs
|
620
630
|
}
|
621
631
|
existing_docs_data = {
|
@@ -633,7 +643,7 @@ def sync_pipe(
|
|
633
643
|
if key not in existing_docs
|
634
644
|
}
|
635
645
|
new_ix_vals = {
|
636
|
-
get_document_key(doc, indices, table_name): serialize_document(doc)
|
646
|
+
self.get_document_key(doc, indices, table_name): serialize_document(doc)
|
637
647
|
for doc in new_update_docs.values()
|
638
648
|
}
|
639
649
|
for key, val in new_ix_vals.items():
|
@@ -743,8 +753,8 @@ def clear_pipe(
|
|
743
753
|
table_name = self.quote_table(pipe.target)
|
744
754
|
indices = [col for col in pipe.columns.values() if col]
|
745
755
|
for doc in docs:
|
746
|
-
set_doc_key = get_document_key(doc, indices)
|
747
|
-
table_doc_key = get_document_key(doc, indices, table_name)
|
756
|
+
set_doc_key = self.get_document_key(doc, indices)
|
757
|
+
table_doc_key = self.get_document_key(doc, indices, table_name)
|
748
758
|
try:
|
749
759
|
if dt_col:
|
750
760
|
self.client.zrem(table_name, set_doc_key)
|
@@ -826,13 +836,15 @@ def get_pipe_rowcount(
|
|
826
836
|
return 0
|
827
837
|
|
828
838
|
try:
|
829
|
-
if begin is None and end is None and params
|
839
|
+
if begin is None and end is None and not params:
|
830
840
|
return (
|
831
841
|
self.client.zcard(table_name)
|
832
842
|
if dt_col
|
833
|
-
else self.client.
|
843
|
+
else self.client.scard(table_name)
|
834
844
|
)
|
835
|
-
except Exception:
|
845
|
+
except Exception as e:
|
846
|
+
if debug:
|
847
|
+
dprint(f"Failed to get rowcount for {pipe}:\n{e}")
|
836
848
|
return None
|
837
849
|
|
838
850
|
df = pipe.get_data(begin=begin, end=end, params=params, debug=debug)
|
meerschaum/core/Pipe/__init__.py
CHANGED
meerschaum/core/Pipe/_sync.py
CHANGED
@@ -158,6 +158,7 @@ def sync(
|
|
158
158
|
'error_callback': error_callback,
|
159
159
|
'sync_chunks': sync_chunks,
|
160
160
|
'chunksize': chunksize,
|
161
|
+
'safe_copy': True,
|
161
162
|
})
|
162
163
|
|
163
164
|
### NOTE: Invalidate `_exists` cache before and after syncing.
|
@@ -268,6 +269,7 @@ def sync(
|
|
268
269
|
**kw
|
269
270
|
)
|
270
271
|
)
|
272
|
+
kw['safe_copy'] = False
|
271
273
|
except Exception as e:
|
272
274
|
get_console().print_exception(
|
273
275
|
suppress=[
|
@@ -402,6 +404,7 @@ def sync(
|
|
402
404
|
self._persist_new_numeric_columns(df, debug=debug)
|
403
405
|
self._persist_new_uuid_columns(df, debug=debug)
|
404
406
|
self._persist_new_bytes_columns(df, debug=debug)
|
407
|
+
self._persist_new_geometry_columns(df, debug=debug)
|
405
408
|
|
406
409
|
if debug:
|
407
410
|
dprint(
|
@@ -1009,7 +1012,7 @@ def _persist_new_numeric_columns(self, df, debug: bool = False) -> SuccessTuple:
|
|
1009
1012
|
|
1010
1013
|
self._attributes_sync_time = None
|
1011
1014
|
dtypes = self.parameters.get('dtypes', {})
|
1012
|
-
dtypes.update({col: 'numeric' for col in
|
1015
|
+
dtypes.update({col: 'numeric' for col in new_numeric_cols})
|
1013
1016
|
self.parameters['dtypes'] = dtypes
|
1014
1017
|
if not self.temporary:
|
1015
1018
|
edit_success, edit_msg = self.edit(interactive=False, debug=debug)
|
@@ -1034,7 +1037,7 @@ def _persist_new_uuid_columns(self, df, debug: bool = False) -> SuccessTuple:
|
|
1034
1037
|
|
1035
1038
|
self._attributes_sync_time = None
|
1036
1039
|
dtypes = self.parameters.get('dtypes', {})
|
1037
|
-
dtypes.update({col: 'uuid' for col in
|
1040
|
+
dtypes.update({col: 'uuid' for col in new_uuid_cols})
|
1038
1041
|
self.parameters['dtypes'] = dtypes
|
1039
1042
|
if not self.temporary:
|
1040
1043
|
edit_success, edit_msg = self.edit(interactive=False, debug=debug)
|
@@ -1059,7 +1062,7 @@ def _persist_new_json_columns(self, df, debug: bool = False) -> SuccessTuple:
|
|
1059
1062
|
|
1060
1063
|
self._attributes_sync_time = None
|
1061
1064
|
dtypes = self.parameters.get('dtypes', {})
|
1062
|
-
dtypes.update({col: 'json' for col in
|
1065
|
+
dtypes.update({col: 'json' for col in new_json_cols})
|
1063
1066
|
self.parameters['dtypes'] = dtypes
|
1064
1067
|
|
1065
1068
|
if not self.temporary:
|
@@ -1085,7 +1088,64 @@ def _persist_new_bytes_columns(self, df, debug: bool = False) -> SuccessTuple:
|
|
1085
1088
|
|
1086
1089
|
self._attributes_sync_time = None
|
1087
1090
|
dtypes = self.parameters.get('dtypes', {})
|
1088
|
-
dtypes.update({col: 'bytes' for col in
|
1091
|
+
dtypes.update({col: 'bytes' for col in new_bytes_cols})
|
1092
|
+
self.parameters['dtypes'] = dtypes
|
1093
|
+
|
1094
|
+
if not self.temporary:
|
1095
|
+
edit_success, edit_msg = self.edit(interactive=False, debug=debug)
|
1096
|
+
if not edit_success:
|
1097
|
+
warn(f"Unable to update bytes dtypes for {self}:\n{edit_msg}")
|
1098
|
+
|
1099
|
+
return edit_success, edit_msg
|
1100
|
+
|
1101
|
+
return True, "Success"
|
1102
|
+
|
1103
|
+
|
1104
|
+
def _persist_new_geometry_columns(self, df, debug: bool = False) -> SuccessTuple:
|
1105
|
+
"""
|
1106
|
+
Check for new `geometry` columns and update the parameters.
|
1107
|
+
"""
|
1108
|
+
from meerschaum.utils.dataframe import get_geometry_cols
|
1109
|
+
geometry_cols_types_srids = get_geometry_cols(df, with_types_srids=True)
|
1110
|
+
existing_geometry_cols = [
|
1111
|
+
col
|
1112
|
+
for col, typ in self.dtypes.items()
|
1113
|
+
if typ.startswith('geometry') or typ.startswith('geography')
|
1114
|
+
]
|
1115
|
+
new_geometry_cols = [
|
1116
|
+
col
|
1117
|
+
for col in geometry_cols_types_srids
|
1118
|
+
if col not in existing_geometry_cols
|
1119
|
+
]
|
1120
|
+
if not new_geometry_cols:
|
1121
|
+
return True, "Success"
|
1122
|
+
|
1123
|
+
self._attributes_sync_time = None
|
1124
|
+
dtypes = self.parameters.get('dtypes', {})
|
1125
|
+
|
1126
|
+
new_cols_types = {}
|
1127
|
+
for col, (geometry_type, srid) in geometry_cols_types_srids.items():
|
1128
|
+
if col not in new_geometry_cols:
|
1129
|
+
continue
|
1130
|
+
|
1131
|
+
new_dtype = "geometry"
|
1132
|
+
modifier = ""
|
1133
|
+
if not srid and geometry_type.lower() == 'geometry':
|
1134
|
+
new_cols_types[col] = new_dtype
|
1135
|
+
continue
|
1136
|
+
|
1137
|
+
modifier = "["
|
1138
|
+
if geometry_type.lower() != 'geometry':
|
1139
|
+
modifier += f"{geometry_type}"
|
1140
|
+
|
1141
|
+
if srid:
|
1142
|
+
if modifier != '[':
|
1143
|
+
modifier += ", "
|
1144
|
+
modifier += f"{srid}"
|
1145
|
+
modifier += "]"
|
1146
|
+
new_cols_types[col] = f"{new_dtype}{modifier}"
|
1147
|
+
|
1148
|
+
dtypes.update(new_cols_types)
|
1089
1149
|
self.parameters['dtypes'] = dtypes
|
1090
1150
|
|
1091
1151
|
if not self.temporary:
|