gou2tool 0.2.11__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.
@@ -0,0 +1,202 @@
1
+ from typing import List, Dict, Any, Callable
2
+ import copy
3
+
4
+
5
+ class TreeUtil:
6
+ """
7
+ 树结构工具类
8
+ """
9
+
10
+ @staticmethod
11
+ def list_to_tree(list_data: List[Dict[str, Any]], config: Dict[str, Any] = None) -> List[Dict[str, Any]]:
12
+ """
13
+ 数组转树结构
14
+ :param list_data: 原始列表数据
15
+ :param config: 配置参数
16
+ :return: 树结构数据
17
+ """
18
+ if config is None:
19
+ config = {}
20
+
21
+ # 初始化参数
22
+ config.setdefault("id", "id")
23
+ config.setdefault("parent_id", "parent_id")
24
+ config.setdefault("children", "children")
25
+
26
+ # 新建映射关系
27
+ map_dict = {}
28
+ filtered_list = []
29
+
30
+ for item in list_data:
31
+ # 跳过开头的上级节点
32
+ if "level" in config and "init_level" in config and config.get("level") in item:
33
+ if item[config["level"]] < config["init_level"]:
34
+ continue
35
+ map_dict[item[config["id"]]] = item
36
+ filtered_list.append(item)
37
+
38
+ # 树数据
39
+ tree = []
40
+ for item in filtered_list:
41
+ # 跳过开头的上级节点
42
+ if "level" in config and "init_level" in config and config.get("level") in item:
43
+ if item[config["level"]] < config["init_level"]:
44
+ continue
45
+
46
+ # 获取上级节点
47
+ parent_id = None
48
+ if item[config["parent_id"]] in map_dict:
49
+ parent_id = item[config["parent_id"]]
50
+ else:
51
+ # 通过完整id字段 查找上级节点 补充缺省上级数据
52
+ if "full_id" in config and config["full_id"] in item:
53
+ ids = item[config["full_id"]].split(",")
54
+ for id_val in ids:
55
+ if item[config["id"]] != id_val and id_val in map_dict:
56
+ parent_id = id_val
57
+
58
+ # 判断上级节点是否存在 不存在则添加到顶级节点
59
+ if parent_id is not None:
60
+ # 添加到子节点
61
+ if config["children"] not in map_dict[parent_id]:
62
+ map_dict[parent_id][config["children"]] = []
63
+ map_dict[parent_id][config["children"]].append(item)
64
+ else:
65
+ tree.append(item)
66
+
67
+ def recursion(data, next_func, node):
68
+ result = []
69
+ for item in data:
70
+ new_item = copy.deepcopy(item)
71
+ if "level" in config:
72
+ new_item[config["level"]] = node["level"]
73
+
74
+ if config["children"] in new_item:
75
+ if "level" in config and "max_level" in config:
76
+ if node["level"] + 1 > config["max_level"]:
77
+ del new_item[config["children"]]
78
+ else:
79
+ new_item[config["children"]] = next_func(new_item[config["children"]], next_func)
80
+ else:
81
+ new_item[config["children"]] = next_func(new_item[config["children"]], next_func)
82
+ result.append(new_item)
83
+ return result
84
+
85
+ return TreeUtil.recursion(tree, recursion)
86
+
87
+ @staticmethod
88
+ def tree_to_list(tree: List[Dict[str, Any]], config: Dict[str, Any] = None) -> List[Dict[str, Any]]:
89
+ """
90
+ 树结构转数组
91
+ :param tree: 树结构数据
92
+ :param config: 配置参数
93
+ :return: 列表数据
94
+ """
95
+ if config is None:
96
+ config = {}
97
+
98
+ config.setdefault("children", "children")
99
+ config.setdefault("unset_children", True)
100
+
101
+ def list_handle(tree_data, conf, handler):
102
+ result = []
103
+ for item in tree_data:
104
+ new_item = copy.deepcopy(item)
105
+ children = None
106
+ if conf["children"] in new_item:
107
+ children = new_item[conf["children"]]
108
+ if conf.get("unset_children", True):
109
+ del new_item[conf["children"]]
110
+ else:
111
+ children = None
112
+
113
+ result.append(new_item)
114
+ if isinstance(children, list):
115
+ result.extend(handler(children, conf, handler))
116
+ return result
117
+
118
+ return list_handle(tree, config, list_handle)
119
+
120
+ @staticmethod
121
+ def get_max_depth(tree: List[Dict[str, Any]], config: Dict[str, Any] = None) -> int:
122
+ """
123
+ 获取树节点的最大深度
124
+ :param tree: 树结构数据
125
+ :param config: 配置参数
126
+ :return: 最大深度
127
+ """
128
+ if config is None:
129
+ config = {}
130
+
131
+ config.setdefault("id", "id")
132
+ config.setdefault("children", "children")
133
+
134
+ max_depth = 0
135
+
136
+ def callback(item, node):
137
+ nonlocal max_depth
138
+ if node['level'] + 1 > max_depth:
139
+ max_depth = node['level'] + 1
140
+
141
+ TreeUtil.recursion_each(tree, callback, config)
142
+ return max_depth
143
+
144
+ @staticmethod
145
+ def recursion(param_or_tree: List[Dict[str, Any]], main_next: Callable,
146
+ node: Dict[str, Any] = None) -> Any:
147
+ """
148
+ 递归处理树形数据的核心方法
149
+ :param param_or_tree: 待处理的数据
150
+ :param main_next: 处理函数
151
+ :param node: 节点信息
152
+ :return: 处理后的结果
153
+ """
154
+ if node is None:
155
+ node = {"level": 0, "top": True, "parent": None, "parents": []}
156
+
157
+ def next_func(param_or_tree_inner, parent):
158
+ new_node = {
159
+ "level": node["level"] + 1,
160
+ "top": False,
161
+ "parent": parent,
162
+ "parents": node["parents"] + [parent] if parent else []
163
+ }
164
+ return TreeUtil.recursion(param_or_tree_inner, main_next, new_node)
165
+
166
+ return main_next(param_or_tree, next_func, node)
167
+
168
+ @staticmethod
169
+ def recursion_each(tree: List[Dict[str, Any]], func: Callable,
170
+ config: Dict[str, Any] = None) -> List[Dict[str, Any]]:
171
+ """
172
+ 遍历树形结构中的每个节点并执行回调函数
173
+ :param tree: 树形结构数据
174
+ :param func: 回调函数
175
+ :param config: 配置参数
176
+ :return: 处理后的树
177
+ """
178
+ if config is None:
179
+ config = {}
180
+
181
+ config.setdefault("children", "children")
182
+
183
+ def tree_handle(tree_data, handler, node=None):
184
+ if node is None:
185
+ node = {"level": 0, "top": True, "parent": None, "parents": []}
186
+
187
+ for item in tree_data:
188
+ func(item, node)
189
+ if config["children"] in item and isinstance(item[config["children"]], list):
190
+ handler(
191
+ item[config["children"]],
192
+ handler,
193
+ {
194
+ "parent": item,
195
+ "parents": node["parents"] + [item],
196
+ "level": node["level"] + 1,
197
+ "top": False,
198
+ }
199
+ )
200
+
201
+ tree_handle(tree, tree_handle)
202
+ return tree
@@ -0,0 +1,111 @@
1
+ import hashlib
2
+ import hmac
3
+ import json
4
+ import time
5
+ import requests
6
+ import base64
7
+ import urllib.parse
8
+
9
+
10
+ class WebHookUtil:
11
+
12
+ @staticmethod
13
+ def send(url, data, secret=None):
14
+ """
15
+ 发送Webhook请求
16
+
17
+ Args:
18
+ url (str): Webhook地址
19
+ data (dict): 发送的数据
20
+ secret (str, optional): 签名密钥
21
+
22
+ Returns:
23
+ dict: 响应结果
24
+ """
25
+ headers = {
26
+ 'Content-Type': 'application/json',
27
+ 'User-Agent': 'WebHookUtil/1.0'
28
+ }
29
+
30
+ # 如果提供了secret,则添加签名
31
+ if secret:
32
+ timestamp = str(int(time.time() * 1000))
33
+ string_to_sign = timestamp + "\n" + secret
34
+ hmac_code = hmac.new(
35
+ secret.encode('utf-8'),
36
+ string_to_sign.encode('utf-8'),
37
+ hashlib.sha256
38
+ ).digest()
39
+ sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
40
+
41
+ # 将签名参数添加到URL中而不是请求头
42
+ separator = '&' if '?' in url else '?'
43
+ url_with_sign = f"{url}{separator}timestamp={timestamp}&sign={sign}"
44
+ else:
45
+ url_with_sign = url
46
+
47
+ response = requests.post(url_with_sign, headers=headers, data=json.dumps(data), timeout=30)
48
+ response.raise_for_status()
49
+
50
+ @staticmethod
51
+ def send_text(url, content, secret=None):
52
+ """
53
+ 发送文本消息
54
+
55
+ Args:
56
+ url (str): Webhook地址
57
+ content (str): 消息内容
58
+ secret (str, optional): 签名密钥
59
+
60
+ Returns:
61
+ dict: 响应结果
62
+ """
63
+ WebHookUtil.send(url, {"msgtype": "text", "text": {"content": content}}, secret)
64
+
65
+ @staticmethod
66
+ def send_markdown(url, title, text, secret=None):
67
+ """
68
+ 发送Markdown消息
69
+
70
+ Args:
71
+ url (str): Webhook地址
72
+ title (str): 消息标题
73
+ text (str): 消息文本
74
+ secret (str, optional): 签名密钥
75
+
76
+ Returns:
77
+ dict: 响应结果
78
+ """
79
+ WebHookUtil.send(url, {"msgtype": "markdown", "markdown": {"title": title, "text": text}}, secret)
80
+
81
+ @staticmethod
82
+ def send_link(url, title, text, link, secret=None):
83
+ """
84
+ 发送链接消息
85
+
86
+ Args:
87
+ url (str): Webhook地址
88
+ title (str): 消息标题
89
+ text (str): 消息文本
90
+ link (str): 消息链接
91
+ secret (str, optional): 签名密钥
92
+
93
+ Returns:
94
+ dict: 响应结果
95
+ """
96
+ WebHookUtil.send(url, {"msgtype": "link", "link": {"title": title, "text": text}, "messageUrl": link}, secret)
97
+
98
+ @staticmethod
99
+ def send_image(url, image_base64, secret=None):
100
+ """
101
+ 发送图片消息
102
+
103
+ Args:
104
+ url (str): Webhook地址
105
+ image_base64 (str): 图片
106
+ secret (str, optional): 签名密钥
107
+
108
+ Returns:
109
+ dict: 响应结果
110
+ """
111
+ WebHookUtil.send(url, {"msgtype": "image", "image": {"base64": image_base64}}, secret)
tests/__init__.py ADDED
File without changes
tests/id_util.py ADDED
@@ -0,0 +1,17 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.IdUtil import IdUtil
4
+
5
+
6
+ class TestDemo(unittest.TestCase):
7
+ def test_simple_uuid(self):
8
+ print(IdUtil.simple_uuid())
9
+ self.assertTrue(True)
10
+
11
+ def test_random_uuid(self):
12
+ print(IdUtil.random_uuid())
13
+ self.assertTrue(True)
14
+
15
+
16
+ if __name__ == '__main__':
17
+ unittest.main()
tests/path_util.py ADDED
@@ -0,0 +1,11 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.PathUtil import PathUtil
4
+
5
+ class TestDemo(unittest.TestCase):
6
+ def test_simple_uuid(self):
7
+ print(PathUtil.goutool_path())
8
+ self.assertTrue(True)
9
+
10
+ if __name__ == '__main__':
11
+ unittest.main()
tests/phone_util.py ADDED
@@ -0,0 +1,18 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.PhoneUtil import PhoneUtil
4
+
5
+
6
+ class TestDemo(unittest.TestCase):
7
+
8
+ def test_is_phone(self):
9
+ print(PhoneUtil.is_phone('11571631036'))
10
+ self.assertTrue(True)
11
+
12
+ def test_is_tel(self):
13
+ print(PhoneUtil.is_tel('111-58597128'))
14
+ self.assertTrue(True)
15
+
16
+
17
+ if __name__ == '__main__':
18
+ unittest.main()
tests/sql_util.py ADDED
@@ -0,0 +1,21 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.SqlUtil import SQLUtil
4
+
5
+
6
+ class TestDemo(unittest.TestCase):
7
+
8
+ def test_insert(self):
9
+ print(SQLUtil.update("crm_organizations", {
10
+ "lat": 1,
11
+ "lon": 1,
12
+ "name": None,
13
+ "desc": "'adq'qa",
14
+ "phone": ["11", "22'2"],
15
+ "dict": {'a': 1, 'b': "a'"}
16
+ }, {"id": 1, "ide": "2'"}))
17
+ self.assertTrue(True)
18
+
19
+
20
+ if __name__ == '__main__':
21
+ unittest.main()
tests/str_util.py ADDED
@@ -0,0 +1,15 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.StrUtil import StrUtil
4
+
5
+
6
+ class TestDemo(unittest.TestCase):
7
+
8
+ def test_insert(self):
9
+ print(StrUtil.calculate_similarity("湖北省武汉市武昌区楚汉路18号武汉中央文化区k4地块一期k4-2-5栋1-2层14-15室",
10
+ "湖北省武汉市武昌区楚汉路武汉中央文化区地块一期k4-1-1栋"))
11
+ self.assertTrue(True)
12
+
13
+
14
+ if __name__ == '__main__':
15
+ unittest.main()
tests/web_hook_util.py ADDED
@@ -0,0 +1,22 @@
1
+ import unittest
2
+
3
+ from src.main.py.gou2tool.util.WebHookUtil import WebHookUtil
4
+
5
+
6
+ class TestDemo(unittest.TestCase):
7
+
8
+ def test_insert(self):
9
+ WebHookUtil.send(
10
+ "https://oapi.dingtalk.com/robot/send?access_token=6eb7c4a05da8552f740e9912b488a91a56b958eb191ab2c84783389ac6f8a559",
11
+ {
12
+ 'msgtype': 'text',
13
+ 'text': {
14
+ 'content': "1111"
15
+ }
16
+ },
17
+ "SEC745aba3ccb27eba9e72e73de98649624bd9630085e708eb939456fa5ca2664c4")
18
+ self.assertTrue(True)
19
+
20
+
21
+ if __name__ == '__main__':
22
+ unittest.main()