xtn-tools-pro 1.0.0.0.4__py3-none-any.whl → 1.0.0.0.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|