gomyck-tools 1.3.1__py3-none-any.whl → 1.3.2__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.
Files changed (64) hide show
  1. ctools/__init__.py +0 -0
  2. ctools/aes_tools.py +35 -0
  3. ctools/api_result.py +55 -0
  4. ctools/application.py +386 -0
  5. ctools/b64.py +7 -0
  6. ctools/bashPath.py +13 -0
  7. ctools/bottle_web_base.py +169 -0
  8. ctools/bottle_webserver.py +143 -0
  9. ctools/bottle_websocket.py +75 -0
  10. ctools/browser_element_tools.py +314 -0
  11. ctools/call.py +71 -0
  12. ctools/cftp.py +74 -0
  13. ctools/cjson.py +54 -0
  14. ctools/ckafka.py +159 -0
  15. ctools/compile_tools.py +18 -0
  16. ctools/console.py +55 -0
  17. ctools/coord_trans.py +127 -0
  18. ctools/credis.py +111 -0
  19. ctools/cron_lite.py +252 -0
  20. ctools/ctoken.py +34 -0
  21. ctools/cword.py +30 -0
  22. ctools/czip.py +130 -0
  23. ctools/database.py +185 -0
  24. ctools/date_utils.py +43 -0
  25. ctools/dict_wrapper.py +20 -0
  26. ctools/douglas_rarefy.py +136 -0
  27. ctools/download_tools.py +57 -0
  28. ctools/enums.py +4 -0
  29. ctools/ex.py +31 -0
  30. ctools/excelOpt.py +36 -0
  31. ctools/html_soup.py +35 -0
  32. ctools/http_utils.py +24 -0
  33. ctools/images_tools.py +27 -0
  34. ctools/imgDialog.py +44 -0
  35. ctools/metrics.py +131 -0
  36. ctools/mqtt_utils.py +289 -0
  37. ctools/obj.py +20 -0
  38. ctools/pacth.py +74 -0
  39. ctools/plan_area_tools.py +97 -0
  40. ctools/process_pool.py +36 -0
  41. ctools/pty_tools.py +72 -0
  42. ctools/resource_bundle_tools.py +121 -0
  43. ctools/rsa.py +70 -0
  44. ctools/screenshot_tools.py +127 -0
  45. ctools/sign.py +20 -0
  46. ctools/sm_tools.py +49 -0
  47. ctools/snow_id.py +76 -0
  48. ctools/str_diff.py +20 -0
  49. ctools/string_tools.py +85 -0
  50. ctools/sys_info.py +157 -0
  51. ctools/sys_log.py +89 -0
  52. ctools/thread_pool.py +35 -0
  53. ctools/upload_tools.py +40 -0
  54. ctools/win_canvas.py +83 -0
  55. ctools/win_control.py +106 -0
  56. ctools/word_fill.py +562 -0
  57. ctools/word_fill_entity.py +46 -0
  58. ctools/work_path.py +69 -0
  59. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/METADATA +1 -1
  60. gomyck_tools-1.3.2.dist-info/RECORD +62 -0
  61. gomyck_tools-1.3.2.dist-info/top_level.txt +1 -0
  62. gomyck_tools-1.3.1.dist-info/RECORD +0 -4
  63. gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
  64. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,121 @@
