xtn-tools-pro 1.0.0.0.4__py3-none-any.whl → 1.0.0.0.6__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.
- xtn_tools_pro/db/MongoDB.py +2 -2
- xtn_tools_pro/db/MysqlDB.py +397 -0
- xtn_tools_pro/proxy/XiaoXiangProxy.py +1 -1
- xtn_tools_pro/tools.py +4 -114
- xtn_tools_pro/tools_flie.py +1 -1
- xtn_tools_pro/utils/__init__.py +10 -0
- xtn_tools_pro/utils/crypto.py +87 -0
- xtn_tools_pro/utils/file_utils.py +42 -0
- xtn_tools_pro/utils/helpers.py +123 -0
- xtn_tools_pro/utils/log.py +192 -0
- xtn_tools_pro/utils/retry.py +57 -0
- xtn_tools_pro/utils/sql.py +159 -0
- xtn_tools_pro/utils/time_utils.py +143 -0
- {xtn_tools_pro-1.0.0.0.4.dist-info → xtn_tools_pro-1.0.0.0.6.dist-info}/METADATA +4 -1
- xtn_tools_pro-1.0.0.0.6.dist-info/RECORD +23 -0
- xtn_tools_pro-1.0.0.0.4.dist-info/RECORD +0 -14
- {xtn_tools_pro-1.0.0.0.4.dist-info → xtn_tools_pro-1.0.0.0.6.dist-info}/LICENSE +0 -0
- {xtn_tools_pro-1.0.0.0.4.dist-info → xtn_tools_pro-1.0.0.0.6.dist-info}/WHEEL +0 -0
- {xtn_tools_pro-1.0.0.0.4.dist-info → xtn_tools_pro-1.0.0.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# 说明:
|
5
|
+
# 杂七杂八
|
6
|
+
# History:
|
7
|
+
# Date Author Version Modification
|
8
|
+
# --------------------------------------------------------------------------------------------------
|
9
|
+
# 2024/5/13 xiatn V00.01.000 新建
|
10
|
+
# --------------------------------------------------------------------------------------------------
|
11
|
+
import re
|
12
|
+
import uuid
|
13
|
+
import math
|
14
|
+
import json
|
15
|
+
from uuid import UUID
|
16
|
+
from pprint import pformat
|
17
|
+
from urllib.parse import urlencode
|
18
|
+
|
19
|
+
|
20
|
+
def get_uuid(version=4, namespace: UUID = uuid.NAMESPACE_DNS, name=""):
|
21
|
+
"""
|
22
|
+
生成uuid
|
23
|
+
:param version:版本号
|
24
|
+
1:基于当前时间和 MAC 地址生成版本 1 的 UUID,具有唯一性,但可能存在一定的安全风险。
|
25
|
+
3:基于名称和命名空间的方式生成。它通过将名称和命名空间的标识符组合起来进行哈希计算,生成一个唯一的标识符。UUID 版本 3 使用的哈希算法是 MD5。
|
26
|
+
4:使用随机数生成版本 4 的 UUID,具有足够的随机性和唯一性。
|
27
|
+
5:使用基于命名空间和名称生成版本 5 的 UUID,可以使用自定义的命名空间和名称。
|
28
|
+
:param namespace:命名空间 uuid.NAMESPACE_DNS、uuid.NAMESPACE_URL、uuid.NAMESPACE_OID、uuid.NAMESPACE_X500
|
29
|
+
:param name:名称 自定义
|
30
|
+
:return:
|
31
|
+
"""
|
32
|
+
if version == 1:
|
33
|
+
result = uuid.uuid1()
|
34
|
+
elif version == 3:
|
35
|
+
result = uuid.uuid3(namespace, name)
|
36
|
+
elif version == 5:
|
37
|
+
result = uuid.uuid5(namespace, name)
|
38
|
+
else:
|
39
|
+
result = uuid.uuid4()
|
40
|
+
|
41
|
+
uuid_str = str(result)
|
42
|
+
# uuid_hex = uuid_obj.hex
|
43
|
+
# uuid_int = uuid_obj.int
|
44
|
+
# uuid_bytes = uuid_obj.bytes
|
45
|
+
return uuid_str
|
46
|
+
|
47
|
+
|
48
|
+
def get_str_to_json(str_json):
|
49
|
+
"""
|
50
|
+
字符串类型的json格式 转 json
|
51
|
+
:param str_json: 字符串json
|
52
|
+
:return:
|
53
|
+
"""
|
54
|
+
try:
|
55
|
+
new_str_json = str_json.replace("'", '"'). \
|
56
|
+
replace("None", "null").replace("True", "true"). \
|
57
|
+
replace("False", "false")
|
58
|
+
return json.loads(new_str_json)
|
59
|
+
except Exception as e:
|
60
|
+
return {}
|
61
|
+
|
62
|
+
|
63
|
+
def list_to_strtuple(datas):
|
64
|
+
"""
|
65
|
+
列表转字符串元组
|
66
|
+
:param datas: datas: [1, 2]
|
67
|
+
:return: (1, 2) 字符串类型
|
68
|
+
"""
|
69
|
+
data_str = str(tuple(datas))
|
70
|
+
data_str = re.sub(",\)$", ")", data_str)
|
71
|
+
return data_str
|
72
|
+
|
73
|
+
|
74
|
+
def dumps_json(data, indent=4, sort_keys=False):
|
75
|
+
"""
|
76
|
+
将JSON数据格式化为可打印的字符串
|
77
|
+
:param data:
|
78
|
+
:param indent: 每一级嵌套都使用4个空格进行缩进
|
79
|
+
:param sort_keys: 是否排序
|
80
|
+
:return:
|
81
|
+
"""
|
82
|
+
try:
|
83
|
+
if isinstance(data, str):
|
84
|
+
data = get_str_to_json(data)
|
85
|
+
|
86
|
+
data = json.dumps(
|
87
|
+
data,
|
88
|
+
ensure_ascii=False,
|
89
|
+
indent=indent,
|
90
|
+
skipkeys=True,
|
91
|
+
sort_keys=sort_keys,
|
92
|
+
default=str,
|
93
|
+
)
|
94
|
+
|
95
|
+
except Exception as e:
|
96
|
+
data = pformat(data)
|
97
|
+
|
98
|
+
return data
|
99
|
+
|
100
|
+
|
101
|
+
def get_calculate_total_page(total, limit):
|
102
|
+
"""
|
103
|
+
根据total和limit计算出一共有多少页
|
104
|
+
:param total:
|
105
|
+
:param limit:
|
106
|
+
:return:
|
107
|
+
"""
|
108
|
+
if limit <= 0:
|
109
|
+
return 0
|
110
|
+
# 根据总条数和limit计算总页数
|
111
|
+
total_pages = math.ceil(total / limit)
|
112
|
+
return total_pages
|
113
|
+
|
114
|
+
def get_build_url_with_params(url, params):
|
115
|
+
"""
|
116
|
+
传入url和params拼接完整的url ->效果 https://wwww.xxxx.com/?xxx1=1&xxx2=2
|
117
|
+
:param url:
|
118
|
+
:param params:
|
119
|
+
:return:
|
120
|
+
"""
|
121
|
+
encoded_params = urlencode(params)
|
122
|
+
full_url = url + "?" + encoded_params
|
123
|
+
return full_url
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# 说明:
|
5
|
+
# 日志
|
6
|
+
# History:
|
7
|
+
# Date Author Version Modification
|
8
|
+
# --------------------------------------------------------------------------------------------------
|
9
|
+
# 2024/5/12 xiatn V00.01.000 新建
|
10
|
+
# --------------------------------------------------------------------------------------------------
|
11
|
+
import os
|
12
|
+
import sys
|
13
|
+
import inspect
|
14
|
+
import logging
|
15
|
+
from logging.handlers import BaseRotatingHandler
|
16
|
+
from xtn_tools_pro.utils.time_utils import get_time_timestamp_to_datestr
|
17
|
+
|
18
|
+
|
19
|
+
class RotatingFileHandler(BaseRotatingHandler):
|
20
|
+
def __init__(
|
21
|
+
self, filename, mode="a", max_bytes=0, backup_count=0, encoding=None, delay=0
|
22
|
+
):
|
23
|
+
BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
|
24
|
+
self.max_bytes = max_bytes
|
25
|
+
self.backup_count = backup_count
|
26
|
+
self.placeholder = str(len(str(backup_count)))
|
27
|
+
|
28
|
+
def doRollover(self):
|
29
|
+
if self.stream:
|
30
|
+
self.stream.close()
|
31
|
+
self.stream = None
|
32
|
+
if self.backup_count > 0:
|
33
|
+
for i in range(self.backup_count - 1, 0, -1):
|
34
|
+
sfn = ("%0" + self.placeholder + "d.") % i # '%2d.'%i -> 02
|
35
|
+
sfn = sfn.join(self.baseFilename.split("."))
|
36
|
+
# sfn = "%d_%s" % (i, self.baseFilename)
|
37
|
+
# dfn = "%d_%s" % (i + 1, self.baseFilename)
|
38
|
+
dfn = ("%0" + self.placeholder + "d.") % (i + 1)
|
39
|
+
dfn = dfn.join(self.baseFilename.split("."))
|
40
|
+
if os.path.exists(sfn):
|
41
|
+
# print "%s -> %s" % (sfn, dfn)
|
42
|
+
if os.path.exists(dfn):
|
43
|
+
os.remove(dfn)
|
44
|
+
os.rename(sfn, dfn)
|
45
|
+
dfn = (("%0" + self.placeholder + "d.") % 1).join(
|
46
|
+
self.baseFilename.split(".")
|
47
|
+
)
|
48
|
+
if os.path.exists(dfn):
|
49
|
+
os.remove(dfn)
|
50
|
+
# Issue 18940: A file may not have been created if delay is True.
|
51
|
+
if os.path.exists(self.baseFilename):
|
52
|
+
os.rename(self.baseFilename, dfn)
|
53
|
+
if not self.delay:
|
54
|
+
self.stream = self._open()
|
55
|
+
|
56
|
+
def shouldRollover(self, record):
|
57
|
+
|
58
|
+
if self.stream is None: # delay was set...
|
59
|
+
self.stream = self._open()
|
60
|
+
if self.max_bytes > 0: # are we rolling over?
|
61
|
+
msg = "%s\n" % self.format(record)
|
62
|
+
self.stream.seek(0, 2) # due to non-posix-compliant Windows feature
|
63
|
+
if self.stream.tell() + len(msg) >= self.max_bytes:
|
64
|
+
return 1
|
65
|
+
return 0
|
66
|
+
|
67
|
+
|
68
|
+
class BoldFormatter(logging.Formatter):
|
69
|
+
def format(self, record):
|
70
|
+
result = super().format(record)
|
71
|
+
return "\033[1m" + result + "\033[0m"
|
72
|
+
|
73
|
+
|
74
|
+
class Log:
|
75
|
+
def __init__(self, name, path=None, log_level='DEBUG',
|
76
|
+
is_write_to_console=True,
|
77
|
+
is_write_to_file=False,
|
78
|
+
color=True,
|
79
|
+
mode='a',
|
80
|
+
max_bytes=0, backup_count=0, encoding=None):
|
81
|
+
"""
|
82
|
+
:param name: log名
|
83
|
+
:param path: log文件存储路径 如 D://xxx.log
|
84
|
+
:param log_level: log等级 CRITICAL/ERROR/WARNING/INFO/DEBUG
|
85
|
+
:param is_write_to_console: 是否输出到控制台
|
86
|
+
:param is_write_to_file: 是否写入到文件 默认否
|
87
|
+
:param color: 是否有颜色
|
88
|
+
:param mode: 写文件模式
|
89
|
+
:param max_bytes: 每个日志文件的最大字节数
|
90
|
+
:param backup_count: 日志文件保留数量
|
91
|
+
:param encoding: 日志文件编码
|
92
|
+
|
93
|
+
"""
|
94
|
+
# 创建logger对象
|
95
|
+
self.logger = logging.getLogger(name)
|
96
|
+
# 设置日志等级
|
97
|
+
self.logger.setLevel(log_level.upper())
|
98
|
+
|
99
|
+
# 创建日志格式化器
|
100
|
+
# formatter = logging.Formatter('[%(now_datestr)s] [%(levelname)s] [%(func_name)s] - %(message)s') # 原
|
101
|
+
# formatter = logging.Formatter('\033[1m%(now_datestr)s] [%(levelname)s] [%(func_name)s] - %(message)s\033[0m') #加粗
|
102
|
+
formatter = logging.Formatter(
|
103
|
+
'\033[1m[%(now_datestr)s] | %(levelname)-8s | [%(func_name)s] - %(message)s\033[0m') # 加粗对齐
|
104
|
+
|
105
|
+
# formatter = BoldFormatter('[%(now_datestr)s] [%(levelname)s] [%(func_name)s] - %(message)s') # 加粗
|
106
|
+
|
107
|
+
# 判断是否要输出到控制台
|
108
|
+
if is_write_to_console:
|
109
|
+
# 创建控制台处理器
|
110
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
111
|
+
# 设置控制台处理器的格式化器
|
112
|
+
console_handler.setFormatter(formatter)
|
113
|
+
# 将控制台处理器添加到logger中
|
114
|
+
self.logger.addHandler(console_handler)
|
115
|
+
|
116
|
+
# 判断是否要写入文件
|
117
|
+
if is_write_to_file:
|
118
|
+
# 创建文件处理器
|
119
|
+
file_handler = RotatingFileHandler(path, mode=mode, max_bytes=max_bytes,
|
120
|
+
backup_count=backup_count, encoding=encoding)
|
121
|
+
# 设置文件处理器的格式化器
|
122
|
+
file_handler.setFormatter(formatter)
|
123
|
+
# 将文件处理器添加到logger中
|
124
|
+
self.logger.addHandler(file_handler)
|
125
|
+
|
126
|
+
# 判断是否要带颜色
|
127
|
+
if color:
|
128
|
+
try:
|
129
|
+
from colorlog import ColoredFormatter
|
130
|
+
# 创建带颜色的日志格式化器
|
131
|
+
# color_formatter = ColoredFormatter('%(log_color)s[%(now_datestr)s] [%(levelname)s] [%(func_name)s] - %(message)s') # 原
|
132
|
+
# color_formatter = ColoredFormatter('\033[1m%(log_color)s[%(now_datestr)s] [%(levelname)s] [%(func_name)s] - %(message)s\033[0m') # 加粗
|
133
|
+
# 创建颜色映射
|
134
|
+
log_colors = {
|
135
|
+
'DEBUG': 'bold_blue',
|
136
|
+
'INFO': 'bold_cyan',
|
137
|
+
'WARNING': 'bold_yellow',
|
138
|
+
'ERROR': 'bold_red',
|
139
|
+
'CRITICAL': 'bold_red',
|
140
|
+
}
|
141
|
+
color_formatter = ColoredFormatter(
|
142
|
+
'\033[1m%(log_color)s[%(now_datestr)s] | %(levelname)-8s | [%(func_name)s] - %(message)s\033[0m',
|
143
|
+
log_colors=log_colors) # 加粗对齐
|
144
|
+
# 设置控制台处理器的格式化器为带颜色的格式化器
|
145
|
+
console_handler.setFormatter(color_formatter)
|
146
|
+
except ImportError:
|
147
|
+
pass
|
148
|
+
|
149
|
+
def debug(self, message):
|
150
|
+
# 记录DEBUG级别的日志
|
151
|
+
self.logger.debug(message, extra=self._get_caller_name_extra())
|
152
|
+
|
153
|
+
def info(self, message):
|
154
|
+
# 记录INFO级别的日志
|
155
|
+
self.logger.info(message, extra=self._get_caller_name_extra())
|
156
|
+
|
157
|
+
def warning(self, message):
|
158
|
+
# 记录WARNING级别的日志
|
159
|
+
self.logger.warning(message, extra=self._get_caller_name_extra())
|
160
|
+
|
161
|
+
def error(self, message):
|
162
|
+
# 记录ERROR级别的日志
|
163
|
+
self.logger.error(message, extra=self._get_caller_name_extra())
|
164
|
+
|
165
|
+
def critical(self, message):
|
166
|
+
# 记录CRITICAL级别的日志
|
167
|
+
self.logger.critical(message, extra=self._get_caller_name_extra())
|
168
|
+
|
169
|
+
def _get_caller_name_extra(self):
|
170
|
+
"""
|
171
|
+
获取调用日志函数的函数名称
|
172
|
+
"""
|
173
|
+
# 获取当前栈帧
|
174
|
+
frame = inspect.currentframe()
|
175
|
+
# 获取调用者的栈帧
|
176
|
+
caller_frame = frame.f_back.f_back
|
177
|
+
# 从栈帧中获取代码对象
|
178
|
+
code_obj = caller_frame.f_code
|
179
|
+
# 获取调用者的名字
|
180
|
+
caller_name = code_obj.co_name
|
181
|
+
return {"func_name": caller_name,
|
182
|
+
"now_datestr": get_time_timestamp_to_datestr()}
|
183
|
+
|
184
|
+
|
185
|
+
if __name__ == '__main__':
|
186
|
+
pass
|
187
|
+
logger = Log('mylogger', './xxx.log', log_level='DEBUG', is_write_to_console=True, is_write_to_file=True,
|
188
|
+
color=True, mode='a', max_bytes=1024, backup_count=3)
|
189
|
+
logger.debug("debug message")
|
190
|
+
logger.info("info level message")
|
191
|
+
logger.warning("warning level message")
|
192
|
+
logger.critical("critical level message")
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# 说明:
|
5
|
+
# 重试
|
6
|
+
# History:
|
7
|
+
# Date Author Version Modification
|
8
|
+
# --------------------------------------------------------------------------------------------------
|
9
|
+
# 2024/5/12 xiatn V00.01.000 新建
|
10
|
+
# --------------------------------------------------------------------------------------------------
|
11
|
+
import time
|
12
|
+
from xtn_tools_pro.utils.log import Log
|
13
|
+
|
14
|
+
log = Log(name="retry", color=True)
|
15
|
+
|
16
|
+
|
17
|
+
def retry(max_attempts=3, delay=0, exception_callfunc=None, *args_callfunc, **kwargs_callfunc):
|
18
|
+
"""
|
19
|
+
重试
|
20
|
+
:param max_attempts: 最多重试次数
|
21
|
+
:param delay: 每次重试间隔时间
|
22
|
+
:param exception_callfunc: 失败的回调函数
|
23
|
+
:return:
|
24
|
+
"""
|
25
|
+
|
26
|
+
def decorator(func):
|
27
|
+
def wrapper(*args, **kwargs):
|
28
|
+
attempts = 0
|
29
|
+
while attempts < max_attempts:
|
30
|
+
try:
|
31
|
+
return func(*args, **kwargs)
|
32
|
+
except Exception as e:
|
33
|
+
log.debug(f"重试第 {attempts + 1} 次,failed: {e}")
|
34
|
+
if exception_callfunc:
|
35
|
+
exception_callfunc(*args_callfunc, **kwargs_callfunc)
|
36
|
+
attempts += 1
|
37
|
+
time.sleep(delay)
|
38
|
+
|
39
|
+
return wrapper
|
40
|
+
|
41
|
+
return decorator
|
42
|
+
|
43
|
+
|
44
|
+
if __name__ == '__main__':
|
45
|
+
|
46
|
+
def test1(*args, **kwargs):
|
47
|
+
print("test1", args, kwargs)
|
48
|
+
|
49
|
+
|
50
|
+
@retry(3, 5)
|
51
|
+
def test(a, b):
|
52
|
+
import random
|
53
|
+
if random.random() < 0.5:
|
54
|
+
raise ValueError("Random value too small")
|
55
|
+
print("Success!")
|
56
|
+
|
57
|
+
test(1, 1)
|
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# 说明:
|
5
|
+
# sql相关
|
6
|
+
# History:
|
7
|
+
# Date Author Version Modification
|
8
|
+
# --------------------------------------------------------------------------------------------------
|
9
|
+
# 2024/5/12 xiatn V00.01.000 新建
|
10
|
+
# --------------------------------------------------------------------------------------------------
|
11
|
+
import datetime
|
12
|
+
from xtn_tools_pro.utils.helpers import list_to_strtuple, dumps_json
|
13
|
+
|
14
|
+
|
15
|
+
def format_sql_value(value):
|
16
|
+
if isinstance(value, str):
|
17
|
+
value = value.strip()
|
18
|
+
|
19
|
+
elif isinstance(value, (list, dict)):
|
20
|
+
value = dumps_json(value, indent=None)
|
21
|
+
|
22
|
+
elif isinstance(value, (datetime.date, datetime.time)):
|
23
|
+
value = str(value)
|
24
|
+
|
25
|
+
elif isinstance(value, bool):
|
26
|
+
value = int(value)
|
27
|
+
|
28
|
+
return value
|
29
|
+
|
30
|
+
|
31
|
+
def get_insert_sql(table, data, auto_update=False, update_columns=(), insert_ignore=False):
|
32
|
+
"""
|
33
|
+
生成 insert sql
|
34
|
+
:param table: 表
|
35
|
+
:param data: 表数据 json格式
|
36
|
+
:param auto_update: 使用的是replace into, 为完全覆盖已存在的数据
|
37
|
+
:param update_columns: 需要更新的列 默认全部,当指定值时,auto_update设置无效,当duplicate key冲突时更新指定的列
|
38
|
+
:param insert_ignore: 数据存在忽略,True则会忽略该插入操作,不会产生冲突错误
|
39
|
+
:return:
|
40
|
+
"""
|
41
|
+
keys = ["`{}`".format(key) for key in data.keys()]
|
42
|
+
keys = list_to_strtuple(keys).replace("'", "")
|
43
|
+
values = [format_sql_value(value) for value in data.values()]
|
44
|
+
values = list_to_strtuple(values)
|
45
|
+
if update_columns:
|
46
|
+
if not isinstance(update_columns, (tuple, list)):
|
47
|
+
update_columns = [update_columns]
|
48
|
+
update_columns_ = ", ".join(
|
49
|
+
["{key}=values({key})".format(key=key) for key in update_columns]
|
50
|
+
)
|
51
|
+
sql = (
|
52
|
+
"insert%s into `{table}` {keys} values {values} on duplicate key update %s"
|
53
|
+
% (" ignore" if insert_ignore else "", update_columns_)
|
54
|
+
)
|
55
|
+
|
56
|
+
elif auto_update:
|
57
|
+
sql = "replace into `{table}` {keys} values {values}"
|
58
|
+
else:
|
59
|
+
sql = "insert%s into `{table}` {keys} values {values}" % (
|
60
|
+
" ignore" if insert_ignore else ""
|
61
|
+
)
|
62
|
+
|
63
|
+
sql = sql.format(table=table, keys=keys, values=values).replace("None", "null")
|
64
|
+
return sql
|
65
|
+
|
66
|
+
def get_insert_batch_sql(table, datas, auto_update=False, update_columns=(), update_columns_value=()):
|
67
|
+
"""
|
68
|
+
生成 批量 insert sql
|
69
|
+
:param table: 表
|
70
|
+
:param datas: 表数据 [{...}]
|
71
|
+
:param auto_update: 使用的是replace into, 为完全覆盖已存在的数据
|
72
|
+
:param update_columns: 需要更新的列 默认全部,当指定值时,auto_update设置无效,当duplicate key冲突时更新指定的列
|
73
|
+
:param update_columns_value: 需要更新的列的值 默认为datas里边对应的值, 注意 如果值为字符串类型 需要主动加单引号, 如 update_columns_value=("'test'",)
|
74
|
+
:return:
|
75
|
+
"""
|
76
|
+
if not datas:
|
77
|
+
return
|
78
|
+
keys = list(set([key for data in datas for key in data]))
|
79
|
+
values_placeholder = ["%s"] * len(keys)
|
80
|
+
|
81
|
+
values = []
|
82
|
+
for data in datas:
|
83
|
+
value = []
|
84
|
+
for key in keys:
|
85
|
+
current_data = data.get(key)
|
86
|
+
current_data = format_sql_value(current_data)
|
87
|
+
|
88
|
+
value.append(current_data)
|
89
|
+
|
90
|
+
values.append(value)
|
91
|
+
|
92
|
+
keys = ["`{}`".format(key) for key in keys]
|
93
|
+
keys = list_to_strtuple(keys).replace("'", "")
|
94
|
+
|
95
|
+
values_placeholder = list_to_strtuple(values_placeholder).replace("'", "")
|
96
|
+
|
97
|
+
if update_columns:
|
98
|
+
if not isinstance(update_columns, (tuple, list)):
|
99
|
+
update_columns = [update_columns]
|
100
|
+
if update_columns_value:
|
101
|
+
update_columns_ = ", ".join(
|
102
|
+
[
|
103
|
+
"`{key}`={value}".format(key=key, value=value)
|
104
|
+
for key, value in zip(update_columns, update_columns_value)
|
105
|
+
]
|
106
|
+
)
|
107
|
+
else:
|
108
|
+
update_columns_ = ", ".join(
|
109
|
+
["`{key}`=values(`{key}`)".format(key=key) for key in update_columns]
|
110
|
+
)
|
111
|
+
sql = "insert into `{table}` {keys} values {values_placeholder} on duplicate key update {update_columns}".format(
|
112
|
+
table=table,
|
113
|
+
keys=keys,
|
114
|
+
values_placeholder=values_placeholder,
|
115
|
+
update_columns=update_columns_,
|
116
|
+
)
|
117
|
+
elif auto_update:
|
118
|
+
sql = "replace into `{table}` {keys} values {values_placeholder}".format(
|
119
|
+
table=table, keys=keys, values_placeholder=values_placeholder
|
120
|
+
)
|
121
|
+
else:
|
122
|
+
sql = "insert ignore into `{table}` {keys} values {values_placeholder}".format(
|
123
|
+
table=table, keys=keys, values_placeholder=values_placeholder
|
124
|
+
)
|
125
|
+
|
126
|
+
return sql, values
|
127
|
+
|
128
|
+
def get_update_sql(table, data, condition):
|
129
|
+
"""
|
130
|
+
生成更新sql
|
131
|
+
:param table: 表
|
132
|
+
:param data: 表数据 json格式
|
133
|
+
:param condition: where 条件
|
134
|
+
:return:
|
135
|
+
"""
|
136
|
+
key_values = []
|
137
|
+
|
138
|
+
for key, value in data.items():
|
139
|
+
value = format_sql_value(value)
|
140
|
+
if isinstance(value, str):
|
141
|
+
key_values.append("`{}`={}".format(key, repr(value)))
|
142
|
+
elif value is None:
|
143
|
+
key_values.append("`{}`={}".format(key, "null"))
|
144
|
+
else:
|
145
|
+
key_values.append("`{}`={}".format(key, value))
|
146
|
+
|
147
|
+
key_values = ", ".join(key_values)
|
148
|
+
|
149
|
+
sql = "update `{table}` set {key_values} where {condition}"
|
150
|
+
sql = sql.format(table=table, key_values=key_values, condition=condition)
|
151
|
+
return sql
|
152
|
+
|
153
|
+
if __name__ == '__main__':
|
154
|
+
print(get_insert_sql("user_copy1", {"id": 5, "nickname": "1212", "email": "121212", "auth": 2},insert_ignore=True))
|
155
|
+
print(get_insert_batch_sql("user_copy1", [{"id": 5, "nickname": "555", "email": "555", "auth": 1},
|
156
|
+
{"id": 6, "nickname": "666", "email": "666", "auth": 2},
|
157
|
+
{"id": 7, "nickname": "777", "email": "777", "auth": 1}],
|
158
|
+
))
|
159
|
+
print(get_update_sql("user_copy1",{"email":"123","status":4},"id=2"))
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# 说明:
|
5
|
+
# 时间
|
6
|
+
# History:
|
7
|
+
# Date Author Version Modification
|
8
|
+
# --------------------------------------------------------------------------------------------------
|
9
|
+
# 2024/5/13 xiatn V00.01.000 新建
|
10
|
+
# --------------------------------------------------------------------------------------------------
|
11
|
+
import time
|
12
|
+
import datetime
|
13
|
+
|
14
|
+
|
15
|
+
def get_time_now_timestamp(is_time_10=False, is_time_13=False):
|
16
|
+
"""
|
17
|
+
获取当前时间戳
|
18
|
+
:param is_time_10: 是否需要处理为10位的时间戳,默认不处理
|
19
|
+
:param is_time_13: 是否需要处理为13位的时间戳,默认不处理
|
20
|
+
:return:
|
21
|
+
"""
|
22
|
+
|
23
|
+
if is_time_10:
|
24
|
+
val = int(time.time())
|
25
|
+
elif is_time_13:
|
26
|
+
val = int(time.time() * 1000)
|
27
|
+
else:
|
28
|
+
val = time.time()
|
29
|
+
return val
|
30
|
+
|
31
|
+
|
32
|
+
def get_time_now_day0_timestamp(is_time_13=False):
|
33
|
+
"""
|
34
|
+
获取当天0点时间戳
|
35
|
+
:param is_time_13: 是否需要处理为13位的时间戳,默认不处理并且返回10位时间戳
|
36
|
+
:return:
|
37
|
+
"""
|
38
|
+
val = time.mktime(datetime.date.today().timetuple())
|
39
|
+
if is_time_13:
|
40
|
+
return int(val * 1000)
|
41
|
+
else:
|
42
|
+
return int(val)
|
43
|
+
|
44
|
+
|
45
|
+
def get_time_now_day59_timestamp(is_time_13=False):
|
46
|
+
"""
|
47
|
+
获取当天23:59:59点时间戳
|
48
|
+
:param is_time_13: 是否需要处理为13位的时间戳,默认不处理并且返回10位时间戳
|
49
|
+
:return:
|
50
|
+
"""
|
51
|
+
# 获取当前日期时间
|
52
|
+
now = datetime.datetime.now()
|
53
|
+
# 设置小时、分钟、秒为 23:59:59
|
54
|
+
last_second = now.replace(hour=23, minute=59, second=59)
|
55
|
+
# 转换为时间戳
|
56
|
+
timestamp = time.mktime(last_second.timetuple())
|
57
|
+
# 转换为整数类型
|
58
|
+
if is_time_13:
|
59
|
+
return get_time_10_to_13_timestamp(timestamp)
|
60
|
+
else:
|
61
|
+
return int(timestamp)
|
62
|
+
|
63
|
+
|
64
|
+
def get_time_x_day_timestamp(x, is_time_13=False):
|
65
|
+
"""
|
66
|
+
获取x天的0点的时间戳
|
67
|
+
:param x: 0:当天; 1:1天后; -1:一天前
|
68
|
+
:param is_time_13: 是否需要处理为13位的时间戳,默认不处理并且返回10位时间戳
|
69
|
+
:return:
|
70
|
+
"""
|
71
|
+
if x == 0:
|
72
|
+
date_string = datetime.datetime.now().strftime("%Y-%m-%d") # 当天日期
|
73
|
+
elif x > 0:
|
74
|
+
future_date = datetime.datetime.now() + datetime.timedelta(days=x)
|
75
|
+
date_string = future_date.strftime("%Y-%m-%d") # x天后的日期
|
76
|
+
else:
|
77
|
+
past_date = datetime.datetime.now() - datetime.timedelta(days=abs(x))
|
78
|
+
date_string = past_date.strftime("%Y-%m-%d") # x天前的日期
|
79
|
+
|
80
|
+
timestamp = get_time_datestr_to_timestamp(date_string=date_string, is_time_13=is_time_13)
|
81
|
+
return timestamp
|
82
|
+
|
83
|
+
|
84
|
+
def get_time_datestr_to_timestamp(date_string, date_format="%Y-%m-%d", is_time_13=False):
|
85
|
+
"""
|
86
|
+
根据日期格式转换为时间戳,date_string和date_format需要配合,自行传参修改,这里是以%Y-%m-%d为格式也就是2024-04-18
|
87
|
+
:param date_string: 字符串类型的日期格式 例如:2024-04-18
|
88
|
+
:param date_format: 时间格式
|
89
|
+
:param is_time_13: 是否需要处理为13位的时间戳,默认不处理并且返回10位时间戳
|
90
|
+
:return:
|
91
|
+
"""
|
92
|
+
date_obj = datetime.datetime.strptime(date_string, date_format)
|
93
|
+
timestamp = date_obj.timestamp()
|
94
|
+
if is_time_13:
|
95
|
+
return get_time_10_to_13_timestamp(timestamp)
|
96
|
+
else:
|
97
|
+
return int(timestamp)
|
98
|
+
|
99
|
+
|
100
|
+
def get_time_10_to_13_timestamp(timestamp):
|
101
|
+
"""
|
102
|
+
10位时间戳转13位时间戳
|
103
|
+
:param timestamp:
|
104
|
+
:return:
|
105
|
+
"""
|
106
|
+
val = int(timestamp)
|
107
|
+
if len(str(val)) == 10:
|
108
|
+
return int(val * 1000)
|
109
|
+
return val
|
110
|
+
|
111
|
+
|
112
|
+
def get_time_13_to_10_timestamp(timestamp):
|
113
|
+
"""
|
114
|
+
13位时间戳转10位时间戳
|
115
|
+
:param timestamp:
|
116
|
+
:return:
|
117
|
+
"""
|
118
|
+
val = int(timestamp)
|
119
|
+
if len(str(val)) == 13:
|
120
|
+
return int(val // 1000)
|
121
|
+
return val
|
122
|
+
|
123
|
+
|
124
|
+
def get_time_timestamp_to_datestr(format='%Y-%m-%d %H:%M:%S', now_time=0):
|
125
|
+
"""
|
126
|
+
根据时间戳转换为日期格式,兼容10位时间戳和13位时间戳
|
127
|
+
:param format: 日期格式,常用:%Y-%m-%d %H:%M:%S、%Y-%m-%d、%Y/%m/%d、%H:%M:%S ...
|
128
|
+
:param now_time: 时间戳,默认0表示当前时间戳
|
129
|
+
:return:
|
130
|
+
"""
|
131
|
+
# 根据格式获取当前转换好的时间
|
132
|
+
if not now_time:
|
133
|
+
now_time = get_time_now_timestamp()
|
134
|
+
now_time = get_time_13_to_10_timestamp(now_time)
|
135
|
+
val = time.strftime(format, time.localtime(now_time))
|
136
|
+
return val
|
137
|
+
|
138
|
+
|
139
|
+
if __name__ == '__main__':
|
140
|
+
pass
|
141
|
+
print(get_time_timestamp_to_datestr())
|
142
|
+
print(get_time_timestamp_to_datestr(format="%H:%M:%S", now_time=get_time_now_timestamp(is_time_10=True)))
|
143
|
+
print(get_time_timestamp_to_datestr(now_time=get_time_now_timestamp(is_time_13=True)))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: xtn-tools-pro
|
3
|
-
Version: 1.0.0.0.
|
3
|
+
Version: 1.0.0.0.6
|
4
4
|
Summary: xtn 开发工具
|
5
5
|
Author: xtn
|
6
6
|
Author-email: czw011122@163.com
|
@@ -10,6 +10,9 @@ Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
11
11
|
Requires-Dist: pymongo
|
12
12
|
Requires-Dist: redis
|
13
|
+
Requires-Dist: pymysql
|
14
|
+
Requires-Dist: dbutils
|
15
|
+
Requires-Dist: colorlog
|
13
16
|
Requires-Dist: requests
|
14
17
|
|
15
18
|
xtnkk-tools
|