jit-utils-backend 0.0.1__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.
jit_utils/matchTool.py ADDED
@@ -0,0 +1,136 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/7/12
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+ import copy
10
+ from abc import ABCMeta, abstractmethod
11
+
12
+
13
+ class BaseMatchRole(metaclass=ABCMeta):
14
+ successMsg = ""
15
+
16
+ @abstractmethod
17
+ def hash(self, data) -> int:
18
+ """
19
+ 将 Dict 数据按照自定义的方式进行hash,返回一个hash int
20
+ 如果这里返回的是None,则被认为不合格数据,将不进行匹配
21
+ """
22
+ pass
23
+
24
+
25
+ class MatchResultNumEnum:
26
+ success = 0
27
+ oldOnly = -1
28
+ newOnly = 1
29
+
30
+
31
+ # 这个的 作用是 给MatchTool.match返回值 构建返回 数据
32
+ class MatchResult(object):
33
+ DEFAULT_SUCCESS_RESULT_STRING = "匹配成功"
34
+ DEFAULT_OLD_ONLY_RESULT_STRING = "只有旧数据匹配"
35
+ DEFAULT_NEW_ONLY_RESULT_STRING = "只有新数据匹配"
36
+
37
+ def __init__(self, oldData=None, newData=None, resultStr=None):
38
+ self.oldData = oldData
39
+ self.newData = newData
40
+ self.resultStr = resultStr
41
+
42
+ @property
43
+ def resultNum(self):
44
+ if self.oldData and self.newData:
45
+ return MatchResultNumEnum.success
46
+ if self.oldData:
47
+ return MatchResultNumEnum.oldOnly
48
+ if self.newData:
49
+ return MatchResultNumEnum.newOnly
50
+
51
+ @property
52
+ def matchResultStr(self):
53
+ if self.resultStr:
54
+ return self.resultStr
55
+ if self.resultNum == MatchResultNumEnum.success:
56
+ return self.DEFAULT_SUCCESS_RESULT_STRING
57
+ if self.resultNum == MatchResultNumEnum.oldOnly:
58
+ return self.DEFAULT_OLD_ONLY_RESULT_STRING
59
+ if self.resultNum == MatchResultNumEnum.newOnly:
60
+ return self.DEFAULT_NEW_ONLY_RESULT_STRING
61
+ return "未知错误"
62
+
63
+ def toDict(self):
64
+ return {
65
+ "oldData": self.oldData,
66
+ "newData": self.newData,
67
+ "resultStr": self.resultStr,
68
+ "matchResultStr": self.matchResultStr,
69
+ }
70
+
71
+ def __repr__(self):
72
+ return "<{} {}>".format(self.__class__.__name__, self.matchResultStr)
73
+
74
+
75
+ class MatchHashData(object):
76
+ def __init__(self, data, hashList=None):
77
+ self.data = data
78
+ self.hashList = hashList
79
+
80
+
81
+ class MatchTool(object):
82
+ """
83
+ 匹配工具,自定义匹配规则,只要有一个匹配成功,则算匹配成功。
84
+ """
85
+
86
+ matchRoles = []
87
+ matchResultCls = MatchResult
88
+
89
+ @classmethod
90
+ def match(cls, oldList: list, newList: list):
91
+ # 避免污染数据源
92
+ oldList = copy.deepcopy(oldList)
93
+ newList = copy.deepcopy(newList)
94
+
95
+ result = []
96
+ # 循环是判断匹配规则,可能会thirdDeptId 等多个匹配规则 或判断
97
+ for matchRole in cls.matchRoles:
98
+ oldHashMap = cls.getHashMap(oldList, matchRole())
99
+ newHashMap = cls.getHashMap(newList, matchRole())
100
+ hashSet = set(oldHashMap.keys()) | set(newHashMap.keys())
101
+ for h in hashSet:
102
+ oldData = oldHashMap.get(h)
103
+ newData = newHashMap.get(h)
104
+ if oldData and newData:
105
+ # 匹配成功,只要匹配成功,这个数据就不用进行下个匹配规则
106
+ oldHashMap.pop(h)
107
+ newHashMap.pop(h)
108
+ result.append(cls.matchResultCls(oldData, newData, matchRole.successMsg))
109
+
110
+ # 通过一次匹配规则后,将已经匹配的数据对从列表中删除
111
+ oldList = list(oldHashMap.values())
112
+ newList = list(newHashMap.values())
113
+
114
+ # 经过所有匹配规则中,将未匹配到的数据也加到匹配结果中
115
+ for oldData in oldList:
116
+ # 只有老数据,没有新数据匹配
117
+ result.append(cls.matchResultCls(oldData=oldData))
118
+
119
+ for newData in newList:
120
+ # 只有新数据,没有老数据匹配
121
+ result.append(cls.matchResultCls(newData=newData))
122
+
123
+ return result
124
+
125
+ @classmethod
126
+ def getHashMap(cls, dataList, matchRole: BaseMatchRole) -> dict:
127
+ """
128
+ 将多个数据,按照hash规则转换成map
129
+ """
130
+ hashMap = {}
131
+ for data in dataList:
132
+ h = matchRole.hash(data)
133
+ if h is None:
134
+ h = hash(str(data))
135
+ hashMap.setdefault(h, data)
136
+ return hashMap
jit_utils/network.py ADDED
@@ -0,0 +1,36 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2024/10/31 11:08
4
+
5
+ @author: 'wuhao'
6
+
7
+ @desc:
8
+ """
9
+ import socket
10
+
11
+
12
+ def checkConnect(host, port, timeout=0.3):
13
+ """
14
+ 检查ip和端口是否合法
15
+ :param ip:
16
+ :param port:
17
+ :return:
18
+ """
19
+ try:
20
+ port = int(port)
21
+ except ValueError:
22
+ return False
23
+ if not (0 <= port <= 65535):
24
+ return False
25
+ if host.startswith("127") and host != "127.0.0.1":
26
+ return False
27
+ # 检查ip port 是否开启
28
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
29
+ s.settimeout(timeout)
30
+ try:
31
+ s.connect((host, port))
32
+ return True
33
+ except: # noqa E722
34
+ return False
35
+ finally:
36
+ s.close()
jit_utils/qrcode.py ADDED
@@ -0,0 +1,60 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2025/01/21
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 二维码工具,暂时用于文件渲染
8
+ """
9
+ import base64
10
+ from io import BytesIO
11
+
12
+ import qrcode
13
+
14
+
15
+ class Qrcode(object):
16
+ """
17
+ 二维码模块,暂用于文件渲染
18
+ """
19
+
20
+ DEFAULT_ROW = 200
21
+ DEFAULT_COL = 200
22
+ DEFAULT_BOX_SIZE = 10
23
+ DEFAULT_BORDER = 4
24
+
25
+ def __init__(self, value: str, row=None, col=None):
26
+ self.value = value
27
+ # self.boxSize = boxSize or self.DEFAULT_BOX_SIZE
28
+ # self.border = border or self.DEFAULT_BORDER
29
+ self.row = row or self.DEFAULT_ROW
30
+ self.col = col or self.DEFAULT_COL
31
+
32
+ def toByte(self):
33
+ file = self.toFile()
34
+ data = file.read()
35
+ return data
36
+
37
+ def toFile(self):
38
+ qr = qrcode.QRCode(
39
+ version=1,
40
+ error_correction=qrcode.constants.ERROR_CORRECT_L,
41
+ box_size=self.DEFAULT_BOX_SIZE,
42
+ border=self.DEFAULT_BORDER,
43
+ )
44
+ # 添加数据
45
+ qr.add_data(self.value)
46
+ qr.make(fit=True)
47
+ img = qr.make_image(fill_color="black", back_color="white")
48
+ img._img = img.get_image().resize((self.row, self.col))
49
+ imgBytes = BytesIO()
50
+ img.save(imgBytes)
51
+ imgBytes.seek(0)
52
+ return imgBytes
53
+
54
+ def toStr(self):
55
+ if not self.value:
56
+ return ""
57
+ return "<image:{}>".format(base64.b64encode(self.toByte()).decode("utf-8"))
58
+
59
+ def __str__(self):
60
+ return self.toStr()
jit_utils/signature.py ADDED
@@ -0,0 +1,56 @@
1
+ """
2
+ Created on 2024/8/14 14:24
3
+
4
+ @author: 'wuhao'
5
+
6
+ @desc:
7
+ """
8
+
9
+ import base64
10
+ import hashlib
11
+ import json
12
+ from decimal import Decimal
13
+
14
+
15
+ def formatNumber(obj):
16
+ if isinstance(obj, (float, int)) and not isinstance(obj, bool):
17
+ # 保证不使用科学计数法,保留 10 位精度
18
+ obj = Decimal(str(obj))
19
+ return format(obj, ".32f").rstrip("0").rstrip(".")
20
+ elif isinstance(obj, dict):
21
+ return {k: formatNumber(v) for k, v in obj.items()}
22
+ elif isinstance(obj, list):
23
+ return [formatNumber(i) for i in obj]
24
+ else:
25
+ return obj
26
+
27
+
28
+ def uniqueParams(paramDict, signKey="sign"):
29
+ parmList = []
30
+ for key in sorted(paramDict.keys(), reverse=True):
31
+ if key == signKey:
32
+ continue
33
+ parmList.append("{key}{val}".format(key=key, val=paramDict[key]))
34
+ return "".join(parmList), len(parmList)
35
+
36
+
37
+ def generateSignature(string, interval, divisor=1.4):
38
+ signString = hashlib.sha1(base64.b64encode(bytes(string, encoding="utf-8"))).hexdigest()
39
+ signStringLen = len(signString)
40
+ sampleCount = int(interval * divisor)
41
+ if sampleCount >= signStringLen:
42
+ return signString
43
+ cycle = int(signStringLen / sampleCount)
44
+ rr = "".join(signString[int(i * cycle)] for i in range(sampleCount))
45
+ return rr
46
+
47
+
48
+ def getSign(paramDict):
49
+ paramDict = formatNumber(paramDict)
50
+ for k, v in paramDict.items():
51
+ if isinstance(v, (list, dict)):
52
+ paramDict[k] = json.dumps(v, ensure_ascii=False, separators=(",", ":"))
53
+ else:
54
+ paramDict[k] = v
55
+ uString, lenValue = uniqueParams(paramDict)
56
+ return generateSignature(uString, lenValue)
@@ -0,0 +1,44 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2024/04/28
4
+
5
+ @author: 臧韬
6
+
7
+ @desc: 默认描述
8
+ """
9
+ import requests
10
+
11
+ from .signature import getSign
12
+ from .time import getTimestamp
13
+
14
+
15
+ class SpaceSender:
16
+ @classmethod
17
+ def sendApi(cls, entry, appId, element: str, func: str, argDict: dict, headers: dict = None):
18
+ """
19
+ myapp之间接口转发
20
+ :param entry: 请求入口
21
+ :param appId: 发送给哪个app
22
+ :param element: 元素的fullName(通常是svc)
23
+ :param func: 具体调用的函数
24
+ :param argDict: 具体请求数据
25
+ :param headers: 具体请求头
26
+ :return:
27
+ """
28
+ path = "/api/{appId}/{element}/{func}".format(
29
+ appId=appId.replace(".", "/"), element=element.replace(".", "/"), func=func
30
+ )
31
+ headers = headers or {}
32
+ url = "{entry}{path}".format(entry=entry, path=path)
33
+ token = headers.get("token", "")
34
+ timestamp = str(getTimestamp())
35
+ paramDict = {"path": path, "token": token, "timestamp": timestamp, **argDict}
36
+ sign = getSign(paramDict)
37
+ headers["sign"] = sign
38
+ headers["timestamp"] = timestamp
39
+ headers["Domain"] = entry
40
+ response = requests.post(url, json=argDict, headers=headers)
41
+ if response.status_code != 200:
42
+ raise Exception("请求失败")
43
+ respJson = response.json()
44
+ return respJson
jit_utils/string.py ADDED
@@ -0,0 +1,118 @@
1
+ # -*-coding:utf-8-*-
2
+ """
3
+ Created on 2023/10/25 20:04
4
+
5
+ @author: 'wuhao'
6
+
7
+ @desc: 简单说明
8
+ """
9
+ import hashlib
10
+ import math
11
+ import os
12
+ import random
13
+ import re
14
+ import string
15
+ import uuid
16
+
17
+ CHARS = string.ascii_letters + string.digits
18
+
19
+
20
+ def randomString(size=8):
21
+ chars = random.choices(CHARS, k=size)
22
+ return "".join(chars)
23
+
24
+
25
+ def randomNum(size=6):
26
+ """生成随机6位数字,用于手机号验证码"""
27
+ num = "%0{}d".format(size) % random.randint(0, 10**size - 1)
28
+ return num
29
+
30
+
31
+ def getUuidStr():
32
+ return str(uuid.uuid4()).replace("-", "")
33
+
34
+
35
+ def md5Bytes(b):
36
+ """
37
+ md5加密
38
+ :param b: 文件的二进制数据
39
+ :return: str: md5字符串
40
+ """
41
+ return hashlib.md5(b).hexdigest()
42
+
43
+
44
+ def md5Str(normalStr, encodeType="utf-8"):
45
+ """
46
+ md5加密
47
+ :param normalStr: 普通python字符串
48
+ :param encodeType: 编码方式
49
+ :return str: md5字符串
50
+ """
51
+ return md5Bytes(normalStr.encode(encodeType))
52
+
53
+
54
+ def lowercase(name):
55
+ if not name:
56
+ return ""
57
+ return name[0].lower() + name[1:]
58
+
59
+
60
+ def capitalize(name):
61
+ if not name:
62
+ return ""
63
+ return name[0].upper() + name[1:]
64
+
65
+
66
+ def getFileMd5(filename):
67
+ if not os.path.isfile(filename):
68
+ return
69
+ myhash = hashlib.md5()
70
+ f = open(filename, "rb")
71
+ while True:
72
+ b = f.read(4096)
73
+ if not b:
74
+ break
75
+ myhash.update(b)
76
+ f.close()
77
+ return myhash.hexdigest()
78
+
79
+
80
+ def getRandomField(k=4):
81
+ character = string.ascii_lowercase + string.digits
82
+ charArray = random.choices(character, k=k)
83
+ res = "fk" + "".join(charArray)
84
+ return res
85
+
86
+
87
+ def getRandom(k=8):
88
+ character = string.ascii_lowercase + string.digits
89
+ charArray = random.choices(character, k=k)
90
+ return "".join(charArray)
91
+
92
+
93
+ def genrSublist(orgList, sliceSize):
94
+ """
95
+ for tempList in genrSublist(range(20), 5):
96
+ print(tempList)
97
+
98
+ [0, 1, 2, 3, 4]
99
+ [5, 6, 7, 8, 9]
100
+ [10, 11, 12, 13, 14]
101
+ [15, 16, 17, 18, 19]
102
+ """
103
+ sliceCount = int(math.ceil(len(orgList) / float(sliceSize)))
104
+ for i in range(sliceCount):
105
+ yield orgList[i * sliceSize : (i + 1) * sliceSize]
106
+
107
+
108
+ def renderTemplateString(source, **context):
109
+ # 使用正则表达式查找所有的变量 {{var_name}}
110
+ pattern = r"\{\{(\w+)\}\}"
111
+
112
+ def replaceVar(match):
113
+ var_name = match.group(1) # 获取变量名
114
+ return str(context.get(var_name, "")) # 从上下文中获取变量值,如果不存在则返回空字符串
115
+
116
+ rendered = re.sub(pattern, replaceVar, source)
117
+
118
+ return rendered