ezKit 1.10.4__tar.gz → 1.10.6__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.10.4
3
+ Version: 1.10.6
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -0,0 +1,363 @@
1
+ """Database"""
2
+ # Column, Table, MetaData API
3
+ # https://docs.sqlalchemy.org/en/14/core/metadata.html#column-table-metadata-api
4
+ # CursorResult
5
+ # https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.CursorResult
6
+ # PostgreSQL 14 Data Types
7
+ # https://www.postgresql.org/docs/14/datatype.html
8
+ import csv
9
+ import json
10
+ from typing import Any
11
+
12
+ import pandas as pd
13
+ from loguru import logger
14
+ from sqlalchemy import CursorResult, Index, create_engine, text
15
+ from sqlalchemy.orm import DeclarativeBase
16
+
17
+ from . import utils
18
+
19
+
20
+ class Database():
21
+ """Database"""
22
+
23
+ engine = create_engine('sqlite://')
24
+
25
+ def __init__(self, target: str | None = None, **options):
26
+ """Initiation"""
27
+ if isinstance(target, str) and utils.isTrue(target, str):
28
+ if utils.isTrue(options, dict):
29
+ self.engine = create_engine(target, **options)
30
+ else:
31
+ self.engine = create_engine(target)
32
+ else:
33
+ pass
34
+
35
+ # ----------------------------------------------------------------------------------------------
36
+
37
+ def initializer(self):
38
+ """ensure the parent proc's database connections are not touched in the new connection pool"""
39
+ self.engine.dispose(close=False)
40
+
41
+ # ----------------------------------------------------------------------------------------------
42
+
43
+ def connect_test(self) -> bool:
44
+ info = "Database connect test"
45
+ try:
46
+ logger.info(f"{info} ......")
47
+ self.engine.connect()
48
+ logger.success(f"{info} [success]")
49
+ return True
50
+ except Exception as e:
51
+ logger.error(f"{info} [failure]")
52
+ logger.exception(e)
53
+ return False
54
+
55
+ # ----------------------------------------------------------------------------------------------
56
+
57
+ def metadata_init(self, base: DeclarativeBase, **kwargs) -> bool:
58
+ # https://stackoverflow.com/questions/19175311/how-to-create-only-one-table-with-sqlalchemy
59
+ info = "Database init table"
60
+ try:
61
+ logger.info(f"{info} ......")
62
+ base.metadata.drop_all(self.engine, **kwargs)
63
+ base.metadata.create_all(self.engine, **kwargs)
64
+ logger.success(f"{info} [success]")
65
+ return True
66
+ except Exception as e:
67
+ logger.error(f"{info} [failure]")
68
+ logger.exception(e)
69
+ return False
70
+
71
+ # ----------------------------------------------------------------------------------------------
72
+
73
+ def create_index(self, index_name, table_field) -> bool:
74
+ # 创建索引
75
+ # https://stackoverflow.com/a/41254430
76
+ # 示例:
77
+ # index_name: a_share_list_code_idx1
78
+ # table_field: Table_a_share_list.code
79
+ info = "Database create index"
80
+ try:
81
+ logger.info(f"{info} ......")
82
+ idx = Index(index_name, table_field)
83
+ try:
84
+ idx.drop(bind=self.engine)
85
+ except Exception as e:
86
+ logger.exception(e)
87
+ idx.create(bind=self.engine)
88
+ logger.success(f'{info} [success]')
89
+ return True
90
+ except Exception as e:
91
+ logger.error(f'{info} [failure]')
92
+ logger.error(e)
93
+ return False
94
+
95
+ # ----------------------------------------------------------------------------------------------
96
+
97
+ # 私有函数, 保存 execute 的结果到 CSV 文件
98
+ def _result_save(self, file, data) -> bool:
99
+ try:
100
+ outcsv = csv.writer(file)
101
+ outcsv.writerow(data.keys())
102
+ outcsv.writerows(data)
103
+ logger.success("save to csv success")
104
+ return True
105
+ except Exception as e:
106
+ logger.error("save to csv failed")
107
+ logger.exception(e)
108
+ return False
109
+
110
+ # ----------------------------------------------------------------------------------------------
111
+
112
+ # def execute(
113
+ # self,
114
+ # sql: str | None = None,
115
+ # sql_file: str | None = None,
116
+ # sql_file_kwargs: dict | None = None,
117
+ # csv_file: str | None = None,
118
+ # csv_file_kwargs: dict | None = None
119
+ # ) -> CursorResult[Any] | bool:
120
+ # """"运行"""
121
+
122
+ # # ------------------------------------------------------------
123
+
124
+ # # 提取 SQL
125
+ # # 如果 sql 和 sql_file 同时存在, 优先执行 sql
126
+
127
+ # sql_object = None
128
+
129
+ # info: str = f"""Extract SQL: {sql}"""
130
+
131
+ # try:
132
+
133
+ # logger.info(f"{info} ......")
134
+
135
+ # if utils.isTrue(sql, str):
136
+
137
+ # sql_object = sql
138
+
139
+ # elif sql_file is not None and utils.isTrue(sql_file, str):
140
+
141
+ # # 判断文件是否存在
142
+ # if isinstance(sql_file, str) and utils.check_file_type(sql_file, "file") is False:
143
+
144
+ # logger.error(f"No such file: {sql_file}")
145
+ # return False
146
+
147
+ # if isinstance(sql_file, str) and utils.isTrue(sql_file, str):
148
+
149
+ # # 读取文件内容
150
+ # if sql_file_kwargs is not None and utils.isTrue(sql_file_kwargs, dict):
151
+ # with open(sql_file, "r", encoding="utf-8", **sql_file_kwargs) as _file:
152
+ # sql_object = _file.read()
153
+ # else:
154
+ # with open(sql_file, "r", encoding="utf-8") as _file:
155
+ # sql_object = _file.read()
156
+
157
+ # else:
158
+
159
+ # logger.error("SQL or SQL file error")
160
+ # logger.error(f"{info} [failure]")
161
+ # return False
162
+
163
+ # logger.success(f'{info} [success]')
164
+
165
+ # except Exception as e:
166
+
167
+ # logger.error(f"{info} [failure]")
168
+ # logger.exception(e)
169
+ # return False
170
+
171
+ # # ------------------------------------------------------------
172
+
173
+ # # 执行 SQL
174
+
175
+ # info = f"""Execute SQL: {sql_object}"""
176
+
177
+ # try:
178
+
179
+ # logger.info(f"{info} ......")
180
+
181
+ # with self.engine.connect() as connect:
182
+
183
+ # # 执行SQL
184
+ # if sql_object is None:
185
+ # return False
186
+
187
+ # result = connect.execute(text(sql_object))
188
+
189
+ # connect.commit()
190
+
191
+ # if csv_file is None:
192
+ # # 如果 csv_file 没有定义, 则直接返回结果
193
+ # logger.success(f'{info} [success]')
194
+ # return result
195
+
196
+ # # 如果 csv_file 有定义, 则保存结果到 csv_file
197
+ # info_of_save = f"Save result to file: {csv_file}"
198
+ # logger.info(f"{info_of_save} .......")
199
+
200
+ # # 保存结果
201
+ # if isinstance(csv_file_kwargs, dict) and utils.isTrue(csv_file_kwargs, dict):
202
+ # with open(csv_file, "w", encoding="utf-8", **csv_file_kwargs) as _file:
203
+ # result_of_save = self._result_save(_file, result)
204
+ # else:
205
+ # with open(csv_file, "w", encoding="utf-8") as _file:
206
+ # result_of_save = self._result_save(_file, result)
207
+
208
+ # # 检查保存结果
209
+ # if result_of_save is True:
210
+ # logger.success(f'{info_of_save} [success]')
211
+ # logger.success(f'{info} [success]')
212
+ # return True
213
+
214
+ # logger.error(f"{info_of_save} [failure]")
215
+ # logger.error(f"{info} [failure]")
216
+ # return False
217
+
218
+ # except Exception as e:
219
+
220
+ # logger.error(f'{info} [failure]')
221
+ # logger.exception(e)
222
+ # return False
223
+
224
+ # ----------------------------------------------------------------------------------------------
225
+
226
+ def connect_execute(
227
+ self,
228
+ sql: str | None = None,
229
+ read_sql_file: dict | None = None,
230
+ save_to_csv: dict | None = None
231
+ ) -> CursorResult[Any] | bool | None:
232
+
233
+ info: str = 'Database connect execute'
234
+
235
+ logger.info(f"{info} ......")
236
+
237
+ sql_statement: str = ""
238
+
239
+ # ------------------------------------------------------------------------------------------
240
+
241
+ try:
242
+ # SQL文件优先
243
+ if isinstance(read_sql_file, dict) and utils.isTrue(read_sql_file, dict):
244
+ read_sql_file.pop("encoding")
245
+ read_sql_file_kwargs: dict = {
246
+ "mode": "r",
247
+ "encoding": "utf-8",
248
+ **read_sql_file
249
+ }
250
+ with open(encoding="utf-8", **read_sql_file_kwargs) as _file:
251
+ sql_statement = _file.read()
252
+ else:
253
+ if not (isinstance(sql, str) and utils.check_arguments([(sql, str, "sql")])):
254
+ return None
255
+ sql_statement = sql
256
+ except Exception as e:
257
+ logger.exception(e)
258
+ return None
259
+
260
+ # ------------------------------------------------------------------------------------------
261
+
262
+ if not self.connect_test():
263
+ return None
264
+
265
+ # ------------------------------------------------------------------------------------------
266
+
267
+ # 创建一个连接
268
+ with self.engine.connect() as connection:
269
+
270
+ # 开始一个事务
271
+ with connection.begin(): # 事务会自动提交或回滚
272
+
273
+ try:
274
+
275
+ # 执行 SQL 查询
276
+ result = connection.execute(text(sql_statement))
277
+
278
+ # 执行成功
279
+ logger.success(f"{info} [success]")
280
+
281
+ # 返回查询结果
282
+ if isinstance(save_to_csv, dict) and utils.isTrue(save_to_csv, dict):
283
+ save_to_csv_kwargs: dict = {
284
+ "mode": "w",
285
+ "encoding": "utf-8",
286
+ **save_to_csv
287
+ }
288
+ with open(encoding="utf-8", **save_to_csv_kwargs) as _file:
289
+ return self._result_save(_file, result)
290
+
291
+ return result
292
+
293
+ except Exception as e:
294
+ # 发生异常时回滚事务
295
+ logger.info(f"{info} [failed]")
296
+ logger.exception(e)
297
+ return None
298
+
299
+ # ----------------------------------------------------------------------------------------------
300
+
301
+ def read_with_pandas(
302
+ self,
303
+ method: str = "read_sql",
304
+ result_type: str = "df",
305
+ **kwargs
306
+ ) -> pd.DataFrame | list | dict:
307
+ """读取数据"""
308
+
309
+ # 使用SQL查询数据: 使用 pd.read_sql 的参数
310
+ # read_data_with_pandas(by="sql", result_type="df", sql="SELECT * FROM table ORDER BY date DESC LIMIT 1")
311
+
312
+ # 读取表中的数据: 使用 pd.read_sql_table 的参数
313
+ # read_data_with_pandas(by="table", result_type="df", table_name="ashare")
314
+
315
+ data: pd.DataFrame = pd.DataFrame()
316
+
317
+ if not utils.check_arguments([(method, str, "method")]):
318
+ return data
319
+
320
+ if not utils.check_arguments([(result_type, str, "result_type")]):
321
+ return data
322
+
323
+ info: str = "read data"
324
+
325
+ try:
326
+
327
+ logger.info(f"{info} ......")
328
+
329
+ # 从 kwargs 中删除 con 键
330
+ kwargs.pop('con', None)
331
+
332
+ match method:
333
+ case "read_sql":
334
+ data = pd.read_sql(con=self.engine, **kwargs)
335
+ case "read_sql_query":
336
+ data = pd.read_sql_query(con=self.engine, **kwargs)
337
+ case "read_sql_table":
338
+ data = pd.read_sql_table(con=self.engine, **kwargs)
339
+ case _:
340
+ logger.error(f"{info} [incorrect method: {method}]")
341
+ return data
342
+
343
+ if data.empty:
344
+ logger.error(f"{info} [failed]")
345
+ return data
346
+
347
+ logger.success(f"{info} [success]")
348
+
349
+ match result_type:
350
+ case "json":
351
+ return json.loads(data.to_json(orient='records'))
352
+ case "dict":
353
+ return data.to_dict()
354
+ case "list":
355
+ # https://stackoverflow.com/a/26716774
356
+ return data.to_dict('list')
357
+ case _:
358
+ return data
359
+
360
+ except Exception as e:
361
+ logger.error(f"{info} [failed]")
362
+ logger.exception(e)
363
+ return data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.10.4
3
+ Version: 1.10.6
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
 
