ezKit 1.8.0__py3-none-any.whl → 1.8.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.
ezKit/cls.py ADDED
@@ -0,0 +1,90 @@
1
+ """财联社数据"""
2
+ import re
3
+
4
+ import pandas as pd
5
+ import requests
6
+ from loguru import logger
7
+
8
+ from . import stock, utils
9
+
10
+
11
+ def up_down_analysis(
12
+ target: str = "up_pool",
13
+ df: bool = False
14
+ ) -> list | pd.DataFrame | None:
15
+ """涨停跌停数据"""
16
+
17
+ if not utils.v_true(target, str):
18
+ logger.error(f"error type: {target}")
19
+ return None
20
+
21
+ info: str = "获取涨停池股票"
22
+ match True:
23
+ case True if target == "up_pool":
24
+ info = "获取涨停池股票"
25
+ case True if target == "continuous_up_pool":
26
+ info = "获取连板池股票"
27
+ case True if target == "up_open_pool":
28
+ info = "获取炸板池股票"
29
+ case True if target == "down_pool":
30
+ info = "获取跌停池股票"
31
+ case _:
32
+ pass
33
+
34
+ try:
35
+ logger.info(f"{info} ......")
36
+
37
+ user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
38
+ headers = {"User-Agent": user_agent}
39
+
40
+ # 涨停池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=up_pool
41
+ # 连板池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=continuous_up_pool
42
+ # 炸板池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=up_open_pool
43
+ # 跌停池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=down_pool
44
+ api = f"https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type={target}"
45
+
46
+ response = requests.get(api, headers=headers, timeout=10)
47
+
48
+ response_dict: dict = response.json()
49
+
50
+ result: list = []
51
+
52
+ for i in response_dict["data"]:
53
+
54
+ # if re.match(r"^(sz00|sh60)", i["secu_code"]):
55
+ # print(i["secu_code"])
56
+
57
+ # if re.search(r"ST|银行", i["secu_name"]):
58
+ # print(i["secu_name"])
59
+
60
+ # 主板, 非ST, 非银行, 非证券
61
+ if (not re.match(r"^(sz00|sh60)", i["secu_code"])) or re.search(r"ST|银行|证券", i["secu_name"]):
62
+ continue
63
+
64
+ if target in ["up_pool", "up_pool"]:
65
+ result.append({
66
+ "code": stock.coderename(i["secu_code"], restore=True),
67
+ "name": i["secu_name"],
68
+ "up_days": i["limit_up_days"],
69
+ "reason": i["up_reason"]
70
+ })
71
+
72
+ if target in ["up_open_pool", "down_pool"]:
73
+ result.append({
74
+ "code": stock.coderename(i["secu_code"], restore=True),
75
+ "name": i["secu_name"]
76
+ })
77
+
78
+ if utils.v_true(df, bool) is False:
79
+ logger.success(f"{info} [成功]")
80
+ return result
81
+
82
+ # data: pd.DataFrame = pd.DataFrame(response_dict["data"], columns=["secu_code", "secu_name", "limit_up_days", "up_reason"])
83
+ # data = data.rename(columns={"secu_code": "code", "secu_name": "name", "limit_up_days": "up_days", "up_reason": "reason"})
84
+
85
+ return pd.DataFrame(data=pd.DataFrame(result))
86
+
87
+ except Exception as e:
88
+ logger.error(f"{info} [失败]")
89
+ logger.exception(e)
90
+ return None
ezKit/qywx.py ADDED
@@ -0,0 +1,186 @@
1
+ """
2
+ 企业微信开发者中心
3
+
4
+ https://developer.work.weixin.qq.com/
5
+ https://developer.work.weixin.qq.com/document/path/90313 (全局错误码)
6
+
7
+ 参考文档:
8
+
9
+ https://www.gaoyuanqi.cn/python-yingyong-qiyewx/
10
+ https://www.jianshu.com/p/020709b130d3
11
+ """
12
+ import json
13
+ import time
14
+
15
+ import requests
16
+ from loguru import logger
17
+
18
+ from . import utils
19
+
20
+
21
+ class QYWX:
22
+ """企业微信"""
23
+
24
+ url_prefix = "https://qyapi.weixin.qq.com"
25
+ work_id: str | None = None
26
+ agent_id: str | None = None
27
+ agent_secret: str | None = None
28
+ access_token: str | None = None
29
+
30
+ def __init__(self, work_id: str | None, agent_id: str | None, agent_secret: str | None):
31
+ """Initiation"""
32
+ self.work_id = work_id
33
+ self.agent_id = agent_id
34
+ self.agent_secret = agent_secret
35
+
36
+ """获取 Token"""
37
+ self.getaccess_token()
38
+
39
+ def getaccess_token(self) -> str | None:
40
+ try:
41
+ response = requests.get(f"{self.url_prefix}/cgi-bin/gettoken?corpid={self.work_id}&corpsecret={self.agent_secret}", timeout=10)
42
+ if response.status_code == 200:
43
+ result: dict = response.json()
44
+ self.access_token = result.get('access_token')
45
+ else:
46
+ self.access_token = None
47
+ return result.get('access_token')
48
+ except Exception as e:
49
+ logger.exception(e)
50
+ return None
51
+
52
+ def get_agent_list(self) -> dict | str | None:
53
+ try:
54
+ if self.access_token is None:
55
+ self.getaccess_token()
56
+ response = requests.get(f"{self.url_prefix}/cgi-bin/agent/list?access_token={self.access_token}", timeout=10)
57
+ if response.status_code == 200:
58
+ response_data: dict = response.json()
59
+ if response_data.get('errcode') == 42001:
60
+ self.getaccess_token()
61
+ time.sleep(1)
62
+ self.get_agent_list()
63
+ return response_data
64
+ return response.text
65
+ except Exception as e:
66
+ logger.exception(e)
67
+ return None
68
+
69
+ def get_department_list(self, eid: str | None = None) -> dict | str | None:
70
+ """eid: Enterprise ID"""
71
+ try:
72
+ if self.access_token is None:
73
+ self.getaccess_token()
74
+ response = requests.get(f"{self.url_prefix}/cgi-bin/department/list?access_token={self.access_token}&id={eid}", timeout=10)
75
+ if response.status_code == 200:
76
+ response_data: dict = response.json()
77
+ if response_data.get('errcode') == 42001:
78
+ self.getaccess_token()
79
+ time.sleep(1)
80
+ self.get_department_list(eid)
81
+ return response_data
82
+ return response.text
83
+ except Exception as e:
84
+ logger.exception(e)
85
+ return None
86
+
87
+ def get_user_list(self, eid: str | None = None) -> dict | str | None:
88
+ """eid: Enterprise ID"""
89
+ try:
90
+ if self.access_token is None:
91
+ self.getaccess_token()
92
+ response = requests.get(f"{self.url_prefix}/cgi-bin/user/list?access_token={self.access_token}&department_id={eid}", timeout=10)
93
+ if response.status_code == 200:
94
+ response_data: dict = response.json()
95
+ if response_data.get('errcode') == 42001:
96
+ self.getaccess_token()
97
+ time.sleep(1)
98
+ self.get_user_list(eid)
99
+ return response_data
100
+ return response.text
101
+ except Exception as e:
102
+ logger.exception(e)
103
+ return None
104
+
105
+ def get_user_id_by_mobile(self, mobile: str | None = None) -> dict | str | None:
106
+ try:
107
+ if self.access_token is None:
108
+ self.getaccess_token()
109
+ json_string = json.dumps({'mobile': mobile})
110
+ response = requests.post(f"{self.url_prefix}/cgi-bin/user/getuserid?access_token={self.access_token}", data=json_string, timeout=10)
111
+ if response.status_code == 200:
112
+ response_data: dict = response.json()
113
+ if response_data.get('errcode') == 42001:
114
+ self.getaccess_token()
115
+ time.sleep(1)
116
+ self.get_user_id_by_mobile(mobile)
117
+ return response_data
118
+ return response.text
119
+ except Exception as e:
120
+ logger.exception(e)
121
+ return None
122
+
123
+ def get_user_info(self, eid: str | None = None) -> dict | str | None:
124
+ """eid: Enterprise ID"""
125
+ try:
126
+ if self.access_token is None:
127
+ self.getaccess_token()
128
+ response = requests.get(f"{self.url_prefix}/cgi-bin/user/get?access_token={self.access_token}&userid={eid}", timeout=10)
129
+ if response.status_code == 200:
130
+ response_data: dict = response.json()
131
+ if response_data.get('errcode') == 42001:
132
+ self.getaccess_token()
133
+ time.sleep(1)
134
+ self.get_user_info(eid)
135
+ return response_data
136
+ return response.text
137
+ except Exception as e:
138
+ logger.exception(e)
139
+ return None
140
+
141
+ def send_message_by_mobile(self, mobile: str | list, message: str) -> bool:
142
+ """发送消息"""
143
+
144
+ # 参考文档:
145
+ # https://developer.work.weixin.qq.com/document/path/90235
146
+
147
+ try:
148
+ if self.access_token is None:
149
+ self.getaccess_token()
150
+
151
+ users: list = []
152
+
153
+ match True:
154
+ case True if utils.v_true(mobile, list):
155
+ users = mobile
156
+ case True if utils.v_true(mobile, str):
157
+ users.append(mobile)
158
+ case _:
159
+ return None
160
+
161
+ for user in users:
162
+ user_object = self.get_user_id_by_mobile(user)
163
+ json_dict = {
164
+ 'touser': user_object.get('userid'),
165
+ 'msgtype': 'text',
166
+ 'agentid': self.agent_id,
167
+ 'text': {'content': message},
168
+ 'safe': 0,
169
+ 'enable_id_trans': 0,
170
+ 'enable_duplicate_check': 0,
171
+ 'duplicate_check_interval': 1800
172
+ }
173
+ json_string = json.dumps(json_dict)
174
+ response = requests.post(f"{self.url_prefix}/cgi-bin/message/send?access_token={self.access_token}", data=json_string, timeout=10)
175
+ if response.status_code == 200:
176
+ response_data: dict = response.json()
177
+ if response_data.get('errcode') == 42001:
178
+ self.getaccess_token()
179
+ time.sleep(1)
180
+ self.send_message_by_mobile(mobile, message)
181
+
182
+ return True
183
+
184
+ except Exception as e:
185
+ logger.exception(e)
186
+ return False
ezKit/stock.py ADDED
@@ -0,0 +1,61 @@
1
+ """股票"""
2
+ # pylint: disable=R0911
3
+ from copy import deepcopy
4
+ from typing import Any
5
+
6
+ from loguru import logger
7
+
8
+ from . import utils
9
+
10
+
11
+ def coderename(target: str | dict, restore: bool = False) -> str | dict | None:
12
+ """
13
+ 正向:
14
+ coderename('000001') => 'sz000001'
15
+ coderename({'code': '000001', 'name': '平安银行'}) => {'code': 'sz000001', 'name': '平安银行'}
16
+ 反向:
17
+ coderename('sz000001', restore=True) => '000001'
18
+ coderename({'code': 'sz000001', 'name': '平安银行'}) => {'code': '000001', 'name': '平安银行'}
19
+ """
20
+
21
+ try:
22
+
23
+ _object: Any = None
24
+ _code_name: Any = None
25
+
26
+ # 判断 target 是 string 还是 dictionary
27
+ if isinstance(target, str) and utils.v_true(target, str):
28
+ _code_name = target
29
+ elif isinstance(target, dict) and utils.v_true(target, dict):
30
+ _object = deepcopy(target)
31
+ _code_name = str(deepcopy(target["code"]))
32
+ else:
33
+ return None
34
+
35
+ # 是否还原
36
+ if restore:
37
+ if len(_code_name) == 8 and ("sh" in _code_name or "sz" in _code_name):
38
+ _code_name = _code_name[2:8]
39
+ else:
40
+ return None
41
+ else:
42
+ if _code_name[0:2] == "00":
43
+ _code_name = "sz" + _code_name
44
+ elif _code_name[0:2] == "60":
45
+ _code_name = "sh" + _code_name
46
+ else:
47
+ return None
48
+
49
+ # 返回结果
50
+ if utils.v_true(target, str):
51
+ return _code_name
52
+
53
+ if utils.v_true(target, dict):
54
+ _object["code"] = _code_name
55
+ return _object
56
+
57
+ return None
58
+
59
+ except Exception as e:
60
+ logger.exception(e)
61
+ return None
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.8.0
3
+ Version: 1.8.2
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
7
7
  Requires-Python: >=3.11
8
8
  License-File: LICENSE
9
- Requires-Dist: loguru>=0.7.0
9
+ Requires-Dist: loguru>=0.7
@@ -2,16 +2,19 @@ ezKit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  ezKit/bottle.py,sha256=usKK1wVaZw4_D-4VwMYmOIc8jtz4TrpM30nck59HMFw,180178
3
3
  ezKit/bottle_extensions.py,sha256=LQikCbbYZBAa4AcihvrTvixWHHMY7OjCBsT02PqWMMM,1129
4
4
  ezKit/cipher.py,sha256=uVUzeoYg5ZPJLpqg2wLst-7vfdDGmqWKka9Oyi0WrCA,2883
5
+ ezKit/cls.py,sha256=mkNSguUNocP8aKh1GEpkUMgL9xRXfMKdGs6tEeeV5X8,3266
5
6
  ezKit/database.py,sha256=awTEdD4aqevyJg5LPBHmUPrOKweqzJ2Ezqqx0_xgUDA,6859
6
7
  ezKit/http.py,sha256=cyS18-TW9f7p1OaiJ4nnVsKZT8Ghy-8OewkfYm8yesw,1790
7
8
  ezKit/mongo.py,sha256=P6WTwFRxaaHixJK_PyKlOfPHkeJRxxrNLV77xy5LVBQ,2048
