meerschaum 2.3.6__py3-none-any.whl → 2.4.0.dev1__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/actions/bootstrap.py +36 -10
- meerschaum/actions/copy.py +3 -3
- meerschaum/actions/start.py +13 -14
- meerschaum/api/dash/__init__.py +7 -6
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/dashboard.py +7 -5
- meerschaum/api/dash/callbacks/pipes.py +42 -0
- meerschaum/api/dash/pages/__init__.py +1 -0
- meerschaum/api/dash/pages/pipes.py +16 -0
- meerschaum/api/dash/pipes.py +79 -47
- meerschaum/api/dash/users.py +19 -6
- meerschaum/api/routes/_login.py +4 -4
- meerschaum/api/routes/_pipes.py +3 -3
- meerschaum/config/_default.py +9 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +59 -16
- meerschaum/connectors/Connector.py +19 -13
- meerschaum/connectors/__init__.py +9 -5
- meerschaum/connectors/poll.py +30 -24
- meerschaum/connectors/sql/_pipes.py +126 -154
- meerschaum/connectors/sql/_plugins.py +45 -43
- meerschaum/connectors/sql/_users.py +46 -38
- meerschaum/connectors/valkey/ValkeyConnector.py +535 -0
- meerschaum/connectors/valkey/__init__.py +8 -0
- meerschaum/connectors/valkey/_fetch.py +75 -0
- meerschaum/connectors/valkey/_pipes.py +839 -0
- meerschaum/connectors/valkey/_plugins.py +265 -0
- meerschaum/connectors/valkey/_users.py +305 -0
- meerschaum/core/Pipe/__init__.py +3 -0
- meerschaum/core/Pipe/_attributes.py +1 -2
- meerschaum/core/Pipe/_clear.py +16 -13
- meerschaum/core/Pipe/_copy.py +106 -0
- meerschaum/core/Pipe/_drop.py +4 -4
- meerschaum/core/Pipe/_dtypes.py +14 -14
- meerschaum/core/Pipe/_edit.py +15 -14
- meerschaum/core/Pipe/_sync.py +134 -51
- meerschaum/core/Pipe/_verify.py +11 -11
- meerschaum/core/User/_User.py +14 -12
- meerschaum/plugins/_Plugin.py +17 -13
- meerschaum/utils/_get_pipes.py +14 -20
- meerschaum/utils/dataframe.py +288 -101
- meerschaum/utils/dtypes/__init__.py +31 -6
- meerschaum/utils/dtypes/sql.py +4 -4
- meerschaum/utils/misc.py +3 -3
- meerschaum/utils/packages/_packages.py +1 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/METADATA +3 -1
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/RECORD +53 -44
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/WHEEL +1 -1
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/LICENSE +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/NOTICE +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/zip-safe +0 -0
@@ -0,0 +1,265 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define methods for registering plugins.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from __future__ import annotations
|
9
|
+
|
10
|
+
import json
|
11
|
+
|
12
|
+
import meerschaum as mrsm
|
13
|
+
from meerschaum.utils.typing import Optional, Any, List, SuccessTuple, Dict, Union
|
14
|
+
|
15
|
+
PLUGINS_TABLE: str = "mrsm_plugins"
|
16
|
+
PLUGIN_PREFIX: str = "mrsm_plugin"
|
17
|
+
|
18
|
+
|
19
|
+
def get_plugins_pipe(self) -> mrsm.Pipe:
|
20
|
+
"""
|
21
|
+
Return the pipe to store the plugins.
|
22
|
+
"""
|
23
|
+
return mrsm.Pipe(
|
24
|
+
'mrsm', 'plugins',
|
25
|
+
columns=['plugin_name'],
|
26
|
+
temporary=True,
|
27
|
+
target=PLUGINS_TABLE,
|
28
|
+
instance=self,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def get_plugin_key(cls, plugin_name: str, sub_key: str) -> str:
|
34
|
+
"""
|
35
|
+
Return the key for a plugin's attribute.
|
36
|
+
"""
|
37
|
+
return cls.get_entity_key(PLUGIN_PREFIX, plugin_name, sub_key)
|
38
|
+
|
39
|
+
|
40
|
+
@classmethod
|
41
|
+
def get_plugin_keys_vals(
|
42
|
+
cls,
|
43
|
+
plugin: 'mrsm.core.Plugin',
|
44
|
+
mutable_only: bool = False,
|
45
|
+
) -> Dict[str, str]:
|
46
|
+
"""
|
47
|
+
Return a dictionary containing keys and values to set for the plugin.
|
48
|
+
|
49
|
+
Parameters
|
50
|
+
----------
|
51
|
+
plugin: mrsm.core.Plugin
|
52
|
+
The plugin for which to generate the keys.
|
53
|
+
|
54
|
+
mutable_only: bool, default False
|
55
|
+
If `True`, only return keys which may be edited.
|
56
|
+
|
57
|
+
Returns
|
58
|
+
-------
|
59
|
+
A dictionary mapping a plugins's keys to values.
|
60
|
+
"""
|
61
|
+
plugin_attributes_str = json.dumps(plugin.attributes, separators=(',', ':'))
|
62
|
+
mutable_keys_vals = {
|
63
|
+
cls.get_plugin_key(plugin.name, 'attributes'): plugin_attributes_str,
|
64
|
+
cls.get_plugin_key(plugin.name, 'version'): plugin.version,
|
65
|
+
}
|
66
|
+
if mutable_only:
|
67
|
+
return mutable_keys_vals
|
68
|
+
|
69
|
+
immutable_keys_vals = {
|
70
|
+
cls.get_plugin_key(plugin.name, 'user_id'): plugin.user_id,
|
71
|
+
}
|
72
|
+
|
73
|
+
return {**immutable_keys_vals, **mutable_keys_vals}
|
74
|
+
|
75
|
+
|
76
|
+
def register_plugin(
|
77
|
+
self,
|
78
|
+
plugin: 'mrsm.core.Plugin',
|
79
|
+
force: bool = False,
|
80
|
+
debug: bool = False,
|
81
|
+
**kw: Any
|
82
|
+
) -> SuccessTuple:
|
83
|
+
"""Register a new plugin to the `mrsm_plugins` "table"."""
|
84
|
+
from meerschaum.utils.misc import generate_password
|
85
|
+
|
86
|
+
plugins_pipe = self.get_plugins_pipe()
|
87
|
+
keys_vals = self.get_plugin_keys_vals(plugin)
|
88
|
+
|
89
|
+
try:
|
90
|
+
sync_success, sync_msg = plugins_pipe.sync(
|
91
|
+
[
|
92
|
+
{
|
93
|
+
'plugin_name': plugin.name,
|
94
|
+
'user_id': plugin.user_id,
|
95
|
+
},
|
96
|
+
],
|
97
|
+
check_existing=False,
|
98
|
+
debug=debug,
|
99
|
+
)
|
100
|
+
if not sync_success:
|
101
|
+
return sync_success, sync_msg
|
102
|
+
|
103
|
+
for key, val in keys_vals.items():
|
104
|
+
if val is not None:
|
105
|
+
self.set(key, val)
|
106
|
+
|
107
|
+
success, msg = True, "Success"
|
108
|
+
except Exception as e:
|
109
|
+
success = False
|
110
|
+
msg = f"Failed to register plugin '{plugin.name}':\n{e}"
|
111
|
+
|
112
|
+
if not success:
|
113
|
+
for key in keys_vals:
|
114
|
+
try:
|
115
|
+
self.client.delete(key)
|
116
|
+
except Exception:
|
117
|
+
pass
|
118
|
+
|
119
|
+
return success, msg
|
120
|
+
|
121
|
+
|
122
|
+
def get_plugin_id(
|
123
|
+
self,
|
124
|
+
plugin: 'mrsm.core.Plugin',
|
125
|
+
debug: bool = False
|
126
|
+
) -> Union[str, None]:
|
127
|
+
"""
|
128
|
+
Return a plugin's ID.
|
129
|
+
"""
|
130
|
+
return plugin.name
|
131
|
+
|
132
|
+
|
133
|
+
def get_plugin_version(
|
134
|
+
self,
|
135
|
+
plugin: 'mrsm.core.Plugin',
|
136
|
+
debug: bool = False,
|
137
|
+
) -> Union[str, None]:
|
138
|
+
"""
|
139
|
+
Return a plugin's version.
|
140
|
+
"""
|
141
|
+
version_key = self.get_plugin_key(plugin.name, 'version')
|
142
|
+
|
143
|
+
try:
|
144
|
+
return self.get(version_key)
|
145
|
+
except Exception:
|
146
|
+
return None
|
147
|
+
|
148
|
+
|
149
|
+
def get_plugin_user_id(
|
150
|
+
self,
|
151
|
+
plugin: 'mrsm.core.Plugin',
|
152
|
+
debug: bool = False
|
153
|
+
) -> Union[str, None]:
|
154
|
+
"""
|
155
|
+
Return a plugin's user ID.
|
156
|
+
"""
|
157
|
+
user_id_key = self.get_plugin_key(plugin.name, 'user_id')
|
158
|
+
|
159
|
+
try:
|
160
|
+
return self.get(user_id_key)
|
161
|
+
except Exception:
|
162
|
+
return None
|
163
|
+
|
164
|
+
|
165
|
+
def get_plugin_username(
|
166
|
+
self,
|
167
|
+
plugin: 'mrsm.core.Plugin',
|
168
|
+
debug: bool = False
|
169
|
+
) -> Union[str]:
|
170
|
+
"""
|
171
|
+
Return the username of a plugin's owner.
|
172
|
+
"""
|
173
|
+
user_id = self.get_plugin_user_id(plugin, debug=debug)
|
174
|
+
if user_id is None:
|
175
|
+
return None
|
176
|
+
|
177
|
+
username_key = self.get_user_key(user_id, 'username')
|
178
|
+
try:
|
179
|
+
return self.get(username_key)
|
180
|
+
except Exception:
|
181
|
+
return None
|
182
|
+
|
183
|
+
|
184
|
+
def get_plugin_attributes(
|
185
|
+
self,
|
186
|
+
plugin: 'mrsm.core.Plugin',
|
187
|
+
debug: bool = False
|
188
|
+
) -> Dict[str, Any]:
|
189
|
+
"""
|
190
|
+
Return the attributes of a plugin.
|
191
|
+
"""
|
192
|
+
attributes_key = self.get_plugin_key(plugin.name, 'attributes')
|
193
|
+
try:
|
194
|
+
attributes_str = self.get(attributes_key)
|
195
|
+
if not attributes_str:
|
196
|
+
return {}
|
197
|
+
return json.loads(attributes_str)
|
198
|
+
except Exception:
|
199
|
+
return {}
|
200
|
+
|
201
|
+
|
202
|
+
def get_plugins(
|
203
|
+
self,
|
204
|
+
user_id: Optional[int] = None,
|
205
|
+
search_term: Optional[str] = None,
|
206
|
+
debug: bool = False,
|
207
|
+
**kw: Any
|
208
|
+
) -> List[str]:
|
209
|
+
"""
|
210
|
+
Return a list of plugin names.
|
211
|
+
"""
|
212
|
+
plugins_pipe = self.get_plugins_pipe()
|
213
|
+
params = {}
|
214
|
+
if user_id:
|
215
|
+
params['user_id'] = user_id
|
216
|
+
|
217
|
+
df = plugins_pipe.get_data(['plugin_name'], params=params, debug=debug)
|
218
|
+
docs = df.to_dict(orient='records')
|
219
|
+
|
220
|
+
return [
|
221
|
+
doc['plugin_name']
|
222
|
+
for doc in docs
|
223
|
+
if (plugin_name := doc['plugin_name']).startswith(search_term or '')
|
224
|
+
]
|
225
|
+
|
226
|
+
|
227
|
+
def delete_plugin(
|
228
|
+
self,
|
229
|
+
plugin: 'mrsm.core.Plugin',
|
230
|
+
debug: bool = False,
|
231
|
+
**kw: Any
|
232
|
+
) -> SuccessTuple:
|
233
|
+
"""
|
234
|
+
Delete a plugin from the plugins table.
|
235
|
+
"""
|
236
|
+
plugins_pipe = self.get_plugins_pipe()
|
237
|
+
clear_success, clear_msg = plugins_pipe.clear(params={'plugin_name': plugin.name}, debug=debug)
|
238
|
+
if not clear_success:
|
239
|
+
return clear_success, clear_msg
|
240
|
+
|
241
|
+
keys_vals = self.get_plugin_keys_vals(plugin)
|
242
|
+
try:
|
243
|
+
old_keys_vals = {
|
244
|
+
key: self.get(key)
|
245
|
+
for key in keys_vals
|
246
|
+
}
|
247
|
+
except Exception as e:
|
248
|
+
return False, f"Failed to delete plugin '{plugin.name}':\n{e}"
|
249
|
+
|
250
|
+
try:
|
251
|
+
for key in keys_vals:
|
252
|
+
self.client.delete(key)
|
253
|
+
success, msg = True, "Success"
|
254
|
+
except Exception as e:
|
255
|
+
success = False
|
256
|
+
msg = f"Failed to delete plugin '{plugin.name}':\n{e}"
|
257
|
+
|
258
|
+
if not success:
|
259
|
+
try:
|
260
|
+
for key, old_val in old_keys_vals.items():
|
261
|
+
self.set(key, old_val)
|
262
|
+
except Exception:
|
263
|
+
pass
|
264
|
+
|
265
|
+
return success, msg
|
@@ -0,0 +1,305 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define methods for managing users.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from __future__ import annotations
|
9
|
+
|
10
|
+
import json
|
11
|
+
|
12
|
+
import meerschaum as mrsm
|
13
|
+
from meerschaum.utils.typing import Any, Union, SuccessTuple, Dict, List
|
14
|
+
|
15
|
+
USERS_TABLE: str = 'mrsm_users'
|
16
|
+
USER_PREFIX: str = 'mrsm_user'
|
17
|
+
|
18
|
+
|
19
|
+
def get_users_pipe(self):
|
20
|
+
"""
|
21
|
+
Return the pipe which stores the registered users.
|
22
|
+
"""
|
23
|
+
return mrsm.Pipe(
|
24
|
+
'mrsm', 'users',
|
25
|
+
columns=['user_id'],
|
26
|
+
temporary=True,
|
27
|
+
target=USERS_TABLE,
|
28
|
+
instance=self,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def get_user_key(cls, user_id_or_username: str, sub_key: str, by_username: bool = False) -> str:
|
34
|
+
"""
|
35
|
+
Return the key to store metadata about a user.
|
36
|
+
|
37
|
+
Parameters
|
38
|
+
----------
|
39
|
+
user_id_or_username: str
|
40
|
+
The user ID or username of the given user.
|
41
|
+
If `by_username` is `True`, then provide the username.
|
42
|
+
|
43
|
+
sub_key: str
|
44
|
+
The key suffix, e.g. `'attributes'`.
|
45
|
+
|
46
|
+
by_username: bool, default False
|
47
|
+
If `True`, then treat `user_id_or_username` as a username.
|
48
|
+
|
49
|
+
Returns
|
50
|
+
-------
|
51
|
+
A key to store information about a user.
|
52
|
+
|
53
|
+
Examples
|
54
|
+
--------
|
55
|
+
>>> get_user_key('deadbeef', 'attributes')
|
56
|
+
'mrsm_user:user_id:deadbeef:attributes'
|
57
|
+
>>> get_user_key('foo', 'user_id', by_username=True)
|
58
|
+
'mrsm_user:username:foo:user_id'
|
59
|
+
"""
|
60
|
+
key_type = 'user_id' if not by_username else 'username'
|
61
|
+
return cls.get_entity_key(USER_PREFIX, key_type, user_id_or_username, sub_key)
|
62
|
+
|
63
|
+
|
64
|
+
@classmethod
|
65
|
+
def get_user_keys_vals(
|
66
|
+
cls,
|
67
|
+
user: 'mrsm.core.User',
|
68
|
+
mutable_only: bool = False,
|
69
|
+
) -> Dict[str, str]:
|
70
|
+
"""
|
71
|
+
Return a dictionary containing keys and values to set for the user.
|
72
|
+
|
73
|
+
Parameters
|
74
|
+
----------
|
75
|
+
user: mrsm.core.User
|
76
|
+
The user for which to generate the keys.
|
77
|
+
|
78
|
+
mutable_only: bool, default False
|
79
|
+
If `True`, only return keys which may be edited.
|
80
|
+
|
81
|
+
Returns
|
82
|
+
-------
|
83
|
+
A dictionary mapping a user's keys to values.
|
84
|
+
"""
|
85
|
+
user_attributes_str = json.dumps(user.attributes, separators=(',', ':'))
|
86
|
+
mutable_keys_vals = {
|
87
|
+
cls.get_user_key(user.user_id, 'attributes'): user_attributes_str,
|
88
|
+
cls.get_user_key(user.user_id, 'email'): user.email,
|
89
|
+
cls.get_user_key(user.user_id, 'type'): user.type,
|
90
|
+
cls.get_user_key(user.user_id, 'password_hash'): user.password_hash,
|
91
|
+
}
|
92
|
+
if mutable_only:
|
93
|
+
return mutable_keys_vals
|
94
|
+
|
95
|
+
immutable_keys_vals = {
|
96
|
+
cls.get_user_key(user.user_id, 'username'): user.username,
|
97
|
+
cls.get_user_key(user.username, 'user_id', by_username=True): user.user_id,
|
98
|
+
}
|
99
|
+
|
100
|
+
return {**immutable_keys_vals, **mutable_keys_vals}
|
101
|
+
|
102
|
+
|
103
|
+
def register_user(
|
104
|
+
self,
|
105
|
+
user: 'mrsm.core.User',
|
106
|
+
debug: bool = False,
|
107
|
+
**kwargs: Any
|
108
|
+
) -> SuccessTuple:
|
109
|
+
"""
|
110
|
+
Register a new user.
|
111
|
+
"""
|
112
|
+
from meerschaum.utils.misc import generate_password
|
113
|
+
|
114
|
+
user.user_id = generate_password(12)
|
115
|
+
users_pipe = self.get_users_pipe()
|
116
|
+
keys_vals = self.get_user_keys_vals(user)
|
117
|
+
|
118
|
+
try:
|
119
|
+
sync_success, sync_msg = users_pipe.sync(
|
120
|
+
[
|
121
|
+
{
|
122
|
+
'user_id': user.user_id,
|
123
|
+
'username': user.username,
|
124
|
+
},
|
125
|
+
],
|
126
|
+
check_existing=False,
|
127
|
+
debug=debug,
|
128
|
+
)
|
129
|
+
if not sync_success:
|
130
|
+
return sync_success, sync_msg
|
131
|
+
|
132
|
+
for key, val in keys_vals.items():
|
133
|
+
if val is not None:
|
134
|
+
self.set(key, val)
|
135
|
+
|
136
|
+
success, msg = True, "Success"
|
137
|
+
except Exception as e:
|
138
|
+
success = False
|
139
|
+
import traceback
|
140
|
+
traceback.print_exc()
|
141
|
+
msg = f"Failed to register '{user.username}':\n{e}"
|
142
|
+
|
143
|
+
if not success:
|
144
|
+
for key in keys_vals:
|
145
|
+
try:
|
146
|
+
self.client.delete(key)
|
147
|
+
except Exception:
|
148
|
+
pass
|
149
|
+
|
150
|
+
return success, msg
|
151
|
+
|
152
|
+
|
153
|
+
def get_user_id(self, user: 'mrsm.core.User', debug: bool = False) -> Union[str, None]:
|
154
|
+
"""
|
155
|
+
Return the ID for a user, or `None`.
|
156
|
+
"""
|
157
|
+
username_user_id_key = self.get_user_key(user.username, 'user_id', by_username=True)
|
158
|
+
try:
|
159
|
+
user_id = self.get(username_user_id_key)
|
160
|
+
except Exception:
|
161
|
+
user_id = None
|
162
|
+
return user_id
|
163
|
+
|
164
|
+
|
165
|
+
def edit_user(
|
166
|
+
self,
|
167
|
+
user: 'mrsm.core.User',
|
168
|
+
debug: bool = False,
|
169
|
+
**kw: Any
|
170
|
+
) -> SuccessTuple:
|
171
|
+
"""
|
172
|
+
Edit the attributes for an existing user.
|
173
|
+
"""
|
174
|
+
keys_vals = self.get_user_keys_vals(user, mutable_only=True)
|
175
|
+
try:
|
176
|
+
old_keys_vals = {
|
177
|
+
key: self.get(key)
|
178
|
+
for key in keys_vals
|
179
|
+
}
|
180
|
+
except Exception as e:
|
181
|
+
return False, f"Failed to edit user:\n{e}"
|
182
|
+
|
183
|
+
try:
|
184
|
+
for key, val in keys_vals.items():
|
185
|
+
self.set(key, val)
|
186
|
+
success, msg = True, "Success"
|
187
|
+
except Exception as e:
|
188
|
+
success = False
|
189
|
+
msg = f"Failed to edit user:\n{e}"
|
190
|
+
|
191
|
+
if not success:
|
192
|
+
try:
|
193
|
+
for key, old_val in old_keys_vals.items():
|
194
|
+
self.set(key, old_val)
|
195
|
+
except Exception:
|
196
|
+
pass
|
197
|
+
|
198
|
+
return success, msg
|
199
|
+
|
200
|
+
|
201
|
+
def get_user_attributes(
|
202
|
+
self,
|
203
|
+
user: 'mrsm.core.User',
|
204
|
+
debug: bool = False
|
205
|
+
) -> Union[Dict[str, Any], None]:
|
206
|
+
"""
|
207
|
+
Return the user's attributes.
|
208
|
+
"""
|
209
|
+
user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
|
210
|
+
user_id_attributes_key = self.get_user_key(user_id, 'attributes')
|
211
|
+
try:
|
212
|
+
return json.loads(self.get(user_id_attributes_key))
|
213
|
+
except Exception:
|
214
|
+
return None
|
215
|
+
|
216
|
+
|
217
|
+
def delete_user(
|
218
|
+
self,
|
219
|
+
user: 'mrsm.core.User',
|
220
|
+
debug: bool = False
|
221
|
+
) -> SuccessTuple:
|
222
|
+
"""
|
223
|
+
Delete a user's keys.
|
224
|
+
"""
|
225
|
+
user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
|
226
|
+
users_pipe = self.get_users_pipe()
|
227
|
+
keys_vals = self.get_user_keys_vals(user)
|
228
|
+
try:
|
229
|
+
old_keys_vals = {
|
230
|
+
key: self.get(key)
|
231
|
+
for key in keys_vals
|
232
|
+
}
|
233
|
+
except Exception as e:
|
234
|
+
return False, f"Failed to delete user:\n{e}"
|
235
|
+
|
236
|
+
clear_success, clear_msg = users_pipe.clear(params={'user_id': user_id})
|
237
|
+
if not clear_success:
|
238
|
+
return clear_success, clear_msg
|
239
|
+
|
240
|
+
try:
|
241
|
+
for key in keys_vals:
|
242
|
+
self.client.delete(key)
|
243
|
+
success, msg = True, "Success"
|
244
|
+
except Exception as e:
|
245
|
+
success = False
|
246
|
+
msg = f"Failed to delete user:\n{e}"
|
247
|
+
|
248
|
+
if not success:
|
249
|
+
try:
|
250
|
+
for key, old_val in old_keys_vals.items():
|
251
|
+
self.set(key, old_val)
|
252
|
+
except Exception:
|
253
|
+
pass
|
254
|
+
|
255
|
+
return success, msg
|
256
|
+
|
257
|
+
|
258
|
+
def get_users(
|
259
|
+
self,
|
260
|
+
debug: bool = False,
|
261
|
+
**kw: Any
|
262
|
+
) -> List[str]:
|
263
|
+
"""
|
264
|
+
Get the registered usernames.
|
265
|
+
"""
|
266
|
+
users_pipe = self.get_users_pipe()
|
267
|
+
df = users_pipe.get_data()
|
268
|
+
if df is None:
|
269
|
+
return []
|
270
|
+
|
271
|
+
return list(df['username'])
|
272
|
+
|
273
|
+
|
274
|
+
def get_user_password_hash(
|
275
|
+
self,
|
276
|
+
user: 'mrsm.core.User',
|
277
|
+
debug: bool = False,
|
278
|
+
**kw: Any
|
279
|
+
) -> Union[str, None]:
|
280
|
+
"""
|
281
|
+
Return the password has for a user.
|
282
|
+
"""
|
283
|
+
user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
|
284
|
+
user_id_password_hash_key = self.get_user_key(user_id, 'password_hash')
|
285
|
+
try:
|
286
|
+
return self.get(user_id_password_hash_key)
|
287
|
+
except Exception:
|
288
|
+
return None
|
289
|
+
|
290
|
+
|
291
|
+
def get_user_type(
|
292
|
+
self,
|
293
|
+
user: 'mrsm.core.User',
|
294
|
+
debug: bool = False,
|
295
|
+
**kw: Any
|
296
|
+
) -> Union[str, None]:
|
297
|
+
"""
|
298
|
+
Return the user's type.
|
299
|
+
"""
|
300
|
+
user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
|
301
|
+
user_id_type_key = self.get_user_key(user_id, 'type')
|
302
|
+
try:
|
303
|
+
return self.get(user_id_type_key)
|
304
|
+
except Exception:
|
305
|
+
return None
|
meerschaum/core/Pipe/__init__.py
CHANGED
@@ -121,6 +121,8 @@ class Pipe:
|
|
121
121
|
filter_existing,
|
122
122
|
_get_chunk_label,
|
123
123
|
get_num_workers,
|
124
|
+
_persist_new_json_columns,
|
125
|
+
_persist_new_numeric_columns,
|
124
126
|
)
|
125
127
|
from ._verify import (
|
126
128
|
verify,
|
@@ -133,6 +135,7 @@ class Pipe:
|
|
133
135
|
from ._deduplicate import deduplicate
|
134
136
|
from ._bootstrap import bootstrap
|
135
137
|
from ._dtypes import enforce_dtypes, infer_dtypes
|
138
|
+
from ._copy import copy_to
|
136
139
|
|
137
140
|
def __init__(
|
138
141
|
self,
|
@@ -122,8 +122,7 @@ def dtypes(self) -> Union[Dict[str, Any], None]:
|
|
122
122
|
configured_dtypes = self.parameters.get('dtypes', {})
|
123
123
|
remote_dtypes = self.infer_dtypes(persist=False)
|
124
124
|
patched_dtypes = apply_patch_to_config(remote_dtypes, configured_dtypes)
|
125
|
-
|
126
|
-
return self.parameters['dtypes']
|
125
|
+
return patched_dtypes
|
127
126
|
|
128
127
|
|
129
128
|
@dtypes.setter
|
meerschaum/core/Pipe/_clear.py
CHANGED
@@ -7,25 +7,28 @@ Clear pipe data within a bounded or unbounded interval.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
from datetime import datetime
|
10
12
|
from meerschaum.utils.typing import SuccessTuple, Any, Optional, Dict
|
11
13
|
|
14
|
+
|
12
15
|
def clear(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
self,
|
17
|
+
begin: Optional[datetime] = None,
|
18
|
+
end: Optional[datetime] = None,
|
19
|
+
params: Optional[Dict[str, Any]] = None,
|
20
|
+
debug: bool = False,
|
21
|
+
**kwargs: Any
|
22
|
+
) -> SuccessTuple:
|
20
23
|
"""
|
21
24
|
Call the Pipe's instance connector's `clear_pipe` method.
|
22
25
|
|
23
26
|
Parameters
|
24
27
|
----------
|
25
|
-
begin: Optional[datetime
|
28
|
+
begin: Optional[datetime], default None:
|
26
29
|
If provided, only remove rows newer than this datetime value.
|
27
30
|
|
28
|
-
end: Optional[datetime
|
31
|
+
end: Optional[datetime], default None:
|
29
32
|
If provided, only remove rows older than this datetime column (not including end).
|
30
33
|
|
31
34
|
params: Optional[Dict[str, Any]], default None
|
@@ -41,11 +44,11 @@ def clear(
|
|
41
44
|
Examples
|
42
45
|
--------
|
43
46
|
>>> pipe = mrsm.Pipe('test', 'test', columns={'datetime': 'dt'}, instance='sql:local')
|
44
|
-
>>> pipe.sync({'dt': [datetime
|
45
|
-
>>> pipe.sync({'dt': [datetime
|
46
|
-
>>> pipe.sync({'dt': [datetime
|
47
|
+
>>> pipe.sync({'dt': [datetime(2020, 1, 1, 0, 0)]})
|
48
|
+
>>> pipe.sync({'dt': [datetime(2021, 1, 1, 0, 0)]})
|
49
|
+
>>> pipe.sync({'dt': [datetime(2022, 1, 1, 0, 0)]})
|
47
50
|
>>>
|
48
|
-
>>> pipe.clear(begin=datetime
|
51
|
+
>>> pipe.clear(begin=datetime(2021, 1, 1, 0, 0))
|
49
52
|
>>> pipe.get_data()
|
50
53
|
dt
|
51
54
|
0 2020-01-01
|