4
4
  setup(
5
5
  name='ezKit',
6
- version='1.10.4',
6
+ version='1.10.6',
7
7
  author='septvean',
8
8
  author_email='septvean@gmail.com',
9
9
  description='Easy Kit',
@@ -1,206 +0,0 @@
1
- """Database"""
2
- # Column, Table, MetaData API
3
- # https://docs.sqlalchemy.org/en/14/core/metadata.html#column-table-metadata-api
4
- # CursorResult
5
- # https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.CursorResult
6
- # PostgreSQL 14 Data Types
7
- # https://www.postgresql.org/docs/14/datatype.html
8
- import csv
9
- from typing import Any
10
-
11
- from loguru import logger
12
- from sqlalchemy import CursorResult, Index, create_engine, text
13
- from sqlalchemy.orm import DeclarativeBase
14
-
15
- from . import utils
16
-
17
-
18
- class Database():
19
- """Database"""
20
-
21
- engine = create_engine('sqlite://')
22
-
23
- def __init__(self, target: str | None = None, **options):
24
- """Initiation"""
25
- if isinstance(target, str) and utils.isTrue(target, str):
26
- if utils.isTrue(options, dict):
27
- self.engine = create_engine(target, **options)
28
- else:
29
- self.engine = create_engine(target)
30
- else:
31
- pass
32
-
33
- def initializer(self):
34
- """ensure the parent proc's database connections are not touched in the new connection pool"""
35
- self.engine.dispose(close=False)
36
-
37
- def connect_test(self) -> bool:
38
- info = "Database connect test"
39
- try:
40
- logger.info(f"{info} ......")
41
- self.engine.connect()
42
- logger.success(f"{info} [success]")
43
- return True
44
- except Exception as e:
45
- logger.error(f"{info} [failure]")
46
- logger.exception(e)
47
- return False
48
-
49
- def metadata_init(self, base: DeclarativeBase, **kwargs) -> bool:
50
- # https://stackoverflow.com/questions/19175311/how-to-create-only-one-table-with-sqlalchemy
51
- info = "Database init table"
52
- try:
53
- logger.info(f"{info} ......")
54
- base.metadata.drop_all(self.engine, **kwargs)
55
- base.metadata.create_all(self.engine, **kwargs)
56
- logger.success(f"{info} [success]")
57
- return True
58
- except Exception as e:
59
- logger.error(f"{info} [failure]")
60
- logger.exception(e)
61
- return False
62
-
63
- def create_index(self, index_name, table_field) -> bool:
64
- # 创建索引
65
- # https://stackoverflow.com/a/41254430
66
- # 示例:
67
- # index_name: a_share_list_code_idx1
68
- # table_field: Table_a_share_list.code
69
- info = "Database create index"
70
- try:
71
- logger.info(f"{info} ......")
72
- idx = Index(index_name, table_field)
73
- try:
74
- idx.drop(bind=self.engine)
75
- except Exception as e:
76
- logger.exception(e)
77
- idx.create(bind=self.engine)
78
- logger.success(f'{info} [success]')
79
- return True
80
- except Exception as e:
81
- logger.error(f'{info} [failure]')
82
- logger.error(e)
83
- return False
84
-
85
- # 私有函数, 保存 execute 的结果到 CSV 文件
86
- def _result_save(self, file, data) -> bool:
87
- try:
88
- outcsv = csv.writer(file)
89
- outcsv.writerow(data.keys())
90
- outcsv.writerows(data)
91
- return True
92
- except Exception as e:
93
- logger.exception(e)
94
- return False
95
-
96
- def execute(
97
- self,
98
- sql: str | None = None,
99
- sql_file: str | None = None,
100
- sql_file_kwargs: dict | None = None,
101
- csv_file: str | None = None,
102
- csv_file_kwargs: dict | None = None
103
- ) -> CursorResult[Any] | bool:
104
- """"运行"""
105
-
106
- # ------------------------------------------------------------
107
-
108
- # 提取 SQL
109
- # 如果 sql 和 sql_file 同时存在, 优先执行 sql
110
-
111
- sql_object = None
112
-
113
- info: str = f"""Extract SQL: {sql}"""
114
-
115
- try:
116
-
117
- logger.info(f"{info} ......")
118
-
119
- if utils.isTrue(sql, str):
120
-
121
- sql_object = sql
122
-
123
- elif sql_file is not None and utils.isTrue(sql_file, str):
124
-
125
- # 判断文件是否存在
126
- if isinstance(sql_file, str) and utils.check_file_type(sql_file, "file") is False:
127
-
128
- logger.error(f"No such file: {sql_file}")
129
- return False
130
-
131
- if isinstance(sql_file, str) and utils.isTrue(sql_file, str):
132
-
133
- # 读取文件内容
134
- if sql_file_kwargs is not None and utils.isTrue(sql_file_kwargs, dict):
135
- with open(sql_file, "r", encoding="utf-8", **sql_file_kwargs) as _file:
136
- sql_object = _file.read()
137
- else:
138
- with open(sql_file, "r", encoding="utf-8") as _file:
139
- sql_object = _file.read()
140
-
141
- else:
142
-
143
- logger.error("SQL or SQL file error")
144
- logger.error(f"{info} [failure]")
145
- return False
146
-
147
- logger.success(f'{info} [success]')
148
-
149
- except Exception as e:
150
-
151
- logger.error(f"{info} [failure]")
152
- logger.exception(e)
153
- return False
154
-
155
- # ------------------------------------------------------------
156
-
157
- # 执行 SQL
158
-
159
- info = f"""Execute SQL: {sql_object}"""
160
-
161
- try:
162
-
163
- logger.info(f"{info} ......")
164
-
165
- with self.engine.connect() as connect:
166
-
167
- # 执行SQL
168
- if sql_object is None:
169
- return False
170
-
171
- result = connect.execute(text(sql_object))
172
-
173
- connect.commit()
174
-
175
- if csv_file is None:
176
- # 如果 csv_file 没有定义, 则直接返回结果
177
- logger.success(f'{info} [success]')
178
- return result
179
-
180
- # 如果 csv_file 有定义, 则保存结果到 csv_file
181
- info_of_save = f"Save result to file: {csv_file}"
182
- logger.info(f"{info_of_save} .......")
183
-
184
- # 保存结果
185
- if isinstance(csv_file_kwargs, dict) and utils.isTrue(csv_file_kwargs, dict):
186
- with open(csv_file, "w", encoding="utf-8", **csv_file_kwargs) as _file:
187
- result_of_save = self._result_save(_file, result)
188
- else:
189
- with open(csv_file, "w", encoding="utf-8") as _file:
190
- result_of_save = self._result_save(_file, result)
191
-
192
- # 检查保存结果
193
- if result_of_save is True:
194
- logger.success(f'{info_of_save} [success]')
195
- logger.success(f'{info} [success]')
196
- return True
197
-
198
- logger.error(f"{info_of_save} [failure]")
199
- logger.error(f"{info} [failure]")
200
- return False
201
-
202
- except Exception as e:
203
-
204
- logger.error(f'{info} [failure]')
205
- logger.exception(e)
206
- return False
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes