smartpush 1.3.1__tar.gz → 1.3.2__tar.gz
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.
- {smartpush-1.3.1 → smartpush-1.3.2}/PKG-INFO +1 -1
- {smartpush-1.3.1 → smartpush-1.3.2}/setup.py +1 -1
- smartpush-1.3.2/smartpush/get_jira_info.py +386 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/utils/StringUtils.py +7 -1
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush.egg-info/PKG-INFO +1 -1
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush.egg-info/SOURCES.txt +0 -1
- smartpush-1.3.1/smartpush/get_jira_info.py +0 -74
- smartpush-1.3.1/smartpush/test.py +0 -41
- {smartpush-1.3.1 → smartpush-1.3.2}/README.md +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/setup.cfg +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/__init__.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/export/__init__.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/export/basic/ExcelExportChecker.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/export/basic/GetOssUrl.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/export/basic/ReadExcel.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/export/basic/__init__.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/utils/DataTypeUtils.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush/utils/__init__.py +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush.egg-info/dependency_links.txt +0 -0
- {smartpush-1.3.1 → smartpush-1.3.2}/smartpush.egg-info/top_level.txt +0 -0
@@ -0,0 +1,386 @@
|
|
1
|
+
import datetime
|
2
|
+
import json
|
3
|
+
import requests
|
4
|
+
from jira import JIRA
|
5
|
+
|
6
|
+
from smartpush.utils.StringUtils import StringUtils
|
7
|
+
|
8
|
+
test_user = {
|
9
|
+
"邵宇飞": "dw_shaoyufei",
|
10
|
+
"卢泽彬-Lulu-QA": "dw_luzebin",
|
11
|
+
"xiangchen zhong": "dw_zhongxiangchen",
|
12
|
+
"周彦龙": "zhouyanlong"
|
13
|
+
}
|
14
|
+
|
15
|
+
|
16
|
+
def get_last_friday_and_this_thursday():
|
17
|
+
"""
|
18
|
+
获取上个周五和本周四的日期
|
19
|
+
:return:
|
20
|
+
"""
|
21
|
+
today = datetime.date.today()
|
22
|
+
# 计算今天是星期几,0 表示周一,6 表示周日
|
23
|
+
weekday = today.weekday()
|
24
|
+
# 计算距离上周五的天数
|
25
|
+
days_to_last_friday = (weekday + 3) % 7
|
26
|
+
if days_to_last_friday == 0:
|
27
|
+
days_to_last_friday = 7
|
28
|
+
# 得到上周五的日期
|
29
|
+
last_friday = today - datetime.timedelta(days=days_to_last_friday)
|
30
|
+
# 得到本周四的日期
|
31
|
+
this_thursday = last_friday + datetime.timedelta(days=6)
|
32
|
+
return last_friday, this_thursday
|
33
|
+
|
34
|
+
|
35
|
+
def get_update_issues_jql():
|
36
|
+
"""
|
37
|
+
根据上周五到本周四的日期范围生成 JQL 查询语句
|
38
|
+
"""
|
39
|
+
|
40
|
+
# 获取上周五和本周四的日期
|
41
|
+
start_date, end_date = get_last_friday_and_this_thursday()
|
42
|
+
|
43
|
+
# 定义 JQL 模板
|
44
|
+
jql_template = f'''
|
45
|
+
project = "SSP"
|
46
|
+
AND issuetype IN (产品需求, 技术需求)
|
47
|
+
AND (("计划上线时间[date]" >= "{{start_date}}"
|
48
|
+
AND "计划上线时间[date]" <= "{{end_date}}" and "调整后上线时间[Date]" is EMPTY)
|
49
|
+
OR ("实际上线时间[date]" >= "{{start_date}}"
|
50
|
+
AND "实际上线时间[date]" <= "{{end_date}}") OR ("调整后上线时间[Date]" >= "{{start_date}}"
|
51
|
+
AND "调整后上线时间[Date]" <= "{{end_date}}"))
|
52
|
+
ORDER BY cf[12536] ASC, key ASC, cf[12466] ASC, status DESC, created DESC
|
53
|
+
'''
|
54
|
+
# 填充模板中的日期
|
55
|
+
jql_update = jql_template.format(
|
56
|
+
start_date=start_date,
|
57
|
+
end_date=end_date,
|
58
|
+
)
|
59
|
+
print(jql_update)
|
60
|
+
return jql_update
|
61
|
+
|
62
|
+
|
63
|
+
class JiraInfo:
|
64
|
+
def __init__(self, _jira_user, _api_key, _project_key):
|
65
|
+
self.project_name = None
|
66
|
+
self.jira_url = "https://shopline.atlassian.net/"
|
67
|
+
self.project_key = _project_key
|
68
|
+
self.issue = None
|
69
|
+
self.jira_user = _jira_user
|
70
|
+
self.jira_api_key = _api_key
|
71
|
+
self.jira = JIRA(server=self.jira_url, basic_auth=(self.jira_user, self.jira_api_key))
|
72
|
+
self.get_jira_prodcut()
|
73
|
+
self.custom_fields = self.get_custom_fields()
|
74
|
+
|
75
|
+
def get_jira_prodcut(self):
|
76
|
+
""""""
|
77
|
+
project = self.jira.project(str(self.project_key))
|
78
|
+
self.project_name = project.name
|
79
|
+
print(f"Project: {project.key} - {project.name}")
|
80
|
+
# return project
|
81
|
+
|
82
|
+
def get_custom_fields(self) -> dict:
|
83
|
+
"""
|
84
|
+
查询指定项目jira中的自定义字段,smartpush项目是 10559
|
85
|
+
@param project_id: 项目id
|
86
|
+
@param jira_obj: 对象
|
87
|
+
@return: 返回的是自定义的值对应的id
|
88
|
+
:param project_key:
|
89
|
+
"""
|
90
|
+
all_fields = self.jira.fields()
|
91
|
+
custom_fields = {}
|
92
|
+
for field in all_fields:
|
93
|
+
try:
|
94
|
+
if field.get('custom'):
|
95
|
+
# if field['scope']['project']['id'] == self.project_key:
|
96
|
+
custom_fields[field['id']] = field['name']
|
97
|
+
# else:
|
98
|
+
# pass
|
99
|
+
except:
|
100
|
+
continue
|
101
|
+
# print(custom_fields)
|
102
|
+
return custom_fields
|
103
|
+
|
104
|
+
def get_jira_issues_by_jql(self, filter_id=None, jql=None):
|
105
|
+
"""
|
106
|
+
根据过滤id查询,或者jql查询
|
107
|
+
:param filter_id:
|
108
|
+
:param jql:
|
109
|
+
:return:
|
110
|
+
"""
|
111
|
+
if filter_id:
|
112
|
+
filter_obj = self.jira.filter(filter_id)
|
113
|
+
jql = filter_obj.jql
|
114
|
+
elif jql is None:
|
115
|
+
default_jql_str = f'project = {self.project_key}'
|
116
|
+
try:
|
117
|
+
all_issues = {}
|
118
|
+
start_at = 0
|
119
|
+
max_results = 100 # 每次查询的最大结果数
|
120
|
+
while True:
|
121
|
+
# 构建 JQL 查询语句,根据项目 key 进行查询
|
122
|
+
jql_str = jql if jql else default_jql_str
|
123
|
+
print(f"执行查询的jql语句为:{jql_str}")
|
124
|
+
# 执行 JQL 查询获取 issues
|
125
|
+
issues = self.jira.search_issues(jql_str=jql_str, startAt=start_at, maxResults=max_results)
|
126
|
+
if not issues:
|
127
|
+
break
|
128
|
+
for issue in issues:
|
129
|
+
self.issue = issue
|
130
|
+
datas = self.custom_value_type()
|
131
|
+
all_issues[self.issue.key] = datas
|
132
|
+
if len(issues) < max_results:
|
133
|
+
break
|
134
|
+
start_at += max_results
|
135
|
+
return all_issues
|
136
|
+
except Exception as e:
|
137
|
+
raise
|
138
|
+
# return {'error': str(e)}
|
139
|
+
|
140
|
+
def custom_value_type(self):
|
141
|
+
"""
|
142
|
+
用于获取自定义字段的key和value值的处理
|
143
|
+
:return:
|
144
|
+
"""
|
145
|
+
custom_value_info = {}
|
146
|
+
for custom_field_id, custom_field_value in self.issue.raw['fields'].items():
|
147
|
+
custom_field_id = self.custom_fields.get(custom_field_id, custom_field_id)
|
148
|
+
if custom_field_id in ["comment", "attachment"]:
|
149
|
+
custom_field_value = "省略内容..有需要再打开,在这方法过滤:custom_value_type"
|
150
|
+
custom_field_value = self.transform_data(custom_field_value) # 转换数据
|
151
|
+
custom_value_info['url'] = self.jira_url + 'browse/' + self.issue.key
|
152
|
+
custom_value_info[custom_field_id] = custom_field_value
|
153
|
+
return custom_value_info
|
154
|
+
|
155
|
+
def transform_data(self, data):
|
156
|
+
"""
|
157
|
+
转换数据,否则数据冗余的有点多
|
158
|
+
:param data:
|
159
|
+
:return:
|
160
|
+
"""
|
161
|
+
if not isinstance(data, list) and not isinstance(data, dict):
|
162
|
+
return data
|
163
|
+
if isinstance(data, list):
|
164
|
+
result = []
|
165
|
+
for item in data:
|
166
|
+
if isinstance(item, dict):
|
167
|
+
for priority_key in ['value', 'name', 'displayName']:
|
168
|
+
if priority_key in item:
|
169
|
+
result.append(item[priority_key])
|
170
|
+
break
|
171
|
+
else:
|
172
|
+
result.append(item)
|
173
|
+
else:
|
174
|
+
result.append(item)
|
175
|
+
return result
|
176
|
+
elif isinstance(data, dict):
|
177
|
+
for priority_key in ['value', 'name', 'displayName']:
|
178
|
+
if priority_key in data:
|
179
|
+
return data[priority_key]
|
180
|
+
return data
|
181
|
+
return data
|
182
|
+
|
183
|
+
def update_filter_jql(self, filter_id=None, new_jql=None, double_check=False):
|
184
|
+
|
185
|
+
try:
|
186
|
+
if new_jql is None:
|
187
|
+
new_jql = get_update_issues_jql()
|
188
|
+
# 获取过滤器对象
|
189
|
+
filter_obj = self.jira.filter(filter_id)
|
190
|
+
# 更新过滤器的 JQL
|
191
|
+
if str(filter_id) == '19030' or double_check:
|
192
|
+
filter_obj.update(jql=new_jql)
|
193
|
+
print(f"过滤器 {filter_id} 的 JQL 已更新为: {new_jql}")
|
194
|
+
else:
|
195
|
+
print(f"{new_jql} 更新失败!")
|
196
|
+
except Exception as e:
|
197
|
+
print(f"更新过滤器 JQL 时出错: {e}")
|
198
|
+
|
199
|
+
def product_and_ui_acceptance_notice(self, webhook, datas):
|
200
|
+
"""
|
201
|
+
每周一早上11点通知产品/UI验收
|
202
|
+
1、有前端工作量时才需要通知ui
|
203
|
+
2、列出对应的ui,没有显示未填写
|
204
|
+
:return:
|
205
|
+
"""
|
206
|
+
fixVersions = ""
|
207
|
+
content = ""
|
208
|
+
for key, value in datas.items():
|
209
|
+
if value.get("issuetype") == "产品需求":
|
210
|
+
summary = value.get("summary")
|
211
|
+
url = value.get("url")
|
212
|
+
tester_user_id = self.get_tester_wecom_userid(value.get("测试人员"), test_user)
|
213
|
+
product_manager = value.get("产品经理")
|
214
|
+
ued = value.get("设计师")
|
215
|
+
fixVersions = value.get("fixVersions")
|
216
|
+
content += f"> [{key + '-' + summary}]({url})\n PM:{product_manager}\n" \
|
217
|
+
f"UED:{ued}\n测试:{tester_user_id} \n\n "
|
218
|
+
content = f"### <font color=\"warning\"> 提醒本周{fixVersions}上线产品需求验收</font> \n" + content
|
219
|
+
self.send_wecom_robot_message(webhook, content)
|
220
|
+
|
221
|
+
def registration_coverage_notice(self, webhook, datas):
|
222
|
+
"""
|
223
|
+
测试环境通知登记覆盖率--每周三早上
|
224
|
+
1、有后端开发工作量时才提示
|
225
|
+
2、并且把填写的链接附上
|
226
|
+
:return:
|
227
|
+
"""
|
228
|
+
content = ""
|
229
|
+
for key, value in datas.items():
|
230
|
+
if value.get("后端工作量") > 0:
|
231
|
+
summary = value.get("summary")
|
232
|
+
url = value.get("url")
|
233
|
+
tester_user_id = self.get_tester_wecom_userid(value.get("测试人员"), test_user)
|
234
|
+
test_end_time = value.get("测试完成时间(测试环境)")
|
235
|
+
content += f"> [{key + '-' + summary}]({url}) \n测试:{tester_user_id} \n测试完成时间:{test_end_time}\n\n "
|
236
|
+
content = f"### <font color=\"warning\"> 本周涉及后端需求如下,请分析并登记覆盖率,周会前填写</font> \n" + content
|
237
|
+
self.send_wecom_robot_message(webhook, content)
|
238
|
+
|
239
|
+
def bug_not_closed_notice(self, webhook, datas):
|
240
|
+
"""
|
241
|
+
预发环境或者测试环境通知bug还未关闭的需求,每周二,周三执行
|
242
|
+
1、'待修复', '修复中', '待验证'
|
243
|
+
:return:
|
244
|
+
"""
|
245
|
+
content = ""
|
246
|
+
for key, value in datas.items():
|
247
|
+
issuelinks = value['issuelinks']
|
248
|
+
not_fixed_dict = self.is_bug_not_fixed(issuelinks)
|
249
|
+
if not_fixed_dict:
|
250
|
+
summary = value.get("summary")
|
251
|
+
url = value.get("url")
|
252
|
+
tester_user_id = self.get_tester_wecom_userid(value.get("测试人员"), test_user)
|
253
|
+
content += f"> [{key + '-' + summary}]({url}) \n暂未修复bug数量:{len(not_fixed_dict)}\n测试:{tester_user_id} \n\n "
|
254
|
+
if StringUtils.is_empty(content):
|
255
|
+
return
|
256
|
+
content = f"### <font color=\"warning\"> 提醒本周上线需求暂未修复缺陷,请尽快确认</font> \n" + content
|
257
|
+
self.send_wecom_robot_message(webhook, content)
|
258
|
+
# print(content)
|
259
|
+
|
260
|
+
def modify_the_online_status_of_jira_notice(self, webhook, datas):
|
261
|
+
"""
|
262
|
+
每周四上线完通知修改jira上线日期、上线状态、自动化工作量填写 -- 每周四下午4点
|
263
|
+
1、显示jira状态
|
264
|
+
2、jira链接
|
265
|
+
3、测试人员
|
266
|
+
:return:
|
267
|
+
"""
|
268
|
+
content = ""
|
269
|
+
fixVersions = ""
|
270
|
+
for key, value in datas.items():
|
271
|
+
summary = value.get("summary")
|
272
|
+
url = value.get("url")
|
273
|
+
tester_user_id = self.get_tester_wecom_userid(value.get("测试人员"), test_user)
|
274
|
+
status = value.get("status")
|
275
|
+
actually_online_time = value.get("实际上线时间")
|
276
|
+
automated_testing_workload = value.get("自动化测试工作量")
|
277
|
+
automation_saves_labor_time = value.get("自动化节省工时")
|
278
|
+
fixVersions = value.get("fixVersions")
|
279
|
+
content += f"> [{key + '-' + summary}]({url}) \n状态:{status}\n实际上线时间:{actually_online_time}\n自动化测试工作量:{automated_testing_workload}\n自动节省工时:{automation_saves_labor_time}\n" \
|
280
|
+
f"测试:{tester_user_id} \n\n "
|
281
|
+
content = f"### <font color=\"warning\"> 版本{fixVersions}需求已上线,请更新jira数据,进行版本收尾</font> \n" + content
|
282
|
+
self.send_wecom_robot_message(webhook, content)
|
283
|
+
|
284
|
+
def get_tester_wecom_userid(self, tester, user_data):
|
285
|
+
"""
|
286
|
+
获取名字用于群里@使用
|
287
|
+
:param user_data: 用户id映射关系
|
288
|
+
:param tester: 传名字
|
289
|
+
:param data:数据结构
|
290
|
+
:return:
|
291
|
+
"""
|
292
|
+
mobile_string = ""
|
293
|
+
for name in tester:
|
294
|
+
user_id = user_data.get(name, None)
|
295
|
+
mobile_string += f"<@{user_id}>"
|
296
|
+
return mobile_string
|
297
|
+
|
298
|
+
def is_bug_not_fixed(self, issuelinks) -> dict:
|
299
|
+
"""
|
300
|
+
查询未修复的bug
|
301
|
+
:param issuelinks:
|
302
|
+
:return:
|
303
|
+
"""
|
304
|
+
not_fixed_bug_dict = {}
|
305
|
+
not_fixed_status_list = ['待修复', '修复中', '待验证']
|
306
|
+
for issue in issuelinks:
|
307
|
+
if issue.get('type').get('id') == '10003': # bug类型
|
308
|
+
status_name = issue['inwardIssue']['fields']['status']['name']
|
309
|
+
bug_key = issue['inwardIssue']['key']
|
310
|
+
bug_name = issue['inwardIssue']['fields']['summary']
|
311
|
+
if status_name in not_fixed_status_list:
|
312
|
+
not_fixed_bug_dict[bug_key] = bug_name
|
313
|
+
return not_fixed_bug_dict
|
314
|
+
|
315
|
+
def send_wecom_robot_message(self, webhook_url, content, msg_type='markdown'):
|
316
|
+
headers = {
|
317
|
+
'Content-Type': 'application/json'
|
318
|
+
}
|
319
|
+
if msg_type == 'text':
|
320
|
+
data = {
|
321
|
+
"msgtype": "text",
|
322
|
+
"text": {
|
323
|
+
"content": content
|
324
|
+
}
|
325
|
+
}
|
326
|
+
elif msg_type == 'markdown':
|
327
|
+
data = {
|
328
|
+
"msgtype": "markdown",
|
329
|
+
"markdown": {
|
330
|
+
"content": content
|
331
|
+
}
|
332
|
+
}
|
333
|
+
elif msg_type == 'link':
|
334
|
+
if not isinstance(content, dict) or 'title' not in content or 'text' not in content or 'url' not in content:
|
335
|
+
raise ValueError("link类型消息content必须是包含title、text、url的字典")
|
336
|
+
data = {
|
337
|
+
"msgtype": "link",
|
338
|
+
"link": content
|
339
|
+
}
|
340
|
+
elif msg_type == 'file':
|
341
|
+
if not isinstance(content, dict) or 'media_id' not in content:
|
342
|
+
raise ValueError("file类型消息content必须是包含media_id的字典")
|
343
|
+
data = {
|
344
|
+
"msgtype": "file",
|
345
|
+
"file": content
|
346
|
+
}
|
347
|
+
elif msg_type == 'image':
|
348
|
+
if not isinstance(content, dict) or 'media_id' not in content:
|
349
|
+
raise ValueError("image类型消息content必须是包含media_id的字典")
|
350
|
+
data = {
|
351
|
+
"msgtype": "image",
|
352
|
+
"image": content
|
353
|
+
}
|
354
|
+
elif msg_type == 'news':
|
355
|
+
if not isinstance(content, list):
|
356
|
+
raise ValueError("news类型消息content必须是列表")
|
357
|
+
for item in content:
|
358
|
+
if not isinstance(item,
|
359
|
+
dict) or 'title' not in item or 'description' not in item or 'url' not in item or 'picurl' not in item:
|
360
|
+
raise ValueError("news类型消息content列表中的每个元素必须是包含title、description、url、picurl的字典")
|
361
|
+
data = {
|
362
|
+
"msgtype": "news",
|
363
|
+
"news": {
|
364
|
+
"articles": content
|
365
|
+
}
|
366
|
+
}
|
367
|
+
else:
|
368
|
+
raise ValueError("不支持的消息类型,请选择text、markdown、link、file、image、news")
|
369
|
+
|
370
|
+
try:
|
371
|
+
response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
|
372
|
+
response.raise_for_status()
|
373
|
+
return response.json()
|
374
|
+
except requests.RequestException as e:
|
375
|
+
print(f"请求出错: {e}")
|
376
|
+
return None
|
377
|
+
except ValueError as e:
|
378
|
+
print(f"解析响应出错: {e}")
|
379
|
+
return None
|
380
|
+
|
381
|
+
|
382
|
+
# if __name__ == '__main__':
|
383
|
+
# # jirainfo.product_and_ui_acceptance_notice(webhook_url, result) # 发验收
|
384
|
+
# # jirainfo.bug_not_closed_notice(webhook_url, result) # bug未修复
|
385
|
+
# # jirainfo.registration_coverage_notice(webhook_url, result) # 覆盖率
|
386
|
+
# # jirainfo.modify_the_online_status_of_jira_notice(webhook_url, result) # 覆盖率
|
@@ -123,4 +123,10 @@ class StringUtils:
|
|
123
123
|
:param suffix: 后缀字符串
|
124
124
|
:return: 如果以指定后缀结尾返回 True,否则返回 False
|
125
125
|
"""
|
126
|
-
return s.endswith(suffix) if isinstance(s, str) and isinstance(suffix, str) else False
|
126
|
+
return s.endswith(suffix) if isinstance(s, str) and isinstance(suffix, str) else False
|
127
|
+
|
128
|
+
@staticmethod
|
129
|
+
def truncate_string(input_string):
|
130
|
+
if len(input_string) > 30:
|
131
|
+
return input_string[:15]
|
132
|
+
return input_string
|
@@ -1,74 +0,0 @@
|
|
1
|
-
from jira import JIRA
|
2
|
-
import re
|
3
|
-
|
4
|
-
def get_jira_info(jira_user, jira_api_key):
|
5
|
-
"""
|
6
|
-
获取jira对象
|
7
|
-
@return:
|
8
|
-
"""
|
9
|
-
# api_key = "ATATT3xFfGF0JPzR4t2coi53yM2eKZfUy5eXJSbZHqWmbX9PzStyaNCM2lEjn_uP5TkOl_p4pqZCICH5ZqdWkdmfSxJmTsn6AOcU7I3vVWnDl0i1PRktMdSSqWs1yg1JVSVtlGCrMKfZaztsJOjAQsp3Jd6hHdKpB4A4nVBBkmB7sDOpsNbTNeY=910B5249"
|
10
|
-
|
11
|
-
# Jira 服务器的 URL
|
12
|
-
jira_url = "https://shopline.atlassian.net/"
|
13
|
-
# # Jira API 密钥
|
14
|
-
# jira_api_key = api_key
|
15
|
-
# # Jira 用户名
|
16
|
-
# jira_user = "lu.lu@shopline.com"
|
17
|
-
|
18
|
-
# 连接到 Jira 服务器
|
19
|
-
jira = JIRA(server=jira_url, basic_auth=(jira_user, jira_api_key))
|
20
|
-
return jira
|
21
|
-
|
22
|
-
|
23
|
-
def get_jira_prodcut(jira, project_key):
|
24
|
-
project = jira.project(str(project_key))
|
25
|
-
print(f"Project: {project.key} - {project.name}")
|
26
|
-
return project
|
27
|
-
|
28
|
-
|
29
|
-
def get_custom_fields(jira_obj, project_key='10559'):
|
30
|
-
"""
|
31
|
-
查询指定项目jira中的自定义字段,smartpush项目是 10559
|
32
|
-
@param project_id: 项目id
|
33
|
-
@param jira_obj: 对象
|
34
|
-
@return:
|
35
|
-
"""
|
36
|
-
all_fields = jira_obj.fields()
|
37
|
-
# print("all_fields:",all_fields)
|
38
|
-
custom_fields = {}
|
39
|
-
for field in all_fields:
|
40
|
-
try:
|
41
|
-
if field.get('custom'):
|
42
|
-
if field['scope']['project']['id'] == str(project_key):
|
43
|
-
custom_fields[field['id']] = field['name']
|
44
|
-
except:
|
45
|
-
continue
|
46
|
-
print("custom_fields:", custom_fields)
|
47
|
-
return custom_fields
|
48
|
-
|
49
|
-
|
50
|
-
def get_custom_fields_map(jira, project_key=10559):
|
51
|
-
# 获取项目的问题
|
52
|
-
issues = jira.search_issues(f"project={project_key}")
|
53
|
-
issue = jira.issue(issues[0])
|
54
|
-
custom_fields = get_custom_fields(jira, project_key)
|
55
|
-
print(custom_fields)
|
56
|
-
fields_map = {}
|
57
|
-
for field_name, field_value in issue.fields.__dict__.items():
|
58
|
-
try:
|
59
|
-
if field_name.startswith("customfield_"):
|
60
|
-
fields_map[custom_fields[field_name]] = field_value
|
61
|
-
print(f"Custom Field ID: {field_name}, NAME:{custom_fields[field_name]}, Value: {field_value}")
|
62
|
-
else:
|
63
|
-
fields_map[field_name] = field_value
|
64
|
-
print(f"ID: {field_name},Value: {field_value}")
|
65
|
-
except:
|
66
|
-
# raise
|
67
|
-
continue
|
68
|
-
print('fields_map:',fields_map)
|
69
|
-
if __name__ == '__main__':
|
70
|
-
api_key = "ATATT3xFfGF0JPzR4t2coi53yM2eKZfUy5eXJSbZHqWmbX9PzStyaNCM2lEjn_uP5TkOl_p4pqZCICH5ZqdWkdmfSxJmTsn6AOcU7I3vVWnDl0i1PRktMdSSqWs1yg1JVSVtlGCrMKfZaztsJOjAQsp3Jd6hHdKpB4A4nVBBkmB7sDOpsNbTNeY=910B5249"
|
71
|
-
# Jira 用户名
|
72
|
-
jira_user = "lu.lu@shopline.com"
|
73
|
-
jira = get_jira_info(jira_user, api_key)
|
74
|
-
get_custom_fields_map(jira)
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# -*- codeing = utf-8 -*-
|
2
|
-
# @Time :2025/2/20 00:27
|
3
|
-
# @Author :luzebin
|
4
|
-
import pandas as pd
|
5
|
-
|
6
|
-
from smartpush.export.basic.ExcelExportChecker import check_excel_all, read_excel_and_write_to_list, \
|
7
|
-
read_excel_from_oss, read_excel_csv_data, check_excel
|
8
|
-
from smartpush.export.basic.ReadExcel import read_excel_from_oss
|
9
|
-
from smartpush.export.basic.ReadExcel import read_excel_and_write_to_dict
|
10
|
-
from smartpush.export.basic.GetOssUrl import get_oss_address_with_retry, export_requestParam, import_requestParam
|
11
|
-
|
12
|
-
if __name__ == '__main__':
|
13
|
-
oss1 = "https://cdn.smartpushedm.com/material_ec2/2025-02-25/58c4a3a885884741b22380c360ac2894/【自动化导出】营销活动URL点击与热图.xlsx"
|
14
|
-
oss2 = "https://cdn.smartpushedm.com/material_ec2/2025-02-27/a5e18e3b3a83432daca871953cb8471b/【自动化导出】营销活动URL点击与热图.xlsx"
|
15
|
-
# # print(check_excel_all(oss1, oss1))
|
16
|
-
oss3 = "https://cdn.smartpushedm.com/material_ec2/2025-02-25/58c4a3a885884741b22380c360ac2894/【自动化导出】营销活动URL点击与热图.xlsx"
|
17
|
-
oss4 = "https://cdn.smartpushedm.com/material_ec2/2025-02-26/58cee630b4c84eec9572b867af4ce692/%E3%80%90%E8%87%AA%E5%8A%A8%E5%8C%96%E5%AF%BC%E5%87%BA%E3%80%91%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8URL%E7%82%B9%E5%87%BB%E4%B8%8E%E7%83%AD%E5%9B%BE.xlsx"
|
18
|
-
expected_oss = "https://cdn.smartpushedm.com/material_ec2/2025-02-26/757df7e77ce544e193257c0da35a4983/%E3%80%90%E8%87%AA%E5%8A%A8%E5%8C%96%E5%AF%BC%E5%87%BA%E3%80%91%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E6%95%B0%E6%8D%AE%E6%A6%82%E8%A7%88.xlsx"
|
19
|
-
actual_oss = "https://cdn.smartpushedm.com/material_ec2/2025-02-26/757df7e77ce544e193257c0da35a4983/%E3%80%90%E8%87%AA%E5%8A%A8%E5%8C%96%E5%AF%BC%E5%87%BA%E3%80%91%E8%90%A5%E9%94%80%E6%B4%BB%E5%8A%A8%E6%95%B0%E6%8D%AE%E6%A6%82%E8%A7%88.xlsx"
|
20
|
-
|
21
|
-
e_person_oss1 = "https://cdn.smartpushedm.com/material_ec2/2025-02-27/b48f34b3e88045d189631ec1f0f23d51/%E5%AF%BC%E5%87%BA%E5%85%A8%E9%83%A8%E5%AE%A2%E6%88%B7.csv"
|
22
|
-
a_person_oss2 = "https://cdn.smartpushedm.com/material_ec2/2025-02-27/c50519d803c04e3b9b52d9f625fed413/%E5%AF%BC%E5%87%BA%E5%85%A8%E9%83%A8%E5%AE%A2%E6%88%B7.csv"
|
23
|
-
host = "https://test.smartpushedm.com/api-em-ec2"
|
24
|
-
reqHeaders = {
|
25
|
-
'cookie': 'osudb_appid=SMARTPUSH;osudb_oar=#01#SID0000123BL6ciRHRKpvOm/vWT9OS9brpfhSErcOdgeXJc0RJFopg83z0N3RzDE4w2DE5cQj6ALkLP8vG6Rhs0sR7NfToZvCLWXdQtYk6DJoKe4tqdo4kNcIc9F5obzLuyRmwGy9CZKcg/bMlmNyDZwBI1SIO;osudb_subappid=1;osudb_uid=4213785247;ecom_http_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDM4NDI1NTIsImp0aSI6IjEwNGQwOTVjLTA3MDItNDI5MC1iZjQzLWQ4YTVhNjdmNDM2NSIsInVzZXJJbmZvIjp7ImlkIjowLCJ1c2VySWQiOiI0MjEzNzg1MjQ3IiwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImZlbGl4LnNoYW9Ac2hvcGxpbmVhcHAuY29tIiwidXNlclJvbGUiOiJvd25lciIsInBsYXRmb3JtVHlwZSI6Nywic3ViUGxhdGZvcm0iOjEsInBob25lIjoiIiwibGFuZ3VhZ2UiOiJ6aC1oYW5zLWNuIiwiYXV0aFR5cGUiOiIiLCJhdHRyaWJ1dGVzIjp7ImNvdW50cnlDb2RlIjoiQ04iLCJjdXJyZW5jeSI6IkpQWSIsImN1cnJlbmN5U3ltYm9sIjoiSlDCpSIsImRvbWFpbiI6InNtYXJ0cHVzaDQubXlzaG9wbGluZXN0Zy5jb20iLCJsYW5ndWFnZSI6ImVuIiwibWVyY2hhbnRFbWFpbCI6ImZlbGl4LnNoYW9Ac2hvcGxpbmUuY29tIiwibWVyY2hhbnROYW1lIjoiU21hcnRQdXNoNF9lYzJf6Ieq5Yqo5YyW5bqX6ZO6IiwicGhvbmUiOiIiLCJzY29wZUNoYW5nZWQiOmZhbHNlLCJzdGFmZkxhbmd1YWdlIjoiemgtaGFucy1jbiIsInN0YXR1cyI6MCwidGltZXpvbmUiOiJBc2lhL01hY2FvIn0sInN0b3JlSWQiOiIxNjQ0Mzk1OTIwNDQ0IiwiaGFuZGxlIjoic21hcnRwdXNoNCIsImVudiI6IkNOIiwic3RlIjoiIiwidmVyaWZ5IjoiIn0sImxvZ2luVGltZSI6MTc0MTI1MDU1MjI4Miwic2NvcGUiOlsiZW1haWwtbWFya2V0IiwiY29va2llIiwic2wtZWNvbS1lbWFpbC1tYXJrZXQtbmV3LXRlc3QiLCJlbWFpbC1tYXJrZXQtbmV3LWRldi1mcyIsImFwaS11Yy1lYzIiLCJhcGktc3UtZWMyIiwiYXBpLWVtLWVjMiIsImZsb3ctcGx1Z2luIl0sImNsaWVudF9pZCI6ImVtYWlsLW1hcmtldCJ9.SjeTCLaZqbEFEFNeKe_EjrwmR0LdEYO9697ymVNzf5Q;',
|
26
|
-
'Content-Type': 'application/json'}
|
27
|
-
# actual_oss = get_oss_address_with_retry(11911, host, reqHeaders, import_requestParam, is_import=True)
|
28
|
-
# actual_oss = get_oss_address_with_retry(11896, host, reqHeaders, export_requestParam)
|
29
|
-
# # res=read_excel_and_write_to_dict(read_excel_from_oss(actual_oss))
|
30
|
-
# # print(res)
|
31
|
-
# # print(read_excel_and_write_to_dict(read_excel_from_oss(oss1), type=".xlsx"))
|
32
|
-
# print(check_excel(check_type="all", actual_oss=actual_oss, expected_oss=expected_oss))
|
33
|
-
# print(check_excel_all(actual_oss=oss1, expected_oss=oss2,skiprows =1))
|
34
|
-
# print(check_excel_all(actual_oss=oss1, expected_oss=oss2, ignore_sort=0))
|
35
|
-
print(check_excel_all(actual_oss=a_person_oss2, expected_oss=e_person_oss1, check_type="including"))
|
36
|
-
# print(check_excel_all(actual_oss=e_person_oss1, expected_oss=a_person_oss2, check_type="person"))
|
37
|
-
# read_excel_csv_data(type=)
|
38
|
-
|
39
|
-
# flow_ex="https://cdn.smartpushedm.com/material_ec2/2025-02-20/ad9e1534b8134dd098e96813f17d4b4d/%E6%B5%8B%E8%AF%95flow%E6%95%B0%E6%8D%AE%E6%8A%A5%E5%91%8A%E5%AF%BC%E5%87%BA%E5%8B%BF%E5%8A%A8%E6%95%B0%E6%8D%AE%E6%A6%82%E8%A7%88.xlsx"
|
40
|
-
# flow_ac="https://cdn.smartpushedm.com/material_ec2/2025-03-04/0c8f919f28d4455f9908f905aada7efb/测试flow数据报告导出勿动数据概览.xlsx"
|
41
|
-
# print(check_excel_all(actual_oss=flow_ac, expected_oss=flow_ex, check_type="including",export_type="flow",skiprows=1))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|