1
+ import hashlib
2
+ import io
3
+ import json
4
+ import os
5
+ import shutil
6
+ import zipfile
7
+ from base64 import b64decode
8
+ from base64 import b64encode
9
+
10
+ from Crypto.Hash import MD5
11
+ from Crypto.PublicKey import RSA
12
+ from Crypto.Signature import pkcs1_15
13
+
14
+ from ctools import sys_log, work_path, application
15
+
16
+ log = sys_log.flog
17
+
18
+
19
+ def data_layout(manifest: dict):
20
+ """
21
+ 数据重新排版
22
+ :param manifest:
23
+ :return:
24
+ """
25
+ data = ""
26
+ for key in sorted(manifest):
27
+ if key != "signature":
28
+ data += "%s=%s," % (key, manifest[key])
29
+ return data[:-1]
30
+
31
+
32
+ def get_md5_sum(data):
33
+ return hashlib.md5(data + "gomyck-daning".encode()).hexdigest()
34
+
35
+
36
+ def signature(manifest: dict):
37
+ """
38
+ 定义签名函数,能够使用指定的私钥对数据文件进行签名,并将签名结果输出到文件返回
39
+ :param manifest: 元数据字典
40
+ :return:
41
+ """
42
+ with open(work_path.get_app_path() + '/keys/resource_private_key.pem', 'r') as pri:
43
+ private_key = RSA.import_key(pri.read())
44
+ digest = MD5.new(data_layout(manifest).encode('utf-8'))
45
+ return b64encode(pkcs1_15.new(private_key).sign(digest)).decode()
46
+
47
+
48
+ def sign_verify(manifest: dict, sign_val: str):
49
+ """
50
+ 定义签名验证函数,能够使用指定的公钥对任务2中的签名文件进行验证,返回验证结果
51
+ :param manifest: 元数据字典
52
+ :param sign_val: 对比的签名值
53
+ :return:
54
+ """
55
+ with open(work_path.get_app_path() + '/keys/resource_public_key.pem', 'r') as pub:
56
+ public_key = RSA.import_key(pub.read())
57
+ digest = MD5.new(data_layout(manifest).encode('utf-8'))
58
+ try:
59
+ pkcs1_15.new(public_key).verify(digest, b64decode(sign_val))
60
+ return True
61
+ except:
62
+ return False
63
+
64
+
65
+ def unpack_resource(resource_name, file):
66
+ """
67
+ 解压资源包并验证包的完整性和签名
68
+ :param resource_name:
69
+ :param file:
70
+ :return:
71
+ """
72
+ message = None
73
+ file_md5_sum = os.path.splitext(resource_name)[0].split('-')[-1]
74
+ if file_md5_sum != get_md5_sum(file.read()):
75
+ message = "资源包已损坏, 请重新打包"
76
+ else:
77
+ manifest = {}
78
+ with zipfile.ZipFile(file, 'r') as zf:
79
+ for file in zf.filelist:
80
+ if file.filename == "manifest.json":
81
+ manifest = json.loads(zf.read(file.filename))
82
+ break
83
+ # 校验签名
84
+ if sign_verify(manifest, manifest.get('signature')):
85
+ resource_path = os.path.join(application.Upload.source_pkg_path, manifest['name'])
86
+ shutil.rmtree(resource_path, ignore_errors=True)
87
+ zf.extractall(resource_path)
88
+ else:
89
+ message = "资源包签名校验未通过"
90
+ return message
91
+
92
+
93
+ def pack_resource(resource_path):
94
+ """
95
+ 压缩打包资源包为zip文件
96
+ :param resource_path:
97
+ :return:
98
+ """
99
+ file_name = None
100
+ buffer = io.BytesIO()
101
+ manifest_path = os.path.join(resource_path, 'manifest.json')
102
+ if os.path.exists(manifest_path):
103
+ resource_name = os.path.split(resource_path)[-1]
104
+ with open(manifest_path, mode="r", encoding='utf-8') as f:
105
+ manifest = json.loads(f.read())
106
+ manifest["signature"] = signature(manifest)
107
+ with open(manifest_path, mode="w", encoding='utf-8') as f:
108
+ f.write(json.dumps(manifest, indent=2, ensure_ascii=False))
109
+
110
+ with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED, allowZip64=False) as zf:
111
+ for dir_path, dir_names, file_names in os.walk(resource_path):
112
+ for filename in file_names:
113
+ filepath = os.path.join(dir_path, filename)
114
+ arcname = os.path.join(dir_path.split(resource_name)[-1], filename)
115
+ zf.write(filepath, arcname=arcname)
116
+ buffer.seek(0)
117
+ md5_sum = get_md5_sum(buffer.getvalue())
118
+ file_name = "%s-%s.zip" % (resource_name, md5_sum)
119
+ else:
120
+ log.info("资源包%s不存在manifest.json元数据文件, 无法打包" % resource_path)
121
+ return file_name, buffer
ctools/rsa.py ADDED
@@ -0,0 +1,70 @@
1
+ import base64
2
+
3
+ from Crypto.Cipher import PKCS1_OAEP
4
+ from Crypto.PublicKey import RSA
5
+ from Crypto.Signature import pkcs1_15
6
+ from Crypto.Hash import SHA256
7
+
8
+ from ctools import work_path, cjson
9
+
10
+ ENCRYPT_CHUNK_SIZE = 245
11
+ decrypt_CHUNK_SIZE = 512
12
+
13
+ def generate_rsa_keypair(bits=2048):
14
+ key = RSA.generate(bits)
15
+ private_key = key.export_key() # 导出私钥
16
+ public_key = key.publickey().export_key() # 导出公钥
17
+ with open("private_key.pem", "wb") as f:
18
+ f.write(private_key)
19
+ with open("public_key.pem", "wb") as f:
20
+ f.write(public_key)
21
+
22
+ def loadLicenseInfo(auth_code):
23
+ with open(work_path.get_app_path() + '/keys/license.key', 'r') as pri:
24
+ decrypt_message = decrypt(auth_code.strip(), pri.read())
25
+ return cjson.loads(decrypt_message)
26
+
27
+ # 加密函数
28
+ def encrypt(msg, public_key):
29
+ parts = b''
30
+ public_key = RSA.import_key(public_key)
31
+ cipher = PKCS1_OAEP.new(public_key)
32
+ for i in range(0, len(msg), ENCRYPT_CHUNK_SIZE):
33
+ parts += cipher.encrypt(msg[i:i + ENCRYPT_CHUNK_SIZE].encode())
34
+ encrypted_base64 = base64.b64encode(parts)
35
+ return encrypted_base64.decode()
36
+
37
+ # 解密函数
38
+ def decrypt(msg, private_key):
39
+ parts = b''
40
+ public_key = RSA.import_key(private_key)
41
+ cipher = PKCS1_OAEP.new(public_key)
42
+ encrypted_bytes = base64.b64decode(msg)
43
+ for i in range(0, len(encrypted_bytes), decrypt_CHUNK_SIZE):
44
+ parts += cipher.decrypt(encrypted_bytes[i:i + decrypt_CHUNK_SIZE])
45
+ return parts.decode()
46
+
47
+ # 验签
48
+ def verify_sign(msg, public_key, sign):
49
+ public_key = RSA.import_key(public_key)
50
+ hash_message = SHA256.new(msg.encode())
51
+ try:
52
+ pkcs1_15.new(public_key).verify(hash_message, base64.b64decode(sign.encode()))
53
+ return True
54
+ except Exception as e:
55
+ print('签名验证失败: {}'.format(e))
56
+ return False
57
+
58
+ # 签名
59
+ def sign_msg(msg, private_key):
60
+ private_key = RSA.import_key(private_key)
61
+ hash_message = SHA256.new(msg.encode())
62
+ signature = pkcs1_15.new(private_key).sign(hash_message)
63
+ return base64.b64encode(signature).decode()
64
+
65
+
66
+ # with open(work_path.get_current_path() + '/private_key.pem', 'r') as key:
67
+ # key = key.read()
68
+ # sign = sign_msg(key, key)
69
+ # with open(work_path.get_current_path() + '/public_key.pem', 'r') as pub:
70
+ # print(verify_sign(key, pub.read(), sign+'123'))
@@ -0,0 +1,127 @@
1
+ import os
2
+ import time
3
+ import tkinter as tk
4
+
5
+ import pyautogui
6
+
7
+ from ctools import application, string_tools
8
+
9
+ """
10
+ 截屏工具类, 按回车截图
11
+ image_path=screenshot_tools.screenshot()
12
+ """
13
+
14
+
15
+ class ScreenshotTools:
16
+ def __init__(self):
17
+ self.root = tk.Tk()
18
+ self.root.overrideredirect(True)
19
+ self.root.attributes("-alpha", 0.1)
20
+ self.root.attributes('-topmost', 'true')
21
+ self.root.geometry("{0}x{1}+0+0".format(self.root.winfo_screenwidth(), self.root.winfo_screenheight()))
22
+ self.root.configure(bg="black")
23
+
24
+ self.canvas = tk.Canvas(self.root)
25
+ self.canvas.bind("<Button-3>", self.change_center_point)
26
+
27
+ self.x, self.y = 0, 0
28
+ self.xstart, self.ystart = 0, 0
29
+ self.xend, self.yend = 0, 0
30
+ self.xcenter, self.ycenter = 0, 0
31
+
32
+ self.select_area = self.canvas.create_rectangle(0, 0, 0, 0, outline='red', width=0, dash=(4, 4))
33
+ self.screenshot_path = None
34
+ self.center_offset = (0, 0)
35
+
36
+ self.pos_div = tk.Toplevel(self.root)
37
+ self.pos_div.overrideredirect(True)
38
+ self.pos_div.attributes('-topmost', 'true')
39
+ self.pos_div.configure(bg="grey")
40
+ self.pos_div.geometry("360x25")
41
+
42
+ self.pos_div_text = "POS: (%s, %s), Enter键-截图, Esc键-退出, 右键设置点击位置"
43
+ self.pos_label = tk.Label(self.pos_div, text=self.pos_div_text % (0, 0), bg="black", fg="white")
44
+ self.pos_label.pack(fill="both", expand=True)
45
+
46
+ self.center_div = None
47
+
48
+ self.root.bind('<Escape>', self.sys_out) # 键盘Esc键->退出
49
+ self.root.bind('<Motion>', self.print_pos)
50
+ self.root.bind("<Button-1>", self.mouse_left_down) # 鼠标左键点击->显示子窗口
51
+ self.root.bind("<B1-Motion>", self.mouse_left_move) # 鼠标左键移动->改变子窗口大小
52
+ self.root.bind("<ButtonRelease-1>", self.mouse_left_up) # 鼠标左键释放->记录最后光标的位置
53
+ self.root.bind("<Return>", self.save_image) # 回车键->截屏并保存图片
54
+ self.root.focus()
55
+
56
+ def start(self):
57
+ self.root.mainloop()
58
+
59
+ def print_pos(self, event):
60
+ self.x, self.y = event.widget.winfo_pointerxy()
61
+ self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
62
+ self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
63
+
64
+ def change_center_point(self, event):
65
+ offset_x = event.x + self.xstart - self.xcenter - 2
66
+ offset_y = event.y + self.ystart - self.ycenter - 2
67
+ self.center_div.geometry(f"+{self.xstart+event.x-2}+{self.ystart+event.y-2}")
68
+ self.center_offset = (offset_x, offset_y)
69
+
70
+ def mouse_left_down(self, event):
71
+ if self.center_div is None:
72
+ self.center_div = tk.Toplevel(self.root)
73
+ self.center_div.overrideredirect(True)
74
+ self.center_div.attributes('-topmost', 'true')
75
+ self.center_div.configure(bg="red")
76
+ self.center_div.geometry("4x4")
77
+
78
+ self.x, self.y = event.x, event.y
79
+ self.xstart, self.ystart = event.x, event.y
80
+ self.canvas.configure(height=1)
81
+ self.canvas.configure(width=1)
82
+ self.canvas.config(highlightthickness=0) # 无边框
83
+ self.canvas.place(x=event.x, y=event.y)
84
+ self.center_div.geometry(f"+{self.xstart}+{self.ystart}")
85
+
86
+ def mouse_left_move(self, event):
87
+ self.x, self.y = event.x, event.y
88
+ self.xcenter = self.xstart + int((self.x-self.xstart)/2)-2
89
+ self.ycenter = self.ystart + int((self.y-self.ystart)/2)-2
90
+
91
+ self.pos_label.config(text=self.pos_div_text % (self.x, self.y))
92
+ self.pos_div.geometry(f"+{self.x + 10}+{self.y + 10}")
93
+ self.center_div.geometry(f"+{self.xcenter}+{self.ycenter}")
94
+
95
+ self.canvas.configure(height=event.y - self.ystart)
96
+ self.canvas.configure(width=event.x - self.xstart)
97
+ self.canvas.coords(self.select_area, 0, 0, event.x - self.xstart, event.y - self.ystart)
98
+
99
+ def mouse_left_up(self, event):
100
+ self.xend, self.yend = event.x, event.y
101
+
102
+ def save_image(self, event):
103
+ try:
104
+ self.canvas.delete(self.select_area)
105
+ self.canvas.place_forget()
106
+ self.sys_out()
107
+
108
+ time.sleep(0.3)
109
+ img = pyautogui.screenshot(region=[self.xstart, self.ystart, self.xend - self.xstart, self.yend - self.ystart])
110
+ self.screenshot_path = os.path.join(application.Server.screenshotPath,
111
+ "screenshot-%s.png" % string_tools.get_snowflake_id())
112
+ img.save(self.screenshot_path)
113
+ except Exception:
114
+ pass
115
+
116
+ def sys_out(self, event=None):
117
+ if self.center_div:
118
+ self.center_div.destroy()
119
+ self.pos_div.destroy()
120
+ self.root.destroy()
121
+
122
+
123
+ def screenshot():
124
+ st = ScreenshotTools()
125
+ st.start()
126
+ return st.screenshot_path, st.center_offset
127
+
ctools/sign.py ADDED
@@ -0,0 +1,20 @@
1
+ import hashlib
2
+ import hmac
3
+
4
+ global_key = 'gomyck2014'
5
+
6
+
7
+ def generate_signature(file_path, key: str = global_key):
8
+ try:
9
+ with open(file_path, 'rb') as f:
10
+ file_contents = f.read()
11
+ file_hash = hashlib.sha256(file_contents).digest()
12
+ sign_val = hmac.new(key.encode(), file_hash, hashlib.sha256).digest()
13
+ return sign_val.hex()
14
+ except:
15
+ return ''
16
+
17
+ def digest(value: str, key: str = global_key):
18
+ val_hash = hashlib.sha256(value.encode()).digest()
19
+ sign_val = hmac.new(key.encode(), val_hash, hashlib.sha256).digest()
20
+ return sign_val.hex()
ctools/sm_tools.py ADDED
@@ -0,0 +1,49 @@
1
+ import base64
2
+
3
+ from gmssl import sm2
4
+ from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
5
+
6
+ sm2_crypt: sm2.CryptSM2 = None
7
+
8
+ def init(private_key: str, public_key: str):
9
+ global sm2_crypt
10
+ if sm2_crypt is not None:
11
+ print('sm2 is already init!!!')
12
+ return
13
+ sm2_crypt = sm2.CryptSM2(private_key=private_key, public_key=public_key, asn1=True, mode=1)
14
+
15
+ def sign_with_sm2(sign_data: str) -> str:
16
+ if sm2_crypt is None: raise Exception('sm2 is not init!!!')
17
+ return sm2_crypt.sign_with_sm3(sign_data.encode('UTF-8'))
18
+
19
+
20
+ def verify_with_sm2(sign_val: str, sign_data: str) -> bool:
21
+ if sm2_crypt is None: raise Exception('sm2 is not init!!!')
22
+ try:
23
+ return sm2_crypt.verify_with_sm3(sign_val, sign_data.encode('UTF-8'))
24
+ except Exception as e:
25
+ print('签名验证失败: {}'.format(e))
26
+ return False
27
+
28
+ def encrypt_with_sm2(encrypt_data: str) -> str:
29
+ if sm2_crypt is None: raise Exception('sm2 is not init!!!')
30
+ return base64.b64encode(sm2_crypt.encrypt(encrypt_data.encode('UTF-8'))).decode('UTF-8')
31
+
32
+
33
+ def decrypt_with_sm2(encrypt_data: str) -> str:
34
+ if sm2_crypt is None: raise Exception('sm2 is not init!!!')
35
+ return sm2_crypt.decrypt(base64.b64decode(encrypt_data.encode('UTF-8'))).decode('UTF-8')
36
+
37
+
38
+ def encrypt_with_sm4(key: bytes, encrypt_text: str):
39
+ crypt_sm4 = CryptSM4()
40
+ crypt_sm4.set_key(key, SM4_ENCRYPT)
41
+ encrypt_value = base64.b64encode(crypt_sm4.crypt_ecb(encrypt_text.encode()))
42
+ return encrypt_value.decode()
43
+
44
+
45
+ def decrypt_with_sm4(key: bytes, decrypt_text: str):
46
+ crypt_sm4 = CryptSM4()
47
+ crypt_sm4.set_key(key, SM4_DECRYPT)
48
+ decrypt_value = crypt_sm4.crypt_ecb(base64.b64decode(decrypt_text))
49
+ return decrypt_value.decode()
ctools/snow_id.py ADDED
@@ -0,0 +1,76 @@
1
+ import time
2
+
3
+ # 64位ID的划分
4
+ WORKER_ID_BITS = 5
5
+ DATACENTER_ID_BITS = 5
6
+ SEQUENCE_BITS = 12
7
+ # 最大取值计算
8
+ MAX_WORKER_ID = -1 ^ (-1 << WORKER_ID_BITS) # 2**5-1 0b11111
9
+ MAX_DATACENTER_ID = -1 ^ (-1 << DATACENTER_ID_BITS)
10
+ # 移位偏移计算
11
+ WOKER_ID_SHIFT = SEQUENCE_BITS
12
+ DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS
13
+ TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS
14
+ # 序号循环掩码
15
+ SEQUENCE_MASK = -1 ^ (-1 << SEQUENCE_BITS)
16
+ # Twitter元年时间戳
17
+ TWEPOCH = 1288834974657
18
+
19
+ class SnowId(object):
20
+ """
21
+ 用于生成IDs
22
+ """
23
+ def __init__(self, datacenter_id=0, worker_id=0, sequence=0):
24
+ """
25
+ 初始化
26
+ :param datacenter_id: 数据中心(机器区域)ID
27
+ :param worker_id: 机器ID
28
+ :param sequence: 起始序号
29
+ """
30
+ # sanity check
31
+ if worker_id > MAX_WORKER_ID or worker_id < 0:
32
+ raise ValueError('worker_id值越界')
33
+ if datacenter_id > MAX_DATACENTER_ID or datacenter_id < 0:
34
+ raise ValueError('datacenter_id值越界')
35
+ self.worker_id = worker_id
36
+ self.datacenter_id = datacenter_id
37
+ self.sequence = sequence
38
+ self.last_timestamp = -1 # 上次计算的时间戳
39
+
40
+ @staticmethod
41
+ def _gen_timestamp():
42
+ """
43
+ 生成整数时间戳
44
+ :return:int timestamp
45
+ """
46
+ return int(time.time() * 1000)
47
+
48
+ def get_id(self):
49
+ """
50
+ 获取新ID
51
+ :return:
52
+ """
53
+ timestamp = self._gen_timestamp()
54
+ # 时钟回拨
55
+ if timestamp < self.last_timestamp:
56
+ print('clock is moving backwards. Rejecting requests until {}'.format(self.last_timestamp))
57
+ raise
58
+ if timestamp == self.last_timestamp:
59
+ self.sequence = (self.sequence + 1) & SEQUENCE_MASK
60
+ if self.sequence == 0:
61
+ timestamp = self._til_next_millis(self.last_timestamp)
62
+ else:
63
+ self.sequence = 0
64
+ self.last_timestamp = timestamp
65
+ new_id = ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (self.datacenter_id << DATACENTER_ID_SHIFT) | \
66
+ (self.worker_id << WOKER_ID_SHIFT) | self.sequence
67
+ return new_id
68
+
69
+ def _til_next_millis(self, last_timestamp):
70
+ """
71
+ 等到下一毫秒
72
+ """
73
+ timestamp = self._gen_timestamp()
74
+ while timestamp <= last_timestamp:
75
+ timestamp = self._gen_timestamp()
76
+ return timestamp
ctools/str_diff.py ADDED
@@ -0,0 +1,20 @@
1
+ # import difflib
2
+ #
3
+ # s1 = '12-23'
4
+ # s2 = '00-11'
5
+ # s3 = '2023-05-05 12:00:00-2023-05-06 23:00:00 剩余4'
6
+ #
7
+ # sm = difflib.SequenceMatcher(None, s1, s3)
8
+ #
9
+ # print(sm.ratio()) # 输出相似度得分
10
+
11
+
12
+ from fuzzywuzzy import fuzz
13
+
14
+ s1 = '12-23'
15
+ s2 = '00-11'
16
+ s3 = '2023-05-05 12:00:00-2023-05-06 23:00:00 剩余4'
17
+ s4 = '2023-04-28 00:00:00-2023-04-28 11:00:00 剩余12'
18
+ score = fuzz.ratio(s2, s4)
19
+
20
+ print(score) # 输出相似度得
ctools/string_tools.py ADDED
@@ -0,0 +1,85 @@
1
+ from ctools.snow_id import SnowId
2
+
3
+ idWorker = SnowId(1, 2, 0)
4
+
5
+ def get_random_str(size: int = 10) -> str:
6
+ import random
7
+ return "".join(random.sample('abcdefghjklmnpqrstuvwxyz123456789', size))
8
+
9
+ def get_uuid() -> str:
10
+ import uuid
11
+ return str(uuid.uuid1()).replace("-", "")
12
+
13
+ def get_snowflake_id():
14
+ return idWorker.get_id()
15
+
16
+ def decode_bytes(bytes_str):
17
+ import chardet
18
+ res_str = ""
19
+ if bytes_str:
20
+ detect = chardet.detect(bytes_str)
21
+ if detect:
22
+ confidence = 0
23
+ chardet_error = False
24
+ try:
25
+ confidence = float(detect.get('confidence'))
26
+ res_str = bytes_str.decode(encoding=detect.get('encoding'))
27
+ except Exception:
28
+ chardet_error = True
29
+
30
+ try:
31
+ if confidence <= 0.95 or chardet_error:
32
+ encoding = "utf-8" if detect.get('encoding') == "utf-8" else "gbk"
33
+ res_str = bytes_str.decode(encoding=encoding)
34
+ except Exception:
35
+ res_str = str(bytes_str)
36
+ return res_str
37
+
38
+
39
+ def check_sum(content: str):
40
+ import hashlib
41
+ try:
42
+ algorithm = hashlib.sha256()
43
+ algorithm.update(content.encode())
44
+ return algorithm.hexdigest()
45
+ except Exception:
46
+ return None
47
+
48
+
49
+ def is_list(v: str):
50
+ try:
51
+ list(v)
52
+ if v[0] == "[" and v[-1] == "]":
53
+ return True
54
+ else:
55
+ return False
56
+ except Exception:
57
+ return False
58
+
59
+ def is_digit(v: str):
60
+ try:
61
+ float(v)
62
+ return True
63
+ except Exception:
64
+ return False
65
+
66
+ def is_bool(v: str):
67
+ if v in ["False", "True"]:
68
+ return True
69
+ else:
70
+ return False
71
+
72
+ def dict_to_params(obj: dict):
73
+ params = ""
74
+ for k, v in obj.items():
75
+ if k == 'varname':
76
+ continue
77
+ v = str(v)
78
+ if not is_list(v) and not is_digit(v) and not is_bool(v):
79
+ if k == "path" and v[:4] != "http":
80
+ v = "r'%s'" % v
81
+ else:
82
+ v = "'%s'" % v
83
+ params += "%s=%s, " % (k, v)
84
+ params = params[:params.rfind(',')]
85
+ return params