9
+ ezKit/qywx.py,sha256=ra2e3u8IJmNZXr569u1mik3KArAiMdC9x0SgXGXEJes,7003
8
10
  ezKit/redis.py,sha256=HVofsLdSBbBHAR-veumsrjTwZQspRDy2FXNR6MDCCXs,1972
9
11
  ezKit/sendemail.py,sha256=Qxu4XQkHRPeX6FSJdzj-MXND9NyKcgHljbafNmy34H0,8243
12
+ ezKit/stock.py,sha256=gEy4N7S0GsZCdfEdYHfaUHMEHMFmNGfHm198hG5n5eI,1754
10
13
  ezKit/token.py,sha256=9CAZhPdXiRiWoOIeWmP0q6L3j1zQAv4YcVWH95Tjefs,1755
11
14
  ezKit/utils.py,sha256=an7joZy_EEpYfN8zBtEWAnhP0YXYfPieabsK_HAxXl4,48921
12
15
  ezKit/xftp.py,sha256=7BABr-gjxZxK2UXoW9XxN76i1HdubeaomYlYMqRurHE,7770
13
- ezKit-1.8.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
- ezKit-1.8.0.dist-info/METADATA,sha256=CNkyQ01iJgdaiUFUFq2n8aL9MBXtxduFVK6ZBpJrW68,192
15
- ezKit-1.8.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
16
- ezKit-1.8.0.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
17
- ezKit-1.8.0.dist-info/RECORD,,
16
+ ezKit-1.8.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
17
+ ezKit-1.8.2.dist-info/METADATA,sha256=Vnx7icBFnyUYYAHZz3jh5hhDS11d3hHngGlBz-DkWwU,190
18
+ ezKit-1.8.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
19
+ ezKit-1.8.2.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
20
+ ezKit-1.8.2.dist-info/RECORD,,
File without changes
File without changes