addproject
This commit is contained in:
17
base_framework/.gitignore
vendored
Normal file
17
base_framework/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/.idea/workspace.xml
|
||||
**/pz_all_server_ip.ini
|
||||
**/*run.log*
|
||||
**/.__run.lock
|
||||
*/.idea
|
||||
**/.idea/*
|
||||
**/__pycache__/*
|
||||
**/*.pyc
|
||||
**/results/*
|
||||
**/db_config.ini/*
|
||||
*.pyc
|
||||
**/interface.txt
|
||||
**/case_result/*
|
||||
*.project
|
||||
*.txt
|
||||
*.json
|
||||
**/user.txt
|
||||
0
base_framework/__init__.py
Normal file
0
base_framework/__init__.py
Normal file
18
base_framework/base_config/config.ini
Normal file
18
base_framework/base_config/config.ini
Normal file
@@ -0,0 +1,18 @@
|
||||
# coding=utf-8
|
||||
|
||||
# QA环境
|
||||
[QA]
|
||||
zhyy = https://smart-management-api-dev.best-envision.com/admin-api
|
||||
zhyy_login = https://smart-management-api-dev.best-envision.com/admin-api/system/auth/login
|
||||
ZZYY = https://smart-management-api-dev.best-envision.com/admin-api
|
||||
|
||||
# uat环境
|
||||
[UAT]
|
||||
zhyy = https://smart-management-api-pre.best-envision.com/admin-api/
|
||||
zhyy_login = https://smart-management-api-pre.best-envision.com/admin-api/system/auth/login
|
||||
|
||||
|
||||
[team_user_list]
|
||||
ASTWB_owner = 18202810506
|
||||
谯新久 = 18202810506
|
||||
ASTWB_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3
|
||||
33
base_framework/base_config/config_hh_qa.ini
Normal file
33
base_framework/base_config/config_hh_qa.ini
Normal file
@@ -0,0 +1,33 @@
|
||||
# coding=utf-8
|
||||
[PostgreSQL]
|
||||
db_test_host = 39.170.26.156
|
||||
db_test_port = 8566
|
||||
db_test_dbname = smart_management_st
|
||||
db_test_user = sm_test_user
|
||||
db_test_password = Test@736141
|
||||
db_charset = utf8
|
||||
db_min_cached = 10
|
||||
db_max_cached = 20
|
||||
db_max_shared = 20
|
||||
db_max_connecyions = 100
|
||||
db_blocking = True
|
||||
db_max_usage = 5
|
||||
db_set_session = None
|
||||
|
||||
[jwadmin]
|
||||
tenant = 境外租户空间
|
||||
show_username = 超级管理员
|
||||
username = jwadmin
|
||||
password = 1qaz@WSX
|
||||
|
||||
[purchase]
|
||||
tenant = 境内组织
|
||||
show_username = 孙丽萍
|
||||
username = sunliping01
|
||||
password = 1qaz@WSX
|
||||
|
||||
[supply]
|
||||
tenant = 境内组织
|
||||
show_username = 东莞艾斯保健用品有限公司
|
||||
username = SU00014
|
||||
password = 1qaz#WSX
|
||||
19
base_framework/base_config/config_hh_sim.ini
Normal file
19
base_framework/base_config/config_hh_sim.ini
Normal file
@@ -0,0 +1,19 @@
|
||||
# coding=utf-8
|
||||
|
||||
[jwadmin]
|
||||
tenant = 境外租户空间
|
||||
show_username = 超级管理员
|
||||
username = jwadmin
|
||||
password = 1qaz@WSX
|
||||
|
||||
[purchase]
|
||||
tenant = 境内组织
|
||||
show_username = 孙丽萍
|
||||
username = sunliping01
|
||||
password = 1qaz@WSX
|
||||
|
||||
[supply]
|
||||
tenant = 境内组织
|
||||
show_username = 东莞艾斯保健用品有限公司
|
||||
username = SU00014
|
||||
password = 1qaz#WSX
|
||||
31
base_framework/base_config/current_pth.py
Normal file
31
base_framework/base_config/current_pth.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# coding=utf-8
|
||||
|
||||
import os
|
||||
import configparser
|
||||
|
||||
# 配置文件路径
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_PATH = os.path.abspath(os.path.join(HERE, '../../'))
|
||||
ASTWB_PATH = os.path.abspath(os.path.join(HERE, '../../ASTWB/library/Config/'))
|
||||
env_choose_path = os.path.join(HERE, 'env_choose.ini')
|
||||
config_choose_path = os.path.join(HERE, 'config.ini')
|
||||
la_config_path = os.path.join(HERE, '../platform_tools/jira_tools/la_config.ini')
|
||||
config_evn_path = os.path.join(HERE, 'current_pth.py')
|
||||
server_ip_path = os.path.join(HERE, 'server_ip.ini')
|
||||
log_path = os.path.join(ROOT_PATH, 'Log')
|
||||
# 根据启动参数来加载对应的配置文件
|
||||
cof = configparser.ConfigParser()
|
||||
cof.read(env_choose_path)
|
||||
current_business = cof['run_evn_name']['current_business'].lower()
|
||||
current_evn = cof['run_evn_name']['current_evn'].lower()
|
||||
config_file_path = os.path.join(HERE, 'config_{0}_{1}.ini'.format(current_business, current_evn))
|
||||
db_config_path = config_file_path
|
||||
|
||||
pz_all_server_ip_path = os.path.join(HERE, 'pz_all_server_ip.ini')
|
||||
swagger_choose_path = os.path.join(HERE, 'swagger_url.ini')
|
||||
astwb_config = os.path.join(ASTWB_PATH, 'team_config.ini')
|
||||
uat_config_path = os.path.abspath(os.path.join(HERE, 'uat_config'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(config_file_path)
|
||||
45
base_framework/base_config/db_config.ini
Normal file
45
base_framework/base_config/db_config.ini
Normal file
@@ -0,0 +1,45 @@
|
||||
[Mysql]
|
||||
db_test_host = mysql.qa.huohua.cn
|
||||
db_all_school_test_host = qa-my-asc.mysql.rds.aliyuncs.com
|
||||
db_hhi_test_host = qa-my-hhi.mysql.rds.aliyuncs.com
|
||||
db_test_port = 3306
|
||||
db_test_dbname = crmthirdparty
|
||||
db_all_school_test_dbname = hulk_content_audit
|
||||
db_hhi_test_dbname = hhi_account
|
||||
db_test_user = qa-dev
|
||||
db_test_password = jaeg3SCQt0
|
||||
db_charset = utf8
|
||||
db_min_cached = 10
|
||||
db_max_cached = 20
|
||||
db_max_shared = 20
|
||||
db_max_connecyions = 100
|
||||
db_blocking = True
|
||||
db_max_usage = 5
|
||||
db_set_session = None
|
||||
|
||||
|
||||
|
||||
[MONGODB]
|
||||
mongo_host = 10.250.100.106
|
||||
mongo_port = 20007
|
||||
mongo_user = hulk_teach_marketing_rw
|
||||
mongo_passowrd = MmEzZmqatest
|
||||
|
||||
[PgSql]
|
||||
db_test_host = mysql.qa.huohua.cn
|
||||
db_all_school_test_host = qa-my-asc.mysql.rds.aliyuncs.com
|
||||
db_hhi_test_host = qa-my-hhi.mysql.rds.aliyuncs.com
|
||||
db_test_port = 3306
|
||||
db_test_dbname = crmthirdparty
|
||||
db_all_school_test_dbname = hulk_content_audit
|
||||
db_hhi_test_dbname = hhi_account
|
||||
db_test_user = qa-dev
|
||||
db_test_password = jaeg3SCQt0
|
||||
db_charset = utf8
|
||||
db_min_cached = 10
|
||||
db_max_cached = 20
|
||||
db_max_shared = 20
|
||||
db_max_connecyions = 100
|
||||
db_blocking = True
|
||||
db_max_usage = 5
|
||||
db_set_session = None
|
||||
14
base_framework/base_config/env_choose.ini
Normal file
14
base_framework/base_config/env_choose.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
[run_evn_name]
|
||||
current_business = hh
|
||||
current_evn = qa
|
||||
current_team = ZZYY
|
||||
|
||||
[run_jira_id]
|
||||
huohua-podenv = qa
|
||||
|
||||
[is_ip_from_ini]
|
||||
is_ip_from_ini = false
|
||||
|
||||
[run_user_name]
|
||||
default_user = jwadmin
|
||||
|
||||
2
base_framework/base_config/swagger_url.ini
Normal file
2
base_framework/base_config/swagger_url.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
[ZZYY]
|
||||
zhyy = http://39.170.26.156:8380/swagger-ui/index.html#/
|
||||
1
base_framework/logs/database.log
Normal file
1
base_framework/logs/database.log
Normal file
@@ -0,0 +1 @@
|
||||
2026-01-22 11:35:43,802 - database - ERROR - <20><><EFBFBD>ݿ<EFBFBD><DDBF><EFBFBD><EFBFBD>ӳس<D3B3>ʼ<EFBFBD><CABC>ʧ<EFBFBD><CAA7>: δ<>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ե<EFBFBD>·<EFBFBD><C2B7>: ['config/database.ini', '../config/database.ini', '../../config/database.ini', WindowsPath('C:/Users/hasee/.config/database.ini')]
|
||||
33
base_framework/main.py
Normal file
33
base_framework/main.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from robotremoteserver import RobotRemoteServer
|
||||
from library import KwLibrary
|
||||
import platform
|
||||
import os
|
||||
import signal
|
||||
|
||||
def main():
|
||||
RobotRemoteServer(library=KwLibrary(), host='0.0.0.0', port=9999)
|
||||
|
||||
def kill_pid():
|
||||
"""
|
||||
功能:杀掉旧的进程
|
||||
"""
|
||||
sys = platform.system()
|
||||
if sys == "Windows":
|
||||
with os.popen('netstat -aon|findstr "9999"') as res:
|
||||
res = res.read().split('\n')
|
||||
result = []
|
||||
for line in res:
|
||||
temp = [i for i in line.split(' ') if i != '']
|
||||
if len(temp) > 4 and temp[1] == "0.0.0.0:9999":
|
||||
result.append(
|
||||
{'pid': temp[4], 'address': temp[1], 'state': temp[3]})
|
||||
os.popen("taskkill -pid {} -f".format(temp[4]))
|
||||
else:
|
||||
pid = os.popen(
|
||||
"sudo -i netstat -nlp | grep :9999 | awk '{print $7}' | awk -F '/' '{ print $1 }'",
|
||||
'r',
|
||||
1).read().strip('\n')
|
||||
if pid != "":
|
||||
os.kill(int(pid), signal.SIGKILL)
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 检查jenkins上的用例构建结果,并根据用例失败数量,将结果设置为通过或失败
|
||||
import sys
|
||||
import os
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
WORKSPACE = os.path.abspath(os.path.join(HERE, '../../../'))
|
||||
sys.path.append(WORKSPACE)
|
||||
from base_framework.public_tools.xml_file_api import XmlFileApi
|
||||
PROJECT_NAME = sys.argv[0]
|
||||
|
||||
# 指定 Robot Framework 输出文件的路径
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
output_file = os.path.abspath(os.path.join(HERE, '../../../Report/out/output.xml'))
|
||||
|
||||
# 解析输出文件, 获取失败用例数量
|
||||
xf = XmlFileApi(file_path=output_file)
|
||||
result = xf.get_contain_by_tag_names(tag_names='statistics/total/stat')
|
||||
failed_count = int(result[0]['attrib']['fail'])
|
||||
|
||||
# 按失败用例对应的qa人员标签,
|
||||
if failed_count > 0:
|
||||
result = xf.get_contain_by_tag_names(tag_names='statistics/tag/stat')
|
||||
qa_info = {}
|
||||
qa_failed_case_number = 0
|
||||
for item in result:
|
||||
if 'qa-' in item['text'].lower():
|
||||
fail_case = int(item['attrib']['fail'])
|
||||
if fail_case > 0:
|
||||
qa_info[item['text'][3:]] = fail_case
|
||||
qa_failed_case_number += fail_case
|
||||
if len(qa_info) > 0:
|
||||
print("失败用例对应的QA人员及失败用例数量:")
|
||||
for key, value in qa_info.items():
|
||||
print(f"{key}: {value}个")
|
||||
if failed_count > qa_failed_case_number:
|
||||
print(f"无qa标签:{failed_count - qa_failed_case_number}个")
|
||||
|
||||
# 根据失败用例数量设置构建结果
|
||||
if "-IT" in PROJECT_NAME: # it用例失败用例大于5个就标红
|
||||
if failed_count > 5:
|
||||
print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。")
|
||||
sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色
|
||||
elif failed_count > 0:
|
||||
print("测试用例小范围失败,但是不影响整体结果。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,但此时受jenkins构建后步骤限制,将展示为黄色
|
||||
else:
|
||||
print("测试用例执行成功,所有用例都通过了。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色
|
||||
else: # st和smoking有失败用例就标红
|
||||
if failed_count > 0:
|
||||
print(f"测试用例执行失败,共有 {failed_count} 个失败的用例。")
|
||||
sys.exit(1) # 退出码为1,表示构建失败,构建结果将展示为红色
|
||||
else:
|
||||
print("测试用例执行成功,所有用例都通过了。")
|
||||
sys.exit(0) # 退出码为0,表示构建成功,构建结果将展示为绿色
|
||||
68
base_framework/platform_tools/Jenkins/jenkins_api.py
Normal file
68
base_framework/platform_tools/Jenkins/jenkins_api.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# coding: utf-8
|
||||
from base_framework.public_tools.selenium_api import SeleniumWebUI
|
||||
import time
|
||||
|
||||
|
||||
class JenkinsAPI(SeleniumWebUI):
|
||||
def __init__(self):
|
||||
SeleniumWebUI.__init__(self)
|
||||
self.project_info = {}
|
||||
|
||||
def jenkins_login(self, u_name=None, u_pwd=None):
|
||||
""" 登陆jenkins """
|
||||
if not u_name:
|
||||
u_name = "admin"
|
||||
u_pwd = "admin"
|
||||
login_url = "http://39.170.26.156:8256/jenkins/login?from=%2Fjenkins%2F"
|
||||
self.web_open_url(url=login_url)
|
||||
self.web_send_keys(locator="//*[@id=\"j_username\"]", text=u_name)
|
||||
self.web_send_keys(locator="//*[@name=\"j_password\"]", text=u_pwd)
|
||||
self.web_click_element(locator="//*[@name=\"Submit\"]")
|
||||
time.sleep(1) # 切换到构建墙
|
||||
self.web_click_element(locator="//*[text()='1-用户产品组']")
|
||||
time.sleep(1) # 显示用例数量
|
||||
self.web_click_element(locator="//*[@title=\"Configure Build Monitor Settings\"]")
|
||||
time.sleep(1)
|
||||
self.web_click_checkbox(locator="//*[@id=\"settings-show-test-result\"]")
|
||||
self.web_click_checkbox(locator="//button[text()=\"Done\"]")
|
||||
|
||||
def jenkins_find_all_case_number(self):
|
||||
""" 从构建墙上读取用例数量 """
|
||||
elements = self.web_find_elements(locator="//li[@ng-repeat=\"project in jobs track by project.hashCode\"]")
|
||||
for element in elements:
|
||||
item = element.text.split('\n')
|
||||
case_number = item[1].split('/')
|
||||
self.project_info[item[0]] = case_number[1]
|
||||
|
||||
def jenkins_edit_project_config(self, **kwargs):
|
||||
""" 编辑工程配置信息 """
|
||||
for jp in self.project_info:
|
||||
case_number = int(self.project_info[jp])
|
||||
if jp == "TO-IT":
|
||||
jp = "TO-IT-TEST"
|
||||
elif jp == "TO-SMOKING":
|
||||
jp = "TO-SMOKING_NEW"
|
||||
project_url = "http://10.250.200.1:8080/jenkins/view/1-%E7%94%A8%E6%88%B7%E4%BA%A7%E5%93%81%E7%BB%84/" \
|
||||
"job/{}/configure".format(jp)
|
||||
if "SMOKING" in jp or "-ST" in jp:
|
||||
threshold = 99.999999 # 冒烟和ST用例需全部构建成功
|
||||
else:
|
||||
threshold = round((1-5.5/case_number)*100, 4) # 阈值
|
||||
self.web_open_url(url=project_url) # 进入设置页面
|
||||
self.web_send_keys(locator="//input[@name=\"_.unstableThreshold\"]", text=str(threshold))
|
||||
self.web_click_checkbox(locator="//button[text()=\"保存\"]")
|
||||
print("{}设置阈值{}完毕....".format(jp, threshold))
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ja = JenkinsAPI()
|
||||
ja.jenkins_login()
|
||||
time.sleep(3)
|
||||
ja.jenkins_find_all_case_number()
|
||||
ja.jenkins_edit_project_config()
|
||||
time.sleep(3)
|
||||
ja.web_close_browser()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
special_team = ["EN", "PZ", "ASTOP", "ASOPE", "ASTWB", "ASORG", "UBRD", "UBJ", "ES", "ASTEA", 'GUE', "TMO", "PB"]
|
||||
@@ -0,0 +1,684 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import importlib.util as i_util
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from base_framework.platform_tools.Keywords_service import special_team
|
||||
|
||||
token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api", "peppa-la-core-server"]
|
||||
add_api_list = ["peppa-teach-api"]
|
||||
host_servers = ["HULK-ORG-API", "HULK-TEACHER-API", "PEPPA-LA-CORE-SERVER"]
|
||||
host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"]
|
||||
header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"]
|
||||
uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API', 'HUOHUA-SERVICE-API']
|
||||
eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER']
|
||||
|
||||
def check_server_host(servers, host):
|
||||
"""
|
||||
检查不需要token的server是否存在url中
|
||||
"""
|
||||
for server in servers:
|
||||
if server in host:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
|
||||
db = MySqLHelper()
|
||||
log = get_logger()
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
classes = []
|
||||
cls_members = inspect.getmembers(module, inspect.isclass)
|
||||
for (name, _) in cls_members:
|
||||
classes.append(name)
|
||||
return classes
|
||||
|
||||
|
||||
def get_module_by_file_path(file_path):
|
||||
spec = importlib.util.spec_from_file_location("module_name", file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class SetAttr(object):
|
||||
|
||||
def __init__(self, paras):
|
||||
for key, values in paras.items():
|
||||
self._add_attr(key, values)
|
||||
|
||||
def _add_attr(self, key, value):
|
||||
self.__setattr__(key, value)
|
||||
|
||||
|
||||
class SwaggerDBInfo(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_team(teamname):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_id(swagger_id):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_info_by_url_and_type(request_url, request_type, interface_id):
|
||||
sql = '''select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and
|
||||
id = %s''' % (request_url, request_type, interface_id)
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_parameters_by_interface_id(interface_id):
|
||||
sql = """select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on
|
||||
a.id = b.parameter_id where a.interface_id = {} and a.is_need != 2 and b.type = 'request'""".format(
|
||||
interface_id)
|
||||
return db.select_all(sql=sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_request_demo_info_by_interface_id(interface_id):
|
||||
sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
|
||||
class KWFileOperation(object):
|
||||
"""
|
||||
操作xx_interface.py文件
|
||||
"""
|
||||
|
||||
def __init__(self, team):
|
||||
self.kw_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
self.all_keywords = []
|
||||
self.all_url = {}
|
||||
self.kw_name_with_url = {}
|
||||
self.runner = self._get_obj_runner()
|
||||
self.team = team
|
||||
self.kw_class = self._get_interface_class_name(self.kw_file_path)
|
||||
|
||||
def _clean_demo_to_less_key(self, demo):
|
||||
demo_result = copy.deepcopy(demo)
|
||||
for key, value in demo.items():
|
||||
if not value:
|
||||
demo_result.pop(key)
|
||||
lenth = len(demo_result)
|
||||
paras = "kwargs"
|
||||
if lenth == 1 and isinstance(list(demo_result.values())[0], list):
|
||||
paras = "args"
|
||||
return lenth, demo_result, paras
|
||||
|
||||
def _get_interface_class_name(self, file_path):
|
||||
module = get_module_by_file_path(file_path)
|
||||
classes = get_classes(module)
|
||||
for class_ in classes:
|
||||
if "interface" in class_.lower() and class_.lower().startswith(self.team.lower()):
|
||||
return class_
|
||||
|
||||
def _get_obj_runner(self):
|
||||
spec = i_util.spec_from_file_location("module_name", self.kw_file_path)
|
||||
module = i_util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
runner = inspect.getmembers(module)
|
||||
for module in runner:
|
||||
if module[0] == "obj_runner":
|
||||
return module[1]
|
||||
return runner
|
||||
|
||||
def _get_file_content(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as f:
|
||||
for line in f:
|
||||
yield line.strip("\n")
|
||||
|
||||
def re_write_all_keyword_to_py_file(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
index = len(all_content)
|
||||
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file1:
|
||||
for line in file1:
|
||||
if "if __name__" in line and "__main__" in line:
|
||||
index = all_content.index(line)
|
||||
break
|
||||
|
||||
for i in range(index - 1, 0, -1):
|
||||
s = all_content[i]
|
||||
if all_content[i] != "\n":
|
||||
index = i
|
||||
all_content.insert(i + 1, "\n")
|
||||
break
|
||||
a = all_content[i + 1]
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
file.writelines(all_content[:index + 1])
|
||||
|
||||
def get_all_exist_keywords(self):
|
||||
for item in self._get_file_content():
|
||||
kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def get_all_exist_keywords_info(self):
|
||||
for item in self._get_file_content():
|
||||
kw_match = re.search(r"(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def write_content_to_interface_file(self, content, file_path=None):
|
||||
if not file_path:
|
||||
file_path = self.kw_file_path
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def generate_all_keyword_info(self):
|
||||
"""
|
||||
获取interface文件内已有关键字的url请求地址和请求方法
|
||||
"""
|
||||
temp_dict = {}
|
||||
all_content = self._get_file_content()
|
||||
server = ""
|
||||
for line in all_content:
|
||||
line_new = line
|
||||
if "def kw_in_" in line:
|
||||
kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1)
|
||||
if "tools.get_container_ip_from_eureka" in line or "tool.get_container_ip_from_eureka" in line:
|
||||
server = list(re.search("\([\"\']?([\w\-_]+)[\"\']?|\=[\"\']?([\w\-_]+)[\"\']?", line).groups())
|
||||
server.remove(None)
|
||||
server = server[0]
|
||||
if "self." in line and "=" in line:
|
||||
temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"")
|
||||
if " url =" in line or "url=" in line:
|
||||
log.info(line)
|
||||
try:
|
||||
# urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups())
|
||||
match = re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line)
|
||||
urls = list(match.groups()) if match else []
|
||||
except Exception as err:
|
||||
traceback.print_exc()
|
||||
continue
|
||||
# urls.remove(None)
|
||||
urls = [url for url in urls if url is not None]
|
||||
if not urls:
|
||||
continue
|
||||
url = urls[0]
|
||||
if "\\" in line:
|
||||
line_new = all_content.__next__()
|
||||
try:
|
||||
host = re.search("obj_runner\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",")
|
||||
except:
|
||||
if "self." in line:
|
||||
if self.team.upper() in ["EN", "ASTWB", "ASOPE"]:
|
||||
host_header = "https://"
|
||||
else:
|
||||
host_header = "http://"
|
||||
server = re.search("self\.([\w\-_]+)", line_new).group(1).strip().strip(")").strip(",")
|
||||
server = server.replace("_", "-").lower()
|
||||
if self.team.upper() == 'TMO':
|
||||
host = host_header + "%s" % 'swagger' + ".qa.huohua.cn"
|
||||
elif server.upper() in host_servers:
|
||||
if server.upper() == "HULK-ORG-API":
|
||||
host = host_header + "%s" % server + ".qa.huohua.cn"
|
||||
else:
|
||||
host = host_header + "%s" % "api" + ".qa.allschool.com"
|
||||
else:
|
||||
if server.upper() in host_servers_except_api:
|
||||
host = host_header + "%s" % server + ".qa.allschool.com"
|
||||
else:
|
||||
host = host_header + "%s" % server + ".qa.huohua.cn"
|
||||
|
||||
if self.team.upper() == "TO":
|
||||
host = "teach_opt_host"
|
||||
if re.search("\{\}/\{\}", line) and "self." in line:
|
||||
para = re.search("(self\.[\w_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
url = temp_dict[para] + urls[0]
|
||||
if "?" in url:
|
||||
url = url.split("?")[0]
|
||||
# if url.endswith("/"):
|
||||
# url = url[:len(url) - 1]
|
||||
|
||||
if server:
|
||||
if self.team.upper() in special_team and self.team.upper() != 'TMO':
|
||||
if url.startswith("/"):
|
||||
url = host + "%s" % url
|
||||
else:
|
||||
url = host + "/%s" % url
|
||||
elif self.team.upper() == 'TMO':
|
||||
if url.startswith("/"):
|
||||
url = '%s/%s%s' % (host, server, url)
|
||||
else:
|
||||
url = '%s/%s/%s' % (host, server, url)
|
||||
else:
|
||||
try:
|
||||
if url.startswith("/"):
|
||||
url = eval("self.runner.%s" % host) + "/%s" % server + url
|
||||
else:
|
||||
url = eval("self.runner.%s" % host) + "/%s/" % server + url
|
||||
except Exception as err:
|
||||
url = "empty_url"
|
||||
else:
|
||||
try:
|
||||
if url.startswith("/"):
|
||||
url1 = eval("self.runner.%s" % host)
|
||||
url = url1 + url
|
||||
else:
|
||||
url = eval("self.runner.%s" % host) + url
|
||||
except Exception as err:
|
||||
url = "empty_url"
|
||||
|
||||
if "req_type=" in line:
|
||||
method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1)
|
||||
self.all_url[url] = [method, kw_name]
|
||||
server = ""
|
||||
return self.all_url
|
||||
|
||||
|
||||
class KWOperation(object):
|
||||
"""
|
||||
根据数据库内容生成对应的keywords
|
||||
"""
|
||||
|
||||
def __init__(self, team, kw_instance, server=None):
|
||||
self.team = team
|
||||
self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|"
|
||||
self.function_name = ""
|
||||
self.help_doc = None
|
||||
self.function_content = None
|
||||
self.has_kw_list = []
|
||||
self.function_list = []
|
||||
self.file = kw_instance
|
||||
self.all_keywords = self.file.get_all_exist_keywords()
|
||||
self.all_url = self.file.generate_all_keyword_info()
|
||||
self.kw_name = None
|
||||
self.kw_string = None
|
||||
self.kw_array = None
|
||||
self.server = server
|
||||
|
||||
def get_interface_demo(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id)
|
||||
|
||||
def get_interface_parameters(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id)
|
||||
|
||||
def get_swagger_info_by_id(self, swagger_id):
|
||||
return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id)
|
||||
|
||||
def _check_kw_exist(self, keywordname):
|
||||
if keywordname in self.all_keywords:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def generate_kw_name(self, interface_info, demo):
|
||||
"""
|
||||
生成对应关键字方法名称如kw_in_to_get_leads_get(**kwargs)
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
_, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
if "" in name_list:
|
||||
name_list.remove("")
|
||||
name_list.append(interface_info.name)
|
||||
for item in add_api_list:
|
||||
if item in interface_info.in_url:
|
||||
name_list[-1] = 'api_' + name_list[-1]
|
||||
count = -1
|
||||
self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower())
|
||||
while abs(count) <= len(name_list):
|
||||
if self._check_kw_exist(self.kw_name):
|
||||
if lenth > 1:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if self.team.upper() in ["TMO"]:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if demo_result:
|
||||
if isinstance(list(demo_result.values())[0], list):
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
self.all_keywords.append(self.kw_name)
|
||||
return True
|
||||
else:
|
||||
count -= 1
|
||||
self.kw_name += "_" + name_list[count]
|
||||
else:
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
return False
|
||||
|
||||
def generate_kw_doc(self, interface_info, parameters_list):
|
||||
"""
|
||||
根据接口参数信息生成对应的帮助文档
|
||||
"""
|
||||
|
||||
def set_lenth(para):
|
||||
return " " + str(para).strip() + " "
|
||||
def get_normal_value(normal_values):
|
||||
return normal_values.split(",")[0] if normal_values else 0
|
||||
|
||||
doc = []
|
||||
doc_string_head = " \"\"\""
|
||||
doc_string_table_head = " | {} | {} | {} | {} | {} |".format("请求参数名".ljust(30), "说明".ljust(15),
|
||||
"类型".ljust(12), "是否必填".ljust(8),
|
||||
"如无要求时的值".ljust(8))
|
||||
doc.append(doc_string_head)
|
||||
doc.append(" | 功能说明:| {} |".format(interface_info.interface_describe))
|
||||
doc.append(doc_string_table_head)
|
||||
# if self.team.upper() not in ["TMO"]:
|
||||
# doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"),
|
||||
# set_lenth("is_check默认空不校验返回,有值就校验返回"),
|
||||
# set_lenth("string"),
|
||||
# set_lenth("业务case的时候需要传入值"),
|
||||
# set_lenth("False")))
|
||||
for parameter in parameters_list:
|
||||
attr = SetAttr(parameter)
|
||||
if int(attr.in_body) != 0 and int(attr.is_need) != 2:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note),
|
||||
set_lenth(attr.type), set_lenth(attr.is_need),
|
||||
set_lenth(get_normal_value(attr.normal_values))))
|
||||
doc.append(doc_string_head)
|
||||
return "\n".join(doc)
|
||||
|
||||
def _generate_all_url_paramaters(self, url):
|
||||
"""
|
||||
拆解url生成host, 生成keyword名的相关名称和url中带{}的参数
|
||||
"""
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
search.pop(0)
|
||||
url_lasr_temp = url_last.split("/")
|
||||
url_lasr_temp.pop(1)
|
||||
url_last = '/'.join(url_lasr_temp)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return host, head_list, para_list, url_last
|
||||
|
||||
def _generate_url_host(self, host):
|
||||
"""
|
||||
根据host生成对应的url后缀,比如teach_opt_host
|
||||
如果需要将host替换成IP则生成对应的IP
|
||||
"""
|
||||
url_host = ""
|
||||
if 'peppa-cc-manage' in host:
|
||||
url_host = "cc_host"
|
||||
elif 'crmv2' in host or 'smm' in host or 'xxljob' in host:
|
||||
url_host = "crm_host"
|
||||
elif 'scm' in host:
|
||||
url_host = "scm_host"
|
||||
elif "la-gate" in host:
|
||||
url_host = "insights_host"
|
||||
elif 'la-ai-api-bg' in host or 'la-api-ai' in host:
|
||||
url_host = "spark_land_host"
|
||||
elif 'manage' in host:
|
||||
url_host = "manage_host"
|
||||
elif 'teach' in host:
|
||||
url_host = "teach_host"
|
||||
elif 'teach-opt-api' in host or 'sparkle-manage' in host:
|
||||
url_host = "teach_opt_host"
|
||||
elif 'opengalaxy' in host:
|
||||
url_host = "opengalaxy_host"
|
||||
elif 'employee-manage' in host:
|
||||
url_host = "ehr_host"
|
||||
elif 'peppa-agent-manage' in host:
|
||||
url_host = "hhr_host"
|
||||
elif 'peppa-agent-api' in host:
|
||||
url_host = "hhr_api_host"
|
||||
elif "teach-message-api" in host:
|
||||
url_host = "teacher_message_host"
|
||||
elif "swagger" in host:
|
||||
url_host = "swagger_host"
|
||||
elif "peppa-qi-api" in host:
|
||||
url_host = "qi_host"
|
||||
elif "scm-server" in host:
|
||||
url_host = "scm_server_host"
|
||||
elif "scm-biz-server" in host:
|
||||
url_host = "scm_biz_server_host"
|
||||
elif "cti-manage" in host:
|
||||
url_host = "cti_host"
|
||||
elif "peppa-conversion-api" in host:
|
||||
url_host = "uc_host"
|
||||
elif "la-api" in host:
|
||||
url_host = "la_api_host"
|
||||
elif "hulk_content_audit_server" in host:
|
||||
url_host = "hulk_content_audit"
|
||||
elif "ccwx-api" in host:
|
||||
url_host = "cc_weixin_host"
|
||||
|
||||
return url_host
|
||||
|
||||
def generate_function_url(self, interface_info, demo):
|
||||
"""
|
||||
生成对应的url
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
url_list = []
|
||||
user_kwargs = " user, kwargs = get_user(kwargs)"
|
||||
url_list.append(user_kwargs)
|
||||
check_json = " kwargs = convert_json(kwargs)"
|
||||
if paras_d == "args":
|
||||
check_json = " args = convert_json(args)"
|
||||
url_list.append(check_json)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
host = re.findall("[\w+-]+", host)
|
||||
url_host = self._generate_url_host(host)
|
||||
server = ""
|
||||
# if self.team.upper() in ["TMO"]:
|
||||
# url_list.append(" try:\n kwargs = eval(args[0])\n except:")
|
||||
# url_list.append(" try:\n if args[0] and isinstance(args[0], dict):")
|
||||
# url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " "))
|
||||
# url_list.append("except:".rjust(12 + len("except:"), " "))
|
||||
# url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " "))
|
||||
# url_last_tmp = url_last.split("/")
|
||||
# server = url_last_tmp.pop(1)
|
||||
# url_last = "/".join(url_last_tmp)
|
||||
# ip = " ip = tools.get_container_ip_from_eureka(\"%s\", need_jira_id=True)" % server
|
||||
# url_list.append(ip)
|
||||
# host = " obj_runner." + url_host + " = \"http://\" + " + "ip[\"container_ip\"]" + " + \":8080\""
|
||||
# url_list.append(host)
|
||||
if self.team.upper() in special_team:
|
||||
if self.server.upper() in host_servers or self.team.upper() in ["TMO"]:
|
||||
host_server = self.server.lower().replace("-", "_")
|
||||
else:
|
||||
host_server = host[1].replace("-", "_")
|
||||
url_host = "self.%s" % host_server
|
||||
else:
|
||||
if self.server.upper() in host_servers or self.team.upper() in ["TMO"]:
|
||||
host_server = self.server.lower().replace("-", "_")
|
||||
url_host = "self.%s" % host_server
|
||||
# if self.team.upper() == 'LALIVE' and 'la-api' in host:
|
||||
# url_last = url_last.replace("smart", "api/smart")
|
||||
if "PEPPA-QI-API".lower() in host:
|
||||
ip = " obj_runner.%s = \"http://{}:8080\".format(self.get_container_ip('PEPPA-QI-API'))" % url_host
|
||||
url_list.append(ip)
|
||||
if parameters:
|
||||
if lenth > 1:
|
||||
temp = 'kwargs.get("u")'
|
||||
else:
|
||||
temp = 'kwargs'
|
||||
url = " url = \"%s" + url_last + "\".format("
|
||||
for item in parameters:
|
||||
url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item))
|
||||
url += ") % "
|
||||
else:
|
||||
url = " url = \"%s" + url_last + "\" % "
|
||||
|
||||
if self.team.upper() in special_team or self.server.upper() in host_servers:
|
||||
url += url_host
|
||||
else:
|
||||
url += "obj_runner.%s" % url_host
|
||||
url_list.append(url)
|
||||
return "\n".join(url_list), server
|
||||
|
||||
def generate_function_body(self, interface_info, demo, parameters_all):
|
||||
"""
|
||||
生成关键字的body内容
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
func_body = []
|
||||
if re.search(r"\*(\w+)", self.function_name):
|
||||
paras = re.search(r"\*(\w+)", self.function_name).group(1)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
paras = "kwargs"
|
||||
content = ' obj_log.info("your input:{0}".format(%s))' % paras
|
||||
func_body.append(content)
|
||||
if self.kw_string:
|
||||
func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name"))
|
||||
if self.kw_array:
|
||||
func_body.append(" kwargs = eval(kwargs.pop('%s'))" % parameters_all[0].get("name"))
|
||||
else:
|
||||
paras = None
|
||||
if lenth > 1:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type
|
||||
if "d" in demo_result:
|
||||
resp = resp + 'json=kwargs.get("d"), '
|
||||
if "p" in demo_result:
|
||||
resp = resp + 'params=kwargs.get("p"), '
|
||||
if "h" in demo_result:
|
||||
resp = resp + 'headers=kwargs.get("h"), '
|
||||
resp = resp + 'user=user)'
|
||||
else:
|
||||
if demo_result:
|
||||
if "d" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "h" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "p" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "u" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
else:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
|
||||
if self.team.upper() in special_team or check_server_host(token_false_server, host) or \
|
||||
self.team.upper() in ["TMO"]:
|
||||
resp = resp.replace("user=user", "token=False")
|
||||
try:
|
||||
if self.server.upper() in host_servers:
|
||||
resp = resp.replace("token=False", "token=False, user=user")
|
||||
if self.server.upper() == "HULK-ORG-API":
|
||||
resp = resp.replace("user=user", "user=user, as_login_type=2")
|
||||
if self.server.upper() in host_servers_except_api:
|
||||
resp = resp.replace("token=False", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
if str(self.server).lower() in header_servers:
|
||||
header = " header = {'debug-param': 'huangliye@huohua.cn'}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in eid_server:
|
||||
header = " header = {'debug-param': 'eid:%s' % self.eid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in uid_server:
|
||||
header = " header = {'debug-param': 'uid:%s' % self.uid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
func_body.append(resp)
|
||||
# if self.team.upper() not in ["TMO"] and paras_d == "kwargs":
|
||||
# func_body.append(" check_resp(is_check, resp)")
|
||||
func_body.append(" return resp\n")
|
||||
|
||||
return "\n".join(func_body)
|
||||
|
||||
def get_has_keyword_id_in_db(self):
|
||||
"""
|
||||
获取所有已有
|
||||
"""
|
||||
keyword_list = []
|
||||
for key in self.all_url.keys():
|
||||
interface_name = key.split("/")[-1]
|
||||
sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % (
|
||||
interface_name, self.all_url[key][0])
|
||||
interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql)
|
||||
for interface_info in interface_infos:
|
||||
interface_attr = SetAttr(interface_info)
|
||||
if key.lower() in interface_attr.in_url.lower():
|
||||
keyword_list.append(interface_attr.id)
|
||||
|
||||
return keyword_list
|
||||
|
||||
|
||||
def run_keyword_generage(result_path, interface, demo_info, parameters, keyword):
|
||||
if demo_info.request_demo:
|
||||
demo_info = json.loads(demo_info.request_demo)
|
||||
else:
|
||||
demo_info = {}
|
||||
if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[
|
||||
0].lower() == interface.type.lower():
|
||||
msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return True
|
||||
else:
|
||||
exist = keyword.generate_kw_name(interface, demo_info)
|
||||
if exist:
|
||||
keyword.function_list = []
|
||||
doc = keyword.generate_kw_doc(interface, parameters)
|
||||
url, temp = keyword.generate_function_url(interface, demo_info)
|
||||
content = keyword.generate_function_body(interface, demo_info, parameters)
|
||||
keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content)))
|
||||
keyword.file.re_write_all_keyword_to_py_file()
|
||||
keyword.file.write_content_to_interface_file("\n".join(keyword.function_list))
|
||||
keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name]
|
||||
return True
|
||||
else:
|
||||
msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return False
|
||||
@@ -0,0 +1,666 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import importlib.util as i_util
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from base_framework.platform_tools.Keywords_service import special_team
|
||||
|
||||
token_false_server = ["scm-server", "scm-biz-server", "peppa-qi-api", "lggzt", "cti-manage", "ccwx-api"]
|
||||
add_api_list = ["peppa-teach-api"]
|
||||
host_servers = ["HULK-ORG-API", "HULK-TEACHER-API"]
|
||||
host_servers_except_api = ["HULK-CONTENT-AUDIT-SERVER"]
|
||||
header_servers = ["hulk-operation-api-server", "hulk-teach-supply-cli-api", "hulk-teach-backend-api"]
|
||||
uid_server = ['SPARKEDU-API', 'SPARKEDU-SITE-API']
|
||||
eid_server = ['SPARKEDU-SITE-MANAGE', 'SPARKEDU-SITE-SCHEDULER']
|
||||
|
||||
def check_server_host(servers, host):
|
||||
"""
|
||||
检查不需要token的server是否存在url中
|
||||
"""
|
||||
for server in servers:
|
||||
if server in host:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
|
||||
db = MySqLHelper()
|
||||
log = get_logger()
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
classes = []
|
||||
cls_members = inspect.getmembers(module, inspect.isclass)
|
||||
for (name, _) in cls_members:
|
||||
classes.append(name)
|
||||
return classes
|
||||
|
||||
|
||||
def get_module_by_file_path(file_path):
|
||||
spec = importlib.util.spec_from_file_location("module_name", file_path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class SetAttr(object):
|
||||
|
||||
def __init__(self, paras):
|
||||
for key, values in paras.items():
|
||||
self._add_attr(key, values)
|
||||
|
||||
def _add_attr(self, key, value):
|
||||
self.__setattr__(key, value)
|
||||
|
||||
|
||||
class SwaggerDBInfo(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_team(teamname):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where team = '%s'" % teamname
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_team_swagger_info_by_id(swagger_id):
|
||||
sql = "SELECT * FROM sparkatp.swagger_info where id = '%s'" % swagger_id
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_info_by_url_and_type(request_url, request_type, team):
|
||||
sql = 'select * from sparkatp.interface_info where in_url = "%s" and type = "%s" and swagger_id in (select id from sparkatp.swagger_info where team = "%s")' % (request_url, request_type, team)
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_parameters_by_interface_id(interface_id):
|
||||
sql = "select * from sparkatp.request_parameters a right join sparkatp.parameters_relation b on a.id = b.parameter_id where a.interface_id = {} and b.type = 'request' and a.offline=0".format(
|
||||
interface_id)
|
||||
return db.select_all(sql=sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_interface_request_demo_info_by_interface_id(interface_id):
|
||||
sql = 'SELECT * FROM sparkatp.parameters_demo where in_id = "%s" order by id DESC' % interface_id
|
||||
return db.select_all(sql, choose_db="huohua")
|
||||
|
||||
@staticmethod
|
||||
def get_swagger_url_by_team_and_server(team, server):
|
||||
sql = "select * from sparkatp.swagger_info where team='%s' and server_name = '%s';" % (team, server)
|
||||
return db.select_one(sql, choose_db="huohua")
|
||||
|
||||
|
||||
class KWFileOperation(object):
|
||||
"""
|
||||
操作xx_interface.py文件
|
||||
"""
|
||||
|
||||
def __init__(self, team, server):
|
||||
self.kw_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
self.team = team
|
||||
self.server = server
|
||||
self.all_url = {}
|
||||
self.all_keywords = []
|
||||
self.runner = self._get_obj_runner()
|
||||
self.file_content = self.re_write_all_keyword_to_py_file()
|
||||
self.check_server_and_rewrite_server()
|
||||
self.get_all_exist_keywords()
|
||||
self.generate_all_keyword_info()
|
||||
self.kw_class = self._get_interface_class_name(self.kw_file_path)
|
||||
|
||||
def check_server_and_rewrite_server(self):
|
||||
"""
|
||||
检查server是否在interface.py文件中配置获取IP
|
||||
"""
|
||||
start_index = 0
|
||||
empty_index = 0
|
||||
server_host = self.server.lower().replace('-', '_')
|
||||
server_upper = self.server.upper()
|
||||
if "self.%s =" % server_host in ''.join(self.file_content) or '':
|
||||
return
|
||||
for line in self.file_content:
|
||||
if "def __init__(self):" in line:
|
||||
start_index = self.file_content.index(line) + 1
|
||||
log.info(start_index)
|
||||
if start_index == 0:
|
||||
continue
|
||||
if " def " in line and "__init__" not in line:
|
||||
end_index = self.file_content.index(line)
|
||||
log.info(end_index)
|
||||
break
|
||||
if "pass" in self.file_content[start_index]:
|
||||
self.file_content[
|
||||
start_index] = ' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n'
|
||||
elif 'self.need_jira_id = True if ' not in self.file_content[start_index] and "pass" in self.file_content[
|
||||
start_index]:
|
||||
self.file_content.insert(start_index,
|
||||
' self.need_jira_id = True if ReadConfig(env_choose_path).get_value("run_jira_id", "huohua-podenv") else False\n')
|
||||
for item in range(start_index, end_index):
|
||||
if self.file_content[item] == "\n":
|
||||
self.file_content.insert(item,
|
||||
" self.%s_ip = obj_tool.get_container_ip_from_eureka('%s', need_jira_id=self.need_jira_id)\n" % (
|
||||
server_host, server_upper))
|
||||
|
||||
self.file_content.insert(item + 1,
|
||||
' self.%s = "http://" + self.%s_ip["container_ip"] + ":8080"\n' % (
|
||||
server_host, server_host))
|
||||
break
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
for line1 in self.file_content:
|
||||
file.writelines(line1)
|
||||
|
||||
|
||||
def _clean_demo_to_less_key(self, demo):
|
||||
demo_result = copy.deepcopy(demo)
|
||||
for key, value in demo.items():
|
||||
if not value:
|
||||
demo_result.pop(key)
|
||||
lenth = len(demo_result)
|
||||
paras = "kwargs"
|
||||
if lenth == 1 and isinstance(list(demo_result.values())[0], list):
|
||||
paras = "args"
|
||||
return lenth, demo_result, paras
|
||||
|
||||
def _get_interface_class_name(self, file_path):
|
||||
module = get_module_by_file_path(file_path)
|
||||
classes = get_classes(module)
|
||||
for class_ in classes:
|
||||
if "interface" in class_.lower():
|
||||
return class_
|
||||
|
||||
def _get_obj_runner(self):
|
||||
spec = i_util.spec_from_file_location("module_name", self.kw_file_path)
|
||||
module = i_util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
runner = inspect.getmembers(module)
|
||||
for module in runner:
|
||||
if module[0] == "obj_runner":
|
||||
return module[1]
|
||||
return runner
|
||||
|
||||
def _get_file_content(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
|
||||
return all_content
|
||||
|
||||
def re_write_all_keyword_to_py_file(self):
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file:
|
||||
all_content = file.readlines()
|
||||
index = len(all_content)
|
||||
|
||||
with open(self.kw_file_path, "r", encoding="UTF-8") as file1:
|
||||
for line in file1:
|
||||
if "if __name__" in line and "__main__" in line:
|
||||
index = all_content.index(line)
|
||||
break
|
||||
|
||||
for i in range(index - 1, 0, -1):
|
||||
s = all_content[i]
|
||||
if all_content[i] != "\n":
|
||||
index = i
|
||||
all_content.insert(i + 1, "\n")
|
||||
break
|
||||
a = all_content[i + 1]
|
||||
with open(self.kw_file_path, "w", encoding="UTF-8") as file:
|
||||
file.writelines(all_content[:index + 1])
|
||||
return all_content[:index + 1]
|
||||
|
||||
def get_all_exist_keywords(self):
|
||||
for item in self.file_content:
|
||||
kw_match = re.search(r"def\s+(kw_in{1}_\w+)\(", item)
|
||||
if kw_match:
|
||||
self.all_keywords.append(kw_match.group(1))
|
||||
return self.all_keywords
|
||||
|
||||
def write_content_to_interface_file(self, content, file_path=None):
|
||||
if not file_path:
|
||||
file_path = self.kw_file_path
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def generate_all_keyword_info(self):
|
||||
"""
|
||||
获取interface文件内已有关键字的url请求地址和请求方法
|
||||
"""
|
||||
temp_dict = {}
|
||||
all_content = self.file_content
|
||||
server = False
|
||||
kw_line = False
|
||||
url_line = False
|
||||
host = False
|
||||
for line in all_content:
|
||||
if "def kw_in_" in line:
|
||||
kw_line = True
|
||||
kw_name = re.search("(kw_in_[\w+_]+)\(", line).group(1)
|
||||
if kw_line:
|
||||
try:
|
||||
if "get_container_ip" in line:
|
||||
server = list(re.search(r'get_container_ip.*\([\'\"]([\w\-_]+)[\'\"]', line).groups())
|
||||
server = server[0]
|
||||
except Exception as err:
|
||||
pass
|
||||
if "self." in line and "=" in line:
|
||||
temp_dict[line.split("=")[0].strip()] = line.split("=")[1].strip().strip("\"")
|
||||
if (" url =" in line or "url=" in line) and 'url +' not in line:
|
||||
log.info(line)
|
||||
try:
|
||||
urls = list(re.search(r"%s(/[\w+-_\}\{]+/?)\??|\}(/[\w+-_\}\{]+/?)\??", line).groups())
|
||||
urls.remove(None)
|
||||
except Exception as err:
|
||||
log.info("error: %s" % line)
|
||||
url_line = True
|
||||
if url_line:
|
||||
if 'url = "%s/api/customer/{customerId}/give_up"' in line:
|
||||
print()
|
||||
if not host:
|
||||
try:
|
||||
host_key = re.search("obj_runner\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
host = eval("self.runner.%s" % host_key)
|
||||
except:
|
||||
if not server:
|
||||
if "self." in line:
|
||||
server = re.search("self\.([\w\-_]+)", line).group(1).strip().strip(")").strip(",")
|
||||
server = server.replace("_", "-").lower()
|
||||
if server:
|
||||
try:
|
||||
host_temp = SwaggerDBInfo.get_swagger_url_by_team_and_server(self.team, server)['sw_url']
|
||||
host = host_temp.split("/v2")[0]
|
||||
except Exception as err:
|
||||
host = ""
|
||||
if host:
|
||||
url = host + urls[0]
|
||||
|
||||
if "req_type=" in line:
|
||||
method = re.search('req_type="(\w+)"|req_type=\'(\w+)\'', line.replace("\'", "\"")).group(1)
|
||||
self.all_url[url] = [method, kw_name]
|
||||
server = False
|
||||
kw_line = False
|
||||
url_line = False
|
||||
host = False
|
||||
return self.all_url
|
||||
|
||||
|
||||
class KWOperation(object):
|
||||
"""
|
||||
根据数据库内容生成对应的keywords
|
||||
"""
|
||||
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.doc_string_table_content_temp = " |{}|{}|{}|{}|{}|"
|
||||
self.function_name = ""
|
||||
self.help_doc = None
|
||||
self.function_content = None
|
||||
self.has_kw_list = []
|
||||
self.function_list = []
|
||||
self.file = kw_instance
|
||||
self.all_keywords = self.file.all_keywords
|
||||
self.all_url = self.file.all_url
|
||||
self.kw_name = None
|
||||
self.kw_string = None
|
||||
self.server = kw_instance.server
|
||||
|
||||
def get_interface_demo(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_request_demo_info_by_interface_id(interface_id)
|
||||
|
||||
def get_interface_parameters(self, interface_id):
|
||||
return SwaggerDBInfo.get_interface_parameters_by_interface_id(interface_id)
|
||||
|
||||
def get_swagger_info_by_id(self, swagger_id):
|
||||
return SwaggerDBInfo.get_team_swagger_info_by_id(swagger_id)
|
||||
|
||||
def _check_kw_exist(self, keywordname):
|
||||
if keywordname in self.all_keywords:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def generate_kw_name(self, interface_info, demo):
|
||||
"""
|
||||
生成对应关键字方法名称如kw_in_to_get_leads_get(is_check='', **kwargs)
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
_, name_list, _, _ = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
if "" in name_list:
|
||||
name_list.remove("")
|
||||
name_list.append(interface_info.name)
|
||||
for item in add_api_list:
|
||||
if item in interface_info.in_url:
|
||||
name_list[-1] = 'api_' + name_list[-1]
|
||||
count = -1
|
||||
self.kw_name = "kw_in_{}_{}_{}".format(self.team.lower(), name_list[-1], interface_info.type.lower())
|
||||
while abs(count) <= len(name_list):
|
||||
if self._check_kw_exist(self.kw_name):
|
||||
if lenth > 1:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if self.team.upper() in ["TMO"]:
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(kw_name=self.kw_name)
|
||||
else:
|
||||
if demo_result:
|
||||
if isinstance(list(demo_result.values())[0], list):
|
||||
self.function_name = " def {kw_name}(self, *args, **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
else:
|
||||
self.function_name = " def {kw_name}(self, is_check='', **kwargs):".format(
|
||||
kw_name=self.kw_name)
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
self.all_keywords.append(self.kw_name)
|
||||
return True
|
||||
else:
|
||||
count -= 1
|
||||
self.kw_name += "_" + name_list[count]
|
||||
else:
|
||||
self.has_kw_list.append(interface_info.id)
|
||||
return False
|
||||
|
||||
def generate_kw_doc(self, interface_info, parameters_list):
|
||||
"""
|
||||
根据接口参数信息生成对应的帮助文档
|
||||
"""
|
||||
|
||||
def set_lenth(para):
|
||||
return " " + str(para).strip() + " "
|
||||
|
||||
doc = []
|
||||
doc_string_head = " \"\"\""
|
||||
doc_string_table_head = " | {}| {}| {}| {}| {}|".format("请求参数名".ljust(30), "说明".ljust(15),
|
||||
"类型".ljust(12), "条件".ljust(10),
|
||||
"是否必填".ljust(8))
|
||||
doc.append(doc_string_head)
|
||||
doc.append(" {} + {} + interface id: {}".format(interface_info.interface_describe, interface_info.type,
|
||||
interface_info.id))
|
||||
doc.append(" url: " + interface_info.in_url)
|
||||
doc.append(doc_string_table_head)
|
||||
if self.team.upper() not in ["TMO"]:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth("is_check"),
|
||||
set_lenth("is_check默认空不校验返回,有值就校验返回"),
|
||||
set_lenth("string"),
|
||||
set_lenth("业务case的时候需要传入值"),
|
||||
set_lenth("False")))
|
||||
for parameter in parameters_list:
|
||||
attr = SetAttr(parameter)
|
||||
if int(attr.in_body) != 0 and int(attr.is_need) != 2:
|
||||
doc.append(self.doc_string_table_content_temp.format(set_lenth(attr.name), set_lenth(attr.note),
|
||||
set_lenth(attr.type), set_lenth(attr.p_condition),
|
||||
set_lenth(attr.is_need)))
|
||||
doc.append(doc_string_head)
|
||||
return "\n".join(doc)
|
||||
|
||||
def _generate_all_url_paramaters(self, url):
|
||||
"""
|
||||
拆解url生成host, 生成keyword名的相关名称和url中带{}的参数
|
||||
"""
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return host, head_list, para_list, url_last
|
||||
|
||||
def _generate_url_host(self):
|
||||
"""
|
||||
根据host生成对应的url后缀,比如teach_opt_host
|
||||
如果需要将host替换成IP则生成对应的IP
|
||||
"""
|
||||
server_temp = self.server.lower().replace('-', '_')
|
||||
url_host = 'self.%s' % server_temp
|
||||
# if self.server:
|
||||
# url_host = ""
|
||||
# url_host = ""
|
||||
# if 'peppa-cc-manage' in host:
|
||||
# url_host = "cc_host"
|
||||
# elif 'crmv2' in host or 'smm' in host or 'xxljob' in host:
|
||||
# url_host = "crm_host"
|
||||
# elif 'scm' in host:
|
||||
# url_host = "scm_host"
|
||||
# elif "la-gate" in host:
|
||||
# url_host = "insights_host"
|
||||
# elif 'la-ai-api-bg' in host or 'la-api-ai' in host:
|
||||
# url_host = "spark_land_host"
|
||||
# elif 'manage' in host:
|
||||
# url_host = "manage_host"
|
||||
# elif 'teach' in host:
|
||||
# url_host = "teach_host"
|
||||
# elif 'teach-opt-api' in host or 'sparkle-manage' in host:
|
||||
# url_host = "teach_opt_host"
|
||||
# elif 'opengalaxy' in host:
|
||||
# url_host = "opengalaxy_host"
|
||||
# elif 'employee-manage' in host:
|
||||
# url_host = "ehr_host"
|
||||
# elif 'peppa-agent-manage' in host:
|
||||
# url_host = "hhr_host"
|
||||
# elif 'peppa-agent-api' in host:
|
||||
# url_host = "hhr_api_host"
|
||||
# elif "teach-message-api" in host:
|
||||
# url_host = "teacher_message_host"
|
||||
# elif "swagger" in host:
|
||||
# url_host = "swagger_host"
|
||||
# elif "peppa-qi-api" in host:
|
||||
# url_host = "qi_host"
|
||||
# elif "scm-server" in host:
|
||||
# url_host = "scm_server_host"
|
||||
# elif "scm-biz-server" in host:
|
||||
# url_host = "scm_biz_server_host"
|
||||
# elif "cti-manage" in host:
|
||||
# url_host = "cti_host"
|
||||
# elif "peppa-conversion-api" in host:
|
||||
# url_host = "uc_host"
|
||||
# elif "la-api" in host:
|
||||
# url_host = "la_api_host"
|
||||
# elif "hulk_content_audit_server" in host:
|
||||
# url_host = "hulk_content_audit"
|
||||
|
||||
return url_host
|
||||
|
||||
def generate_function_url(self, interface_info, demo):
|
||||
"""
|
||||
生成对应的url
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
url_list = []
|
||||
user_kwargs = " user, kwargs = get_user(kwargs)"
|
||||
url_list.append(user_kwargs)
|
||||
check_json = " kwargs = convert_json(kwargs)"
|
||||
if paras_d == "args":
|
||||
check_json = " args = convert_json(args)"
|
||||
url_list.append(check_json)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
host = re.findall("[\w+-]+", host)
|
||||
url_host = self._generate_url_host()
|
||||
server = ""
|
||||
if self.team.upper() in ["TMO"]:
|
||||
url_list.append(" try:\n kwargs = eval(args[0])\n except:")
|
||||
url_list.append(" try:\n if args[0] and isinstance(args[0], dict):")
|
||||
url_list.append("kwargs = args[0]".rjust(20 + len("kwargs = args[0]"), " "))
|
||||
url_list.append("except:".rjust(12 + len("except:"), " "))
|
||||
url_list.append("kwargs = kwargs".rjust(16 + len("kwargs = kwargs"), " "))
|
||||
url_last_tmp = url_last.split("/")
|
||||
server = url_last_tmp.pop(1)
|
||||
url_last = "/".join(url_last_tmp)
|
||||
|
||||
if parameters:
|
||||
if lenth > 1:
|
||||
temp = 'kwargs.get("u")'
|
||||
else:
|
||||
temp = 'kwargs'
|
||||
url = " url = \"%s" + url_last + "\".format("
|
||||
for item in parameters:
|
||||
url += "%s=%s, " % (item, "%s.get(\"%s\", \"\")" % (temp, item))
|
||||
url += ") % "
|
||||
else:
|
||||
url = " url = \"%s" + url_last + "\" % "
|
||||
|
||||
url += url_host
|
||||
url_list.append(url)
|
||||
return "\n".join(url_list), server
|
||||
|
||||
def generate_function_body(self, interface_info, demo, parameters_all):
|
||||
"""
|
||||
生成关键字的body内容
|
||||
"""
|
||||
lenth, demo_result, paras_d = self.file._clean_demo_to_less_key(demo)
|
||||
host, _, parameters, url_last = self._generate_all_url_paramaters(interface_info.in_url)
|
||||
func_body = []
|
||||
if re.search(r"\*(\w+)", self.function_name):
|
||||
paras = re.search(r"\*(\w+)", self.function_name).group(1)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
paras = "kwargs"
|
||||
content = ' obj_log.info("your input:{0}".format(%s))' % paras
|
||||
func_body.append(content)
|
||||
if self.kw_string:
|
||||
func_body.append(" kwargs = kwargs.get('%s', '')" % parameters_all[0].get("name"))
|
||||
else:
|
||||
paras = None
|
||||
if lenth > 1:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", ' % interface_info.type
|
||||
if "d" in demo_result:
|
||||
resp = resp + 'json=kwargs.get("d"), '
|
||||
if "p" in demo_result:
|
||||
resp = resp + 'params=kwargs.get("p"), '
|
||||
if "h" in demo_result:
|
||||
resp = resp + 'headers=kwargs.get("h"), '
|
||||
resp = resp + 'user=user)'
|
||||
else:
|
||||
if demo_result:
|
||||
if "d" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", json=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "h" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", headers=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "p" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", params=%s, user=user)' % (
|
||||
interface_info.type, paras)
|
||||
elif "u" in demo_result:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
else:
|
||||
resp = ' resp = obj_runner.call_rest_api(API_URL=url, req_type="%s", user=user)' % (
|
||||
interface_info.type)
|
||||
|
||||
if self.team.upper() in special_team or check_server_host(token_false_server, host) or self.team.upper() in [
|
||||
"TMO"]:
|
||||
resp = resp.replace("user=user", "token=False")
|
||||
try:
|
||||
if self.server.upper() in host_servers:
|
||||
resp = resp.replace("token=False", "token=False, user=user")
|
||||
if self.server.upper() == "HULK-ORG-API":
|
||||
resp = resp.replace("user=user", "user=user, as_login_type=2")
|
||||
if self.server.upper() in host_servers_except_api:
|
||||
resp = resp.replace("token=False", "")
|
||||
except:
|
||||
pass
|
||||
|
||||
if str(self.server).lower() in header_servers:
|
||||
header = " header = {'debug-param': 'huangliye@huohua.cn'}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'huangliye@huohua.cn'})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in eid_server:
|
||||
header = " header = {'debug-param': 'eid:%s' % self.eid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'eid:%s' % self.eid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
if str(self.server).upper() in uid_server:
|
||||
header = " header = {'debug-param': 'uid:%s' % self.uid}"
|
||||
if "h" in demo_result:
|
||||
resp = resp.replace(' headers=kwargs.get("h"),', "")
|
||||
header = " header = kwargs.get('h')\n header.update({'debug-param': 'uid:%s' % self.uid})"
|
||||
func_body.append(header)
|
||||
resp = resp.replace("token=False", "token=False, headers=header")
|
||||
func_body.append(resp)
|
||||
if self.team.upper() not in ["TMO"] and paras_d == "kwargs":
|
||||
func_body.append(" check_resp(is_check, resp)")
|
||||
func_body.append(" return resp\n")
|
||||
|
||||
return "\n".join(func_body)
|
||||
|
||||
def get_has_keyword_id_in_db(self):
|
||||
"""
|
||||
获取所有已有
|
||||
"""
|
||||
keyword_list = []
|
||||
for key in self.all_url.keys():
|
||||
interface_name = key.split("/")[-1]
|
||||
sql = "select * from sparkatp.interface_info where name = '%s' and type = '%s'" % (
|
||||
interface_name, self.all_url[key][0])
|
||||
interface_infos = SwaggerDBInfo.get_db_info_by_sql(sql=sql)
|
||||
for interface_info in interface_infos:
|
||||
interface_attr = SetAttr(interface_info)
|
||||
if key.lower() in interface_attr.in_url.lower():
|
||||
keyword_list.append(interface_attr.id)
|
||||
|
||||
return keyword_list
|
||||
|
||||
|
||||
def run_keyword_generage(result_path, interface, demo_info, parameters, keyword):
|
||||
if demo_info.request_demo:
|
||||
demo_info = json.loads(demo_info.request_demo)
|
||||
else:
|
||||
demo_info = {}
|
||||
if keyword.all_url.get(interface.in_url) and keyword.all_url.get(interface.in_url)[
|
||||
0].lower() == interface.type.lower():
|
||||
msg = "接口url:%s,%s,已存在关键字,继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return True
|
||||
else:
|
||||
exist = keyword.generate_kw_name(interface, demo_info)
|
||||
if exist:
|
||||
keyword.function_list = []
|
||||
doc = keyword.generate_kw_doc(interface, parameters)
|
||||
url, temp = keyword.generate_function_url(interface, demo_info)
|
||||
content = keyword.generate_function_body(interface, demo_info, parameters)
|
||||
keyword.function_list.append("\n".join(("", keyword.function_name, doc, url, content)))
|
||||
keyword.file.re_write_all_keyword_to_py_file()
|
||||
keyword.file.write_content_to_interface_file("\n".join(keyword.function_list))
|
||||
keyword.file.all_url[interface.in_url] = [interface.type, keyword.kw_name]
|
||||
return True
|
||||
else:
|
||||
msg = "接口url:%s,%s,无法生成关键字,请检查!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
keyword.file.write_content_to_interface_file(msg + "\n", result_path)
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
kw = KWFileOperation("CC", "peppa-qi-api")
|
||||
kw.check_server_and_rewrite_server()
|
||||
3
base_framework/platform_tools/Keywords_service/readme
Normal file
3
base_framework/platform_tools/Keywords_service/readme
Normal file
@@ -0,0 +1,3 @@
|
||||
目录结构说明:
|
||||
使用者:王刚
|
||||
用途:存放自动生成py文件关键字的脚本
|
||||
141
base_framework/platform_tools/Message_service/Feishu_api.py
Normal file
141
base_framework/platform_tools/Message_service/Feishu_api.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
功能:发送飞书消息接口
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
import urllib3
|
||||
urllib3.disable_warnings()
|
||||
import os
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
|
||||
try:
|
||||
JSONDecodeError = json.decoder.JSONDecodeError
|
||||
except AttributeError:
|
||||
JSONDecodeError = ValueError
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
msg_config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'msg_config.ini')
|
||||
read_config = ReadConfig(filename=msg_config_path)
|
||||
|
||||
|
||||
def get_feishu_config_options():
|
||||
"""
|
||||
功能:获取飞书配置文件信息
|
||||
option_key:配置文件中的key
|
||||
"""
|
||||
return read_config.get_options('feishu_user_list')
|
||||
|
||||
|
||||
def get_feishu_config_value(option_key):
|
||||
"""
|
||||
功能:获取飞书配置文件信息
|
||||
option_key:配置文件中的key
|
||||
"""
|
||||
return read_config.get_value(sections='feishu_user_list', options=option_key)
|
||||
|
||||
def get_user_name_by_email_prefix(email_prefix):
|
||||
"""
|
||||
功能:获取飞书配置文件中的邮箱前缀与中文姓名的对应关系
|
||||
email_prefix:邮箱前缀
|
||||
返回:对应的中文姓名
|
||||
"""
|
||||
return read_config.get_value(sections='user_name', options=email_prefix)
|
||||
|
||||
|
||||
class FeiShuMessage(object):
|
||||
def __init__(self, team='TO', secret=None, pc_slide=False, fail_notice=False):
|
||||
"""
|
||||
机器人初始化
|
||||
:param team: 业务组名,用于从msg_config配置文件中读取对应群组的webhook地址
|
||||
:param secret: 机器人安全设置页面勾选“加签”时需要传入的密钥
|
||||
:param pc_slide: 消息链接打开方式,默认False为浏览器打开,设置为True时为PC端侧边栏打开
|
||||
:param fail_notice: 消息发送失败提醒,默认为False不提醒,开发者可以根据返回的消息发送结果自行判断和处理
|
||||
"""
|
||||
super(FeiShuMessage, self).__init__()
|
||||
self.headers = {'Content-Type': 'application/json; charset=utf-8'}
|
||||
team_name = team.lower() + '_webhook_token'
|
||||
if team_name in get_feishu_config_options():
|
||||
token = get_feishu_config_value(option_key=team_name)
|
||||
self.webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/{0}".format(token)
|
||||
else:
|
||||
self.webhook = None
|
||||
logging.error("Team: {} not in msg_config.ini".format(team))
|
||||
return
|
||||
self.secret = secret
|
||||
self.pc_slide = pc_slide
|
||||
self.fail_notice = fail_notice
|
||||
|
||||
def send_text(self, msg):
|
||||
"""
|
||||
消息类型为text类型
|
||||
:param msg: 消息内容
|
||||
:return: 返回消息发送结果
|
||||
"""
|
||||
data = {"msg_type": "text"}
|
||||
if msg and self.webhook: # 传入msg非空
|
||||
data["content"] = {"text": msg}
|
||||
if "全部通过" in msg:
|
||||
# 不用发飞书消息
|
||||
logging.info("+++++++++ 全部构建成功,不发消息 ++++++++")
|
||||
return False
|
||||
return self.post(data)
|
||||
|
||||
def post(self, data):
|
||||
"""
|
||||
发送消息(内容UTF-8编码)
|
||||
:param data: 消息数据(字典)
|
||||
:return: 返回消息发送结果
|
||||
"""
|
||||
try:
|
||||
post_data = json.dumps(data)
|
||||
response = requests.post(self.webhook, headers=self.headers, data=post_data, verify=False)
|
||||
except requests.exceptions.HTTPError as exc:
|
||||
logging.error("消息发送失败, HTTP error: %d, reason: %s" % (exc.response.status_code, exc.response.reason))
|
||||
raise
|
||||
except requests.exceptions.ConnectionError:
|
||||
logging.error("消息发送失败,HTTP connection error!")
|
||||
raise
|
||||
except requests.exceptions.Timeout:
|
||||
logging.error("消息发送失败,Timeout error!")
|
||||
raise
|
||||
except requests.exceptions.RequestException:
|
||||
logging.error("消息发送失败, Request Exception!")
|
||||
raise
|
||||
else:
|
||||
try:
|
||||
result = response.json()
|
||||
except JSONDecodeError:
|
||||
logging.error("服务器响应异常,状态码:%s,响应内容:%s" % (response.status_code, response.text))
|
||||
return {'errcode': 500, 'errmsg': '服务器响应异常'}
|
||||
else:
|
||||
logging.debug('发送结果:%s' % result)
|
||||
# 消息发送失败提醒(errcode 不为 0,表示消息发送异常),默认不提醒,开发者可以根据返回的消息发送结果自行判断和处理
|
||||
if self.fail_notice and result.get('errcode', True):
|
||||
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
|
||||
error_data = {
|
||||
"msgtype": "text",
|
||||
"text": {
|
||||
"content": "[注意-自动通知]飞书机器人消息发送失败,时间:%s,原因:%s,请及时跟进,谢谢!" % (
|
||||
time_now, result['errmsg'] if result.get('errmsg', False) else '未知异常')
|
||||
},
|
||||
"at": {
|
||||
"isAtAll": False
|
||||
}
|
||||
}
|
||||
logging.error("消息发送失败,自动通知:%s" % error_data)
|
||||
requests.post(self.webhook, headers=self.headers, data=json.dumps(error_data))
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
fs = FeiShuMessage(team='TO')
|
||||
吴勇刚 = "<at user_id='ou_94f57439f58bde9376189a3cabb0b11a'>吴勇刚</at>"
|
||||
刘明浩 = "<at user_id='ou_94f57439f58bde9376189a3cabb0b11b'>刘明浩</at>"
|
||||
陈慧宗 = "<at user_id='ou_bc2b266b05a428959bfcff3378af2d36'>陈慧宗</at>"
|
||||
message = "【TO-SMOKING_NEW】 第【1314】次构建结果:失败【5】个 \n|--{}: 1个\n|--{}: 2个\n|--{}: 3个" \
|
||||
"构建报告:...........test..........".format(吴勇刚, 刘明浩, 陈慧宗)
|
||||
resp = fs.send_text(msg=message)
|
||||
print(resp)
|
||||
101
base_framework/platform_tools/Message_service/check_jira.py
Normal file
101
base_framework/platform_tools/Message_service/check_jira.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# encoding: utf-8
|
||||
# @Time : 2021/12/20 18:14
|
||||
# @Author : yk
|
||||
# @Site :
|
||||
# @File : jira_message_by_ding.py
|
||||
|
||||
from jira import JIRA
|
||||
import json
|
||||
import requests
|
||||
import datetime
|
||||
import urllib.parse
|
||||
|
||||
|
||||
class JiraObj:
|
||||
def __init__(self, user, pwd, jira_addr):
|
||||
if not hasattr(JiraObj, 'jira'):
|
||||
JiraObj.jira_obj(user, pwd, jira_addr)
|
||||
|
||||
@staticmethod
|
||||
def jira_obj(user, pwd, jira_addr):
|
||||
JiraObj.jira = JIRA(auth=(user, pwd), options={'server': jira_addr})
|
||||
|
||||
@staticmethod
|
||||
def search_by_jql(jql, fields=None):
|
||||
return JiraObj.jira.search_issues(jql, fields=fields, maxResults=-1, json_result='true')
|
||||
|
||||
|
||||
|
||||
def get_delay_sub_task_creator(jira_obj, now_date):
|
||||
name_list="chengpu,fengtian,dengzhenbo,guosongchao,handongtang,huanghaifeng,lidaijun,wanglushun,wangyujie02,yangwenlei01,yangwenlei01,zhanghaodong,zhengxin01,zhuzipeng"
|
||||
# name_list="zhanghaodong"
|
||||
jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format(
|
||||
now_date,name_list)
|
||||
fields = 'assignee'
|
||||
print(jql)
|
||||
issue_list = jira_obj.search_by_jql(jql, fields).get('issues')
|
||||
print(issue_list)
|
||||
if issue_list:
|
||||
return list(map(lambda x: x.get('fields').get('assignee').get('name'), issue_list))
|
||||
else:
|
||||
return None
|
||||
def send_message_by_feishu(name, message):
|
||||
data={"list":[name]}
|
||||
a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data)
|
||||
print(a.text)
|
||||
open_id=a.text.split("open_id")[1].split('"')[2].split("\\")[0]
|
||||
|
||||
data={
|
||||
"app_id": "cli_a2d926427f39900d",
|
||||
"app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED"
|
||||
}
|
||||
a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data)
|
||||
|
||||
tenant_access_token=eval(a.text)["tenant_access_token"]
|
||||
data ={"msg_type": "text","content": { "text": "{}".format(message) }, "open_ids": [open_id]}
|
||||
|
||||
header={"Authorization":"Bearer {}".format(tenant_access_token),"Content-Type":"application/json; charset=utf-8"}
|
||||
|
||||
a=requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header)
|
||||
|
||||
print(a.text)
|
||||
if __name__ == '__main__':
|
||||
|
||||
now_date = datetime.datetime.now().strftime('%Y-%m-%d')
|
||||
|
||||
jira_obj = JiraObj('yaokun', 'Cyjayk1314', 'https://jira.bg.huohua.cn')
|
||||
creator = get_delay_sub_task_creator(jira_obj, now_date)
|
||||
|
||||
if creator:
|
||||
creator=list(set(creator))
|
||||
message="你还有未解决的bug,请及时解决,若已解决请及时更新jira状态"
|
||||
for i in creator:
|
||||
jql = 'issuetype = 缺陷 and createdDate < \'{} 18:00\' AND status not in (QA测试,SIM验证,Done,关闭,待测试) and assignee in ({})'.format(
|
||||
now_date, i)
|
||||
print(jql)
|
||||
i=i+"@sparkedu.com"
|
||||
|
||||
a=jira_obj.search_by_jql(jql, "assignee").get('issues')
|
||||
for j in a:
|
||||
print(j["key"])
|
||||
send_message_by_feishu(i,"你有未修复的bug请及时修复https://jira.bg.huohua.cn/browse/{}".format(j["key"]))
|
||||
# send_message_by_feishu("yaokun@sparkedu.com","https://jira.bg.huohua.cn/browse/HHC-50790")
|
||||
# data={"list":["yaokun@sparkedu.com"]}
|
||||
# a=requests.post(url="http://sqe.qc.huohua.cn:8082/feishu/getUserIds",data=data)
|
||||
#
|
||||
# print(a.text.split("open_id")[1].split('"')[2].split("\\")[0])
|
||||
# open_id="ou_95feb664191332a7916bb3b0d886f553"
|
||||
# print(a.text)
|
||||
# data={
|
||||
# "app_id": "cli_a2d926427f39900d",
|
||||
# "app_secret": "CvETCGh3rHu6CtcnxzaWK7rMnVgcSLED"
|
||||
# }
|
||||
# a=requests.post(url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",json=data)
|
||||
# print(a.text)
|
||||
#
|
||||
# data ={"msg_type": "text","content": { "text": "https://jira.bg.huohua.cn/browse/HHC-50790" }, "open_ids": [open_id]}
|
||||
#
|
||||
# header={"Authorization":"Bearer t-719d260f474e6a1a6b4a5d990a6bccec6fc3b17f","Content-Type":"application/json; charset=utf-8"}
|
||||
# print(data)
|
||||
# a = requests.post(url="https://open.feishu.cn/open-apis/message/v4/batch_send/", json=data,headers=header)
|
||||
# print(a.text)
|
||||
@@ -0,0 +1 @@
|
||||
# to be add 。。。。
|
||||
236
base_framework/platform_tools/Message_service/msg_config.ini
Normal file
236
base_framework/platform_tools/Message_service/msg_config.ini
Normal file
@@ -0,0 +1,236 @@
|
||||
# 此文档中配置钉钉,飞书群组和群用户信息
|
||||
[feishu_user_list]
|
||||
JENKINS_webhook_token = 6de2e886-10de-4a85-aa92-2d7446d0c315
|
||||
CDQA_webhook_token = 30e400ed-b802-4afb-b38c-2a0690c1a2a6
|
||||
TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
HHI_TO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
HHI_TMO_webhook_token = 2109acc2-6ffe-4038-8bf1-4bfcfcd4cb01
|
||||
GUE_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4
|
||||
LALIVE_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
H2R_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
CC_webhook_token = 3f9379c7-ebcd-4844-9c61-3f2642f64df4
|
||||
ASTWB_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ASOPE_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ASTOP_webhook_token = 31ab2e27-df7c-46d6-8294-afb98a68b6f0
|
||||
ES_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
SCM_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
UBRD_webhook_token = a46a610c-4c52-4abf-bcad-5fde5cd545e4
|
||||
DBSYNC_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7
|
||||
ODS_webhook_token = 054f4c3c-b38b-46f8-9cc4-d9660e205fc7
|
||||
AUTOMATION_webhook_token = ceb41bf3-f3ce-433f-bbea-2960bf46a819
|
||||
; TO-RD_webhook_token = d8d9ac4b-e5f9-47a6-8810-0ae6b621a7cb
|
||||
TO-RD_webhook_token = 2387b345-3675-49dd-971a-2f81b98e3ed1
|
||||
TO-FE_webhook_token = 1d0dac58-dd68-412d-a2eb-b445927339a
|
||||
GUE-RD_webhook_token = 8951949c-e300-4c96-a725-53346309851a
|
||||
USER-FE_webhook_token = 4f946e2b-0b58-4eb5-aa22-3d3516aee1ba
|
||||
SCM-RD_webhook_token = 74ceedc8-510f-4efd-bf64-4c5e6e87dc0f
|
||||
SCM-monitor_webhook_token = b821c1e5-63b0-42c9-b7ff-ab89c6dcd076
|
||||
PB_webhook_token = 84b165e1-160f-4ebd-a36d-d1873cca0d20
|
||||
INFO_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
CODING_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
OFFLINE_webhook_token = 20114e9a-9f09-44f9-bfbe-0da382745cde
|
||||
|
||||
陈林 = ou_69254b2555a2c6257d5ca45d885f785e
|
||||
吴勇刚 = ou_94f57439f58bde9376189a3cabb0b11a
|
||||
陈慧宗 = ou_bc2b266b05a428959bfcff3378af2d36
|
||||
胥雯筠 = ou_04e4a3ccf680acc17f1039dd57349a00
|
||||
陈江 = ou_02ce6ebcb8a510c0b4102e15aaf59d05
|
||||
谯新久 = ou_c3c6f7ac7997000dcb23e634a830851d
|
||||
罗洪 = ou_f6981cb788d066accae770fda5fa7ee5
|
||||
刘睿权 = ou_218303e87f4c45ff81ed16a1e1d0b1ee
|
||||
陈典模 = ou_0fe84f418974af89e89148e2380427dd
|
||||
李聪 = ou_711ebaac08461ae9a2726fcbe393d7de
|
||||
左其灵 = ou_cc27134156714d88da23e159ba8390e1
|
||||
宋飞飞 = ou_ccafa6ebe0194e42ea55f8804734e6d4
|
||||
|
||||
付文龙 = ou_589b7151ac583ca3d94f0fb2d579f282
|
||||
赵晓放 = ou_a62ef7960b9c57e1946f55a6bb5d8b35
|
||||
张浩东 = ou_04ba397251c63036e9d75096f3c28704
|
||||
王玉杰 = ou_e3198cbf59327bac7dd686a94af30c2a
|
||||
冯天 = ou_3522b9c4015645ea2d4c36aa6c260e90
|
||||
郭松超 = ou_6a8e6ccaceb51167c4745b347085edd9
|
||||
张雄 = ou_1ab263e2e7386fd5c5ecd67c1c6f2b6d
|
||||
刘学刚 = ou_a17ade671a3030f12ae0fa13cc4f82bc
|
||||
朱亮 = ou_0cc7a4c1a97bb58dd188b6aac6c7569b
|
||||
高志军 = ou_5df6937cc109565f92868f0cdca4906c
|
||||
马锦程 = ou_d35f2668f960300ee2d9a7768c7de9ce
|
||||
刘英杰 = ou_d62d587ce3960add08214726897c228d
|
||||
张晓刚 = ou_320976aaeb1e50976932f2eb446918c7
|
||||
|
||||
|
||||
赵飞 = ou_17fdcc65d985a21a225034db318308bf
|
||||
刘信林 = ou_dec1cf51c8b3e63823e7262928b20dbb
|
||||
叶飞 = ou_3559e23c390a60a59f4b10e7053e5551
|
||||
向明 = ou_4c26b3066017f57f6f2658b49e16b8a5
|
||||
姜浩 = ou_8014499bb65adda6399ebc82e1a46724
|
||||
姜灵敏 = ou_e4e113c845e88a43125c8a994710ff94
|
||||
徐长乐 = ou_54c5d0f98d26ab7500a5af64d9022b1b
|
||||
卿晨 = ou_3e517c22dbcdfce4b4e75af20a7e5d8c
|
||||
李柏成 = ou_e40510cf340bc5ac0f860ac01844f1a1
|
||||
苟宇恒 = ou_d82652fb433a470d7ac72a542292cd3b
|
||||
董吉祥 = ou_4d9d1b8cf8c743f75313863a3ac336ad
|
||||
白杨 = ou_ac886a59ffd1a4a0da4cb3d06c1e7b03
|
||||
|
||||
李明泽 = ou_0fc5af64989a1677fa4c34b7542e4c2c
|
||||
沈佳坤 = ou_36ed516d812a1bacc3f978179e32910d
|
||||
朱乾元 = ou_31a10ecba1074d45df6c6cbb13ec16e9
|
||||
顾洋 = ou_543fe7b50e1cadf6974e93ac8663b09d
|
||||
吴优 = ou_117b8d43a8122021d290ef89cbcb9c88
|
||||
|
||||
|
||||
王亚超 = ou_649286a2b30bfe81bb4041a2b617fd80
|
||||
徐佳林 = ou_f382da52d42932870a0ea122753f795a
|
||||
彭霞 = ou_9ef2d9e4a5c098ab27933208dd95ff8a
|
||||
通用 = ou_abcdefghijklmnopqrstuvwxyz123456
|
||||
韩东堂 = ou_4ff52d71cb3cbe7cd1f26b2ac7da5eb0
|
||||
邓振博 = ou_994af7ca097ea1b63a5bec13b0b9a42c
|
||||
朱子朋 = ou_8d13121890e1de3851c0616b8392cb1f
|
||||
李代军 = ou_279bbc6d368cffa743c959d7461d8481
|
||||
黄海峰 = ou_8a4cf96dfc173dbed704d44fdc8fc5c1
|
||||
杨文磊 = ou_885d10cea3c3ae72c380971a7082da06
|
||||
卢纪霖 = ou_eaaf66ef5c53d3977a0bd3961376beae
|
||||
杨远宁 = ou_8693a6f29fe174b4d18155e6d4da0396
|
||||
崔瑞 = ou_7bbb7b678b07e74fd1c245bafc6877f9
|
||||
李振宇 = ou_82a619af1d592dfb994ee28389815ee9
|
||||
王同刚 = ou_ca8ea6ff6bf6258f49932dc4b0c98fbc
|
||||
张瑞涛 = ou_ca355f3fad90fe934087d225bb5794b4
|
||||
左钊 = ou_ea9a364cffa235aec88d37d075e300bb
|
||||
田翔 = ou_75796572af14c996f853f015ecb788e0
|
||||
赵加会 = ou_33ed201f4e7f0f8919bc7021130c066e
|
||||
刘欣畅 = ou_6247db29c46ac536d2aa10fab9711a82
|
||||
田文昌 = ou_60e01001facaadd78b89ef05281e0763
|
||||
郑宇翔 = ou_4e8d677a2389a8f8a5517961f2423970
|
||||
王国静 = ou_862b663bac7be9762eb78d71a9e6defb
|
||||
李其 = ou_cfb094d4d15f7da729eea8b1491ffd13
|
||||
尚万中 = ou_53e6edbf2ef8a1620cbadb4b7efeb966
|
||||
张景峰 = ou_48c88a2ff129e5d23215d245c6b82fc0
|
||||
|
||||
[user_name]
|
||||
fuwenlong = 付文龙
|
||||
fengtian = 冯天
|
||||
guosongchao = 郭松超
|
||||
wangyujie02 = 王玉杰
|
||||
zhanghaodong = 张浩东
|
||||
zhuliang = 朱亮
|
||||
liuxuegang = 刘学刚
|
||||
gaozhijun = 高志军
|
||||
majincheng = 马锦程
|
||||
zhaoxiaofang = 赵晓放
|
||||
liuyingjie = 刘英杰
|
||||
zhangxiaogang = 张晓刚
|
||||
|
||||
zhaofei = 赵飞
|
||||
liuxinlin = 刘信林
|
||||
yefei = 叶飞
|
||||
xiangming = 向明
|
||||
jianghao = 姜浩
|
||||
jianglingmin = 姜灵敏
|
||||
xuchangle = 徐长乐
|
||||
qingchen = 卿晨
|
||||
libaicheng = 李柏成
|
||||
gouyuheng = 苟宇恒
|
||||
jixiang.dong = 董吉祥
|
||||
baiyang01 = 白杨
|
||||
|
||||
wuyonggang = 吴勇刚
|
||||
xuwenjun = 胥雯筠
|
||||
songfeifei = 宋飞飞
|
||||
wanggang02 = 王刚
|
||||
lichao04 = 李超
|
||||
xiexiangyi = 谢祥益
|
||||
denghaiou = 邓海鸥
|
||||
yaokun = 姚坤
|
||||
chenhuizong = 陈慧宗
|
||||
yuanzhengqi = 袁正旗
|
||||
luohong = 罗洪
|
||||
chendianmo = 陈典模
|
||||
licong = 李聪
|
||||
zuoqiling = 左其灵
|
||||
zhengxin01 = 郑新
|
||||
handongtang = 韩东堂
|
||||
dengzhenbo = 邓振博
|
||||
zhuzipeng = 朱子朋
|
||||
lidaijun = 李代军
|
||||
huanghaifeng =黄海峰
|
||||
yangwenlei01 =杨文磊
|
||||
lujilin = 卢纪霖
|
||||
yangyuanning = 杨远宁
|
||||
cuirui = 崔瑞
|
||||
lizhenyu = 李振宇
|
||||
wangtonggang = 王同刚
|
||||
zhangruitao01 = 张瑞涛
|
||||
zuozhao = 左钊
|
||||
tianxiang = 田翔
|
||||
zhaojiahui = 赵加会
|
||||
liuxinchang = 刘欣畅
|
||||
tianwenchang = 田文昌
|
||||
zhengyuxiang = 郑宇翔
|
||||
zhangxiong = 张雄
|
||||
wangguojing = 王国静
|
||||
liqi03 = 李其
|
||||
shangwanzhong = 尚万中
|
||||
zhangjingfeng = 张景峰
|
||||
|
||||
limingze = 李明泽
|
||||
zhuqianyuan = 朱乾元
|
||||
guyang = 顾洋
|
||||
shenjiakun = 沈佳坤
|
||||
wuyou = 吴优
|
||||
|
||||
[dingding_user_list]
|
||||
SCM_owner = 13350956802
|
||||
景虎成 = 13350956802
|
||||
文妮 = 17302811505
|
||||
罗洪 = 13550629276
|
||||
TO_owner = 18215530124
|
||||
李超 = 18011454607
|
||||
吴勇刚 = 13540133074
|
||||
王刚 = 19141999584
|
||||
刘明浩 = 18380450039
|
||||
谢祥益 = 18010623985
|
||||
唐其麟 = 18011454607
|
||||
肖淇迈 = 19141999584
|
||||
唐浩 = 18380450039
|
||||
TMO_owner = 18215530124
|
||||
姚坤 = 18215530124
|
||||
罗志鹏 = 18582482272
|
||||
刘涛婷 = 18328504751
|
||||
陈江 = 13458500234
|
||||
ASTWB_owner = 18202810506
|
||||
谯新久 = 18202810506
|
||||
刘鹏 = 13547858402
|
||||
杨雨 = 13608006659
|
||||
华学敏 = 13708231975
|
||||
ASORG_owner = 18202810506
|
||||
PZ_owner = 18202810506
|
||||
肖亮 = 18108096240
|
||||
ASTOP_owner = 18780106567
|
||||
ASOPE_owner = 18180956201
|
||||
蒋安龙 = 18180956201
|
||||
杨中莲 = 13882105134
|
||||
CC_owner = 18215530124
|
||||
林于棚 = 18782019436
|
||||
黄业宏 = 18603267203
|
||||
EN_owner = 18180956201
|
||||
LALIVE_owner = 18215530124
|
||||
H2R_owner = 18380448416
|
||||
胥雯筠 = 18780106567
|
||||
周仁华 = 18281029023
|
||||
袁正旗 = 18030895120
|
||||
刘睿权 = 15681935823
|
||||
文青 = 18584851102
|
||||
刘德全 = 13402829590
|
||||
包利 = 18380448416
|
||||
ASTOP_to_dd = 495fc7e0171e829acda91ffa08eaf37307e123fafc090249b633eb651c31dd79
|
||||
ASTWB_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3
|
||||
ASOPE_to_dd = 147617cea7e1b480e54cecb6dfd33edb5a9ed872e1bab52007d488673ad8ab0f
|
||||
ASORG_to_dd = 2079cb3e311ab6a37138a6d4671181661d211c12a1532c2012ae7e6b397996c3
|
||||
CC_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
H2R_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
LALIVE_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
TMO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
TO_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
UBRD_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
ULS_to_dd = ccceb513dbc7120dc301fa38a2f634b861009ec54d424d293653dc15c6e4963f
|
||||
SCM_to_dd = 38fc76120cb204e2c1da53ec9921805670466da64c37aed8bc6fba0513f5688b
|
||||
851
base_framework/platform_tools/Testcase_service/case_service.py
Normal file
851
base_framework/platform_tools/Testcase_service/case_service.py
Normal file
@@ -0,0 +1,851 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
sys.path.append(Project_Path)
|
||||
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, log
|
||||
|
||||
|
||||
def load_module_from_file(file_path, team):
|
||||
module_file = re.search(r"\\(%s\\.*)\.py" % team, file_path)
|
||||
# if not module_file:
|
||||
# module_file = re.search(r"\\(%s\\.*)\.py" % team.lower(), file_path)
|
||||
temp = module_file.group(1)
|
||||
temp = temp.replace(os.sep, ".")
|
||||
# try:
|
||||
# module = importlib.import_module(temp)
|
||||
# except Exception as err:
|
||||
# temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp)
|
||||
# module = importlib.import_module(temp)
|
||||
return importlib.import_module(temp)
|
||||
|
||||
|
||||
class ITCaseFileOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.kw = kw_instance
|
||||
self.all_url = kw_instance.all_url
|
||||
self.kw_class_name = kw_instance.kw_class
|
||||
self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口"))
|
||||
self.env_path = os.path.abspath(
|
||||
os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot"))
|
||||
|
||||
def _check_case_file_exist(self, file_path):
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_case_exist(self, case_path, case_name):
|
||||
if self._check_case_file_exist(case_path):
|
||||
with open(case_path, "r", encoding="utf-8") as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith(case_name):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_case_file(self, file_path):
|
||||
dirname = file_path[:-len(os.path.basename(file_path))]
|
||||
if not self._check_case_file_exist(file_path):
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
else:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
content = f.readlines()
|
||||
if content:
|
||||
if "\n" != content[-1]:
|
||||
self.write_content_to_case_file(file_path, "\n")
|
||||
else:
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
|
||||
def generate_env_releate_path(self, file_path):
|
||||
return "*** Settings ***\nResource " + os.path.relpath(self.env_path,
|
||||
file_path).replace("\\",
|
||||
"/") + "\n\n*** Test Cases ***\n"
|
||||
|
||||
def _check_case_file_resource(self, file_path):
|
||||
"""
|
||||
确定case文件中是否有关键字
|
||||
"""
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
content_list = f.readlines()
|
||||
if "*** Keywords ***\n" not in content_list:
|
||||
return True
|
||||
else:
|
||||
kw_index = content_list.rindex("*** Keywords ***\n")
|
||||
for index in range(kw_index, len(content_list) + 1):
|
||||
if "*** Test Cases ***\n" == content_list[index]:
|
||||
status = True
|
||||
if "*** Keywords ***\n" == content_list[index]:
|
||||
status = False
|
||||
return status
|
||||
|
||||
def write_content_to_case_file(self, file_path, content):
|
||||
status = self._check_case_file_resource(file_path)
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
if status:
|
||||
f.write(content)
|
||||
else:
|
||||
content = "*** Test Cases ***\n" + content
|
||||
f.write(content)
|
||||
|
||||
|
||||
class ITCaseContentOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.file = ITCaseFileOperation(team, kw_instance)
|
||||
self.doc_content_row_temp = " ... | {} | {} | {} |"
|
||||
self.normal_1001 = None
|
||||
self.un_expect_list = None
|
||||
self.case_string = None
|
||||
|
||||
def _generate_case_name(self, interface_name, case_type, case_des):
|
||||
return "-".join([interface_name, case_type, case_des])
|
||||
|
||||
def _check_interface_confirm(self, interface_info):
|
||||
if not interface_info.confirmed:
|
||||
raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name)
|
||||
|
||||
def generate_case_body_all_1001_array(self, request_demo, parameters):
|
||||
temp_list = list()
|
||||
demo_copy = copy.deepcopy(request_demo)
|
||||
for key, value in request_demo.items():
|
||||
if not value:
|
||||
demo_copy.pop(key)
|
||||
if len(demo_copy.keys()) == 1:
|
||||
demo_copy = demo_copy.pop(list(demo_copy.keys())[0])
|
||||
dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters)
|
||||
demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para)
|
||||
data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values)
|
||||
if isinstance(demo_copy, list):
|
||||
for item in data_list:
|
||||
temp_list.append([item])
|
||||
data_list = temp_list
|
||||
return data_list
|
||||
|
||||
def _generate_parameter_to_dict(self, parameters):
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None):
|
||||
"""
|
||||
循环生成缺少某个参数的值,其它正确的参数组合列表
|
||||
"""
|
||||
parameter_expect_list = list()
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for key, parameter_attr in parameters.items():
|
||||
if parameter_attr.normal_values:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
parameter_un_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_un_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
for key in parameter_expect_dict.keys():
|
||||
if key not in json.dumps(request_demo):
|
||||
continue
|
||||
value = copy.copy(parameter_expect_dict)
|
||||
value[key] = ['NULL']
|
||||
if parameters.get(key).is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
if type == "1102":
|
||||
name = "缺少参数%s" % key
|
||||
else:
|
||||
name = "缺少参数%s的值" % key
|
||||
parameter_expect_list.append([value, status, name])
|
||||
return parameter_expect_list
|
||||
|
||||
def _distinguish_parameters_by_is_needed(self, parameters):
|
||||
"""
|
||||
将必填参数和非必须参数分开成2个列表
|
||||
"""
|
||||
need_parameters = list()
|
||||
no_need_parameters = list()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.is_need != 0:
|
||||
need_parameters.append(parameter_attr)
|
||||
else:
|
||||
no_need_parameters.append(parameter_attr)
|
||||
return need_parameters, no_need_parameters
|
||||
|
||||
def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1101_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1101用例对应的所有请求参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search("\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
log.info("1101:%s" % name)
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": []" % parameter_attr.name
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def _clean_all_none_json_char(self, really_body):
|
||||
"""
|
||||
清除1102场景删除缺少参数后不规则的字符串内容
|
||||
"""
|
||||
really_body = re.sub(",\s,", ",", really_body)
|
||||
really_body = re.sub("\[\s?,", "[", really_body)
|
||||
really_body = re.sub("\{\s?,", "{", really_body)
|
||||
really_body = re.sub(",\s\]", "]", really_body)
|
||||
really_body = re.sub(",\s\}", "}", really_body)
|
||||
really_body = re.sub("\s,", "", really_body)
|
||||
return really_body
|
||||
|
||||
def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1102_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1102用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
log.info("1102:%s" % name)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def generate_case_body_all_1201_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1201用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
for item in parameters:
|
||||
parameter_attr = SetAttr(item)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json):
|
||||
continue
|
||||
log.info("参数%s开始替换异常值" % parameter_attr.name)
|
||||
for un_expect_item in self.un_expect_list.get(parameter_attr.name, []):
|
||||
name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item)
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
status = "FAIL"
|
||||
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None):
|
||||
"""
|
||||
生成用例的所有内容
|
||||
"""
|
||||
self._check_interface_confirm(interface_info)
|
||||
body_content = list()
|
||||
doc = self.generate_case_doc(interface_info, tags=tags, demo=demo)
|
||||
case_file, _ = self.generate_case_file_path(interface_info)
|
||||
interface_name = interface_info.name
|
||||
if not parameters:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, None, status="PASS")
|
||||
|
||||
else:
|
||||
try:
|
||||
body_array = self.generate_case_body_all_1001_array(demo, parameters)
|
||||
self.normal_1001 = body_array[0]
|
||||
except TypeError:
|
||||
log.warning(traceback.print_exc())
|
||||
body_array = []
|
||||
self.normal_1001 = {}
|
||||
if case_type == "1001":
|
||||
try:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, body_array, status="PASS")
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
|
||||
elif case_type == "1101" or case_type == "1102" or case_type == "1201":
|
||||
body_temp_list = []
|
||||
# parameters_temp = self._generate_parameter_to_dict(parameters)
|
||||
try:
|
||||
body_array = eval(
|
||||
"self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type)
|
||||
if case_type == "1101":
|
||||
name = "缺少参数值"
|
||||
elif case_type == "1102":
|
||||
name = "缺少参数key和值"
|
||||
elif case_type == "1201":
|
||||
name = "参数使用异常值"
|
||||
else:
|
||||
name = ""
|
||||
body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name,
|
||||
case_type,
|
||||
name, body_array, status="PASS")
|
||||
body_temp_list.append(body_tmp)
|
||||
body = "".join(body_temp_list)
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
raise err
|
||||
body_content.append(body)
|
||||
self.file.write_content_to_case_file(case_file, "\n".join(body_content))
|
||||
msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file)
|
||||
log.info(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
return msg
|
||||
|
||||
def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"):
|
||||
body_content = list()
|
||||
case_name = self._generate_case_name(interface_name, case_type, description)
|
||||
body_content.append(case_name)
|
||||
body_content.append(doc)
|
||||
# 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释
|
||||
request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
body_content.append(request)
|
||||
# if case_type != "1001":
|
||||
# request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
# body_content.append(request)
|
||||
# else:
|
||||
# body_content.append(" #请自行添加用例请求数据!\n")
|
||||
return "\n".join(body_content)
|
||||
|
||||
def _generate_case_body_content(self, url, body_content=None, status="PASS"):
|
||||
body_list = []
|
||||
if body_content is None:
|
||||
try:
|
||||
body_request = " ${resp} %s" % (self.file.all_url.get(url)[1])
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp}")
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_success = " should be equal as json ${resp} %s" % '请自行添加检查结果'
|
||||
else:
|
||||
body_validate_code = " Should Be Equal ${resp['code']} ${200}"
|
||||
body_validate_success = " Should Be True ${resp['success']}"
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
else:
|
||||
if not body_content:
|
||||
body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content
|
||||
body_list.append(body_create)
|
||||
count = 0
|
||||
for real_body in body_content:
|
||||
body_error = None
|
||||
result = {"message": "", "code": 200, "success": True}
|
||||
temp = copy.copy(real_body)
|
||||
if len(real_body) == 3 and isinstance(real_body, list):
|
||||
if real_body[1] == "PASS" or real_body[1] == "FAIL":
|
||||
real_body = temp[0]
|
||||
status = temp[1]
|
||||
case_description = temp[2]
|
||||
body_list.append(" #%s" % case_description)
|
||||
try:
|
||||
module = load_module_from_file(self.file.kw.kw_file_path, self.team)
|
||||
importlib.reload(module)
|
||||
if isinstance(real_body, list):
|
||||
temp_type = "*"
|
||||
else:
|
||||
temp_type = "**"
|
||||
result = eval(
|
||||
"%s.%s().%s(%sreal_body)" % (
|
||||
"module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type))
|
||||
except:
|
||||
msg = "#url: %s 无法通过参数得到请求结果!" % url
|
||||
log.warning(real_body)
|
||||
log.warning(msg)
|
||||
log.warning(traceback.print_exc())
|
||||
if "${time}" in str(real_body):
|
||||
body_list.append(" ${time} get time epoch")
|
||||
body_create = " ${body_%s} Evaluate %s" % (count, real_body)
|
||||
try:
|
||||
if isinstance(real_body, list):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "@{body_%s}" % count)
|
||||
elif isinstance(real_body, dict):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "&{body_%s}" % count)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_create)
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp_%s}" % count)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_resp = " Should Be Equal as json ${resp_%s} %s" % (count, result)
|
||||
else:
|
||||
try:
|
||||
body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}"
|
||||
# body_validate_success = " %s ${resp_%s['success']}"
|
||||
body_validate_success = " %s ${resp_%s['success']} %s"
|
||||
body_validate_resp = " %s ${resp_%s[\"message\"]} %s"
|
||||
if isinstance(result, str):
|
||||
body_validate_code = " #特殊接口返回!"
|
||||
body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!"
|
||||
body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result)
|
||||
|
||||
elif status == "PASS":
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
body_validate_success = body_validate_success % ("Should Be True", count, "")
|
||||
try:
|
||||
if result["message"]:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
else:
|
||||
try:
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
except Exception as e:
|
||||
if "status" in result:
|
||||
body_validate_code = body_validate_code % (count, result['status'])
|
||||
body_validate_code = body_validate_code.replace("code", "status")
|
||||
else:
|
||||
raise KeyError("接口不中无code返回,请与开发确认!!!")
|
||||
try:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["success"])
|
||||
except Exception as err:
|
||||
if "error" in result:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["error"])
|
||||
body_validate_success = body_validate_success.replace("success", "error")
|
||||
else:
|
||||
raise KeyError("接口不中无success返回,请与开发确认!!!")
|
||||
try:
|
||||
if result.get("message"):
|
||||
if re.search("[a-zA-Z0-9]{32}", result.get("message", "")):
|
||||
body_validate_resp = body_validate_resp % ("should contain", count,
|
||||
re.sub("[a-zA-Z0-9]{32}", "", result["message"]))
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"should be equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
except Exception as error:
|
||||
log.error(error)
|
||||
body_validate_code = None
|
||||
body_validate_success = None
|
||||
body_validate_resp = None
|
||||
body_error = " #无法正确生成异常用例返回值,请检查并手动生成!"
|
||||
if body_validate_code:
|
||||
body_list.append(body_validate_code)
|
||||
if body_validate_success:
|
||||
body_list.append(body_validate_success)
|
||||
if body_validate_resp:
|
||||
body_list.append(body_validate_resp)
|
||||
if body_error:
|
||||
body_list.append(body_error)
|
||||
# body_list.append(" #请自行添加data校验!")
|
||||
count += 1
|
||||
body_list.append("\n")
|
||||
return "\n".join(body_list)
|
||||
|
||||
def _generate_body_expect_type_value(self, type):
|
||||
if type.lower() == 'integer':
|
||||
value = random.randint(0, 100)
|
||||
elif type.lower() == "string":
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "date":
|
||||
value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
elif type.lower() == "boolean":
|
||||
value = True
|
||||
elif type.lower() == "array":
|
||||
value = [1]
|
||||
else:
|
||||
value = "123"
|
||||
return value
|
||||
|
||||
def _generate_body_un_expect_type_value(self, type):
|
||||
value = ""
|
||||
if type.lower() == 'integer':
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "string":
|
||||
value = random.randint(0, 10000)
|
||||
else:
|
||||
value = False
|
||||
return value
|
||||
|
||||
def _analysis_dict_to_body_value(self, dict_none, dict_data):
|
||||
"""
|
||||
生成所有请求参数的组合列表
|
||||
"""
|
||||
data_list = []
|
||||
if isinstance(dict_none, list):
|
||||
if dict_none:
|
||||
for index in range(len(dict_none)):
|
||||
data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index])
|
||||
data_list.extend(data)
|
||||
else:
|
||||
data_list = dict_data
|
||||
elif isinstance(dict_none, dict):
|
||||
try:
|
||||
len_num = self._get_nums(dict_data)
|
||||
except Exception as e:
|
||||
# log.error(e)
|
||||
log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数")
|
||||
raise e
|
||||
for i in range(0, len_num):
|
||||
copy_dict = copy.copy(dict_none)
|
||||
for key, value in copy_dict.items():
|
||||
data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key])
|
||||
if not data:
|
||||
copy_dict[key] = None
|
||||
elif i >= len(data):
|
||||
copy_dict[key] = data[0]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[0]]
|
||||
else:
|
||||
copy_dict[key] = data[i]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[i]]
|
||||
|
||||
data_list.append(copy_dict)
|
||||
|
||||
else:
|
||||
data_list = dict_data
|
||||
|
||||
return data_list
|
||||
|
||||
def _get_nums(self, dict_data):
|
||||
"""
|
||||
获取所有参数对应值的最大长度
|
||||
"""
|
||||
nums_list = [0]
|
||||
if isinstance(dict_data, list):
|
||||
for item in dict_data:
|
||||
if isinstance(item, dict):
|
||||
data = self._get_nums(dict_data[0])
|
||||
nums_list.append(data)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
|
||||
elif isinstance(dict_data, dict):
|
||||
for value in dict_data.values():
|
||||
data = self._get_nums(value)
|
||||
nums_list.append(data)
|
||||
elif dict_data == None:
|
||||
nums_list.append(0)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
return max(nums_list)
|
||||
|
||||
def _analysis_case_parameters_to_value(self, interface_demo, parameters):
|
||||
"""
|
||||
根据demo将每个参数的对应值的列表写回demo
|
||||
"""
|
||||
body = copy.copy(interface_demo)
|
||||
if isinstance(body, list):
|
||||
for index in range(len(body)):
|
||||
item = body[index]
|
||||
body[index] = self._analysis_case_parameters_to_value(item, parameters)
|
||||
|
||||
elif isinstance(body, dict):
|
||||
for key, value in body.items():
|
||||
if isinstance(value, dict) or isinstance(value, list):
|
||||
if value:
|
||||
body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
return body
|
||||
|
||||
def _analysis_case_parameters_to_dict(self, parameters):
|
||||
"""
|
||||
根据所有参数生成正常值 和异常值的列表
|
||||
"""
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.normal_values:
|
||||
if parameter_attr.normal_values.startswith("["):
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
if parameter_attr.type == "array":
|
||||
parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")]
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
continue
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
# raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name)
|
||||
parameter_un_expect_dict[parameter_attr.name] = []
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth
|
||||
|
||||
def generate_case_doc(self, interface_info, tags=None, demo=None):
|
||||
if demo:
|
||||
lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo)
|
||||
doc_list = list()
|
||||
doc_list.append(
|
||||
" [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe,
|
||||
interface_info.type, interface_info.in_url))
|
||||
# doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明"))
|
||||
# for parameters_info in parameters:
|
||||
# sub_doc = self._generate_case_doc(parameters_info)
|
||||
# doc_list.append(sub_doc)
|
||||
if tags:
|
||||
tag_list = tags.split(",")
|
||||
tag = " [Tags]"
|
||||
for tag_ in tag_list:
|
||||
tag += " %s" % tag_
|
||||
doc_list.append(tag)
|
||||
if lenth > 1:
|
||||
doc_list.append(
|
||||
" # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数")
|
||||
return "\n".join(doc_list)
|
||||
|
||||
def _generate_case_doc(self, request_parameter):
|
||||
doc_content = list()
|
||||
parameter_attr = SetAttr(request_parameter)
|
||||
row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note)
|
||||
doc_content.append(row)
|
||||
return "\n".join(doc_content)
|
||||
|
||||
def generate_case_file_path(self, interface_info):
|
||||
path_cell_list = self._generate_case_file_path_info(interface_info.in_url)
|
||||
path_cell_list.insert(0, self.file.case_dir)
|
||||
return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1]
|
||||
|
||||
def _generate_case_file_path_info(self, url):
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
path_first = re.search("//([\w+\-]+)\.", host).group(1)
|
||||
head_list.append(path_first)
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return head_list
|
||||
|
||||
|
||||
def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None):
|
||||
case_type_list = ["1001", "1101", "1102", "1201"]
|
||||
not_exist_case_type = list()
|
||||
exist_case_name = list()
|
||||
case_path, _ = case_instance.generate_case_file_path(interface)
|
||||
case_name_head = interface.name
|
||||
if parameters:
|
||||
for case_type in case_type_list:
|
||||
case_name = "-".join([case_name_head, case_type])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append(case_type)
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
else:
|
||||
case_name = "-".join([case_name_head, "1001"])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append("1001")
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
for item in exist_case_name:
|
||||
msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
if not_exist_case_type:
|
||||
if normal:
|
||||
not_exist_case_type = ["1001"]
|
||||
case_instance.file.generate_case_file(case_path)
|
||||
try:
|
||||
demo = json.loads(demo_info.request_demo)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
log.warning("the demo of interface(%s) is empty,please check." % interface.id)
|
||||
demo = dict()
|
||||
try:
|
||||
result = list()
|
||||
result.append("")
|
||||
result.append("-" * 60)
|
||||
result.append("用例生成成功!")
|
||||
for case_type in not_exist_case_type:
|
||||
body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags)
|
||||
# result.append(body)
|
||||
result.append(body.split("!")[1])
|
||||
log.info("\n".join(result))
|
||||
except:
|
||||
log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url))
|
||||
log.error(traceback.print_exc())
|
||||
else:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
833
base_framework/platform_tools/Testcase_service/case_service1.py
Normal file
833
base_framework/platform_tools/Testcase_service/case_service1.py
Normal file
@@ -0,0 +1,833 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import copy
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
def get_project_root_path(path="base_framework"):
|
||||
"""
|
||||
获取项目目录
|
||||
"""
|
||||
o_path = os.getcwd()
|
||||
try:
|
||||
project_path = re.search(r"(.*%s)" % path, o_path).group(1)
|
||||
return os.path.abspath(os.path.join(project_path, os.path.pardir))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
Project_Path = get_project_root_path()
|
||||
sys.path.append(Project_Path)
|
||||
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, log
|
||||
|
||||
|
||||
def load_module_from_file(file_path, team):
|
||||
module_file = re.search(r"(%s\\.*)\.py" % team, file_path)
|
||||
if not module_file:
|
||||
module_file = re.search(r"(%s.*)\.py" % team.lower(), file_path)
|
||||
temp = module_file.group(1)
|
||||
temp = temp.replace(os.sep, ".")
|
||||
# try:
|
||||
# module = importlib.import_module(temp)
|
||||
# except Exception as err:
|
||||
# temp = re.sub(r"%s\." % team, "%s." % team.lower(), temp)
|
||||
# module = importlib.import_module(temp)
|
||||
return importlib.import_module(temp)
|
||||
|
||||
|
||||
class ITCaseFileOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.kw = kw_instance
|
||||
self.all_url = kw_instance.all_url
|
||||
self.kw_class_name = kw_instance.kw_class
|
||||
self.case_dir = os.path.abspath(os.path.join(Project_Path, team, "test_case", "TestCase", "1.接口"))
|
||||
self.env_path = os.path.abspath(
|
||||
os.path.join(Project_Path, team, "test_case", "Resource", "AdapterKws", "env.robot"))
|
||||
|
||||
def _check_case_file_exist(self, file_path):
|
||||
if os.path.exists(file_path) and os.path.isfile(file_path):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_case_exist(self, case_path, case_name):
|
||||
if self._check_case_file_exist(case_path):
|
||||
with open(case_path, "r", encoding="utf-8") as f:
|
||||
for line in f.readlines():
|
||||
if line.startswith(case_name):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def generate_case_file(self, file_path):
|
||||
if not self._check_case_file_exist(file_path):
|
||||
dirname = file_path[:-len(os.path.basename(file_path))]
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
self.write_content_to_case_file(file_path, self.generate_env_releate_path(dirname))
|
||||
else:
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
if f.readlines():
|
||||
if "\n" != f.readlines()[-1]:
|
||||
self.write_content_to_case_file(file_path, "\n")
|
||||
|
||||
def generate_env_releate_path(self, file_path):
|
||||
return "*** Settings ***\nResource " + os.path.relpath(self.env_path,
|
||||
file_path).replace("\\",
|
||||
"/") + "\n\n*** Test Cases ***\n"
|
||||
|
||||
def _check_case_file_resource(self, file_path):
|
||||
"""
|
||||
确定case文件中是否有关键字
|
||||
"""
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
content_list = f.readlines()
|
||||
if "*** Keywords ***\n" not in content_list:
|
||||
return True
|
||||
else:
|
||||
kw_index = content_list.rindex("*** Keywords ***\n")
|
||||
for index in range(kw_index, len(content_list) + 1):
|
||||
if "*** Test Cases ***\n" == content_list[index]:
|
||||
status = True
|
||||
if "*** Keywords ***\n" == content_list[index]:
|
||||
status = False
|
||||
return status
|
||||
|
||||
def write_content_to_case_file(self, file_path, content):
|
||||
status = self._check_case_file_resource(file_path)
|
||||
with open(file_path, "a+", encoding="utf-8") as f:
|
||||
if status:
|
||||
f.write(content)
|
||||
else:
|
||||
content = "*** Test Cases ***\n" + content
|
||||
f.write(content)
|
||||
|
||||
|
||||
class ITCaseContentOperation(object):
|
||||
def __init__(self, team, kw_instance):
|
||||
self.team = team
|
||||
self.file = ITCaseFileOperation(team, kw_instance)
|
||||
self.doc_content_row_temp = " ... | {} | {} | {} |"
|
||||
self.normal_1001 = None
|
||||
self.un_expect_list = None
|
||||
self.case_string = None
|
||||
|
||||
def _generate_case_name(self, interface_name, case_type, case_des):
|
||||
return "-".join([interface_name, case_type, case_des])
|
||||
|
||||
def _check_interface_confirm(self, interface_info):
|
||||
if not interface_info.confirmed:
|
||||
raise Exception("接口%s未确认,请先确认接口参数。" % interface_info.name)
|
||||
|
||||
def generate_case_body_all_1001_array(self, request_demo, parameters):
|
||||
temp_list = list()
|
||||
demo_copy = copy.deepcopy(request_demo)
|
||||
for key, value in request_demo.items():
|
||||
if not value:
|
||||
demo_copy.pop(key)
|
||||
if len(demo_copy.keys()) == 1:
|
||||
demo_copy = demo_copy.pop(list(demo_copy.keys())[0])
|
||||
dict_para, self.un_expect_list, _, _ = self._analysis_case_parameters_to_dict(parameters)
|
||||
demo_add_values = self._analysis_case_parameters_to_value(demo_copy, dict_para)
|
||||
data_list = self._analysis_dict_to_body_value(demo_copy, demo_add_values)
|
||||
if isinstance(demo_copy, list):
|
||||
for item in data_list:
|
||||
temp_list.append([item])
|
||||
data_list = temp_list
|
||||
return data_list
|
||||
|
||||
def _generate_parameter_to_dict(self, parameters):
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
def _analysis_case_parameters_to_value_empty_dict(self, request_demo, parameters, type=None):
|
||||
"""
|
||||
循环生成缺少某个参数的值,其它正确的参数组合列表
|
||||
"""
|
||||
parameter_expect_list = list()
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for key, parameter_attr in parameters.items():
|
||||
if parameter_attr.normal_values:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
parameter_un_expect_dict[parameter_attr.name] = [
|
||||
self._generate_body_un_expect_type_value(parameter_attr.type)]
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
for key in parameter_expect_dict.keys():
|
||||
if key not in json.dumps(request_demo):
|
||||
continue
|
||||
value = copy.copy(parameter_expect_dict)
|
||||
value[key] = ['NULL']
|
||||
if parameters.get(key).is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
if type == "1102":
|
||||
name = "缺少参数%s" % key
|
||||
else:
|
||||
name = "缺少参数%s的值" % key
|
||||
parameter_expect_list.append([value, status, name])
|
||||
return parameter_expect_list
|
||||
|
||||
def _distinguish_parameters_by_is_needed(self, parameters):
|
||||
"""
|
||||
将必填参数和非必须参数分开成2个列表
|
||||
"""
|
||||
need_parameters = list()
|
||||
no_need_parameters = list()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.is_need != 0:
|
||||
need_parameters.append(parameter_attr)
|
||||
else:
|
||||
no_need_parameters.append(parameter_attr)
|
||||
return need_parameters, no_need_parameters
|
||||
|
||||
def _replace_null_value_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s[\[\{]" % parameter_attr.name
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1101_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1101用例对应的所有请求参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_value_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数为空"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search("\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = "\"%s\": []" % parameter_attr.name
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = "\"%s\": \"NULL\"" % parameter_attr.name
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def _clean_all_none_json_char(self, really_body):
|
||||
"""
|
||||
清除1102场景删除缺少参数后不规则的字符串内容
|
||||
"""
|
||||
really_body = re.sub(",\s,", ",", really_body)
|
||||
really_body = re.sub("\[\s?,", "[", really_body)
|
||||
really_body = re.sub("\{\s?,", "{", really_body)
|
||||
really_body = re.sub(",\s\]", "]", really_body)
|
||||
really_body = re.sub(",\s\}", "}", really_body)
|
||||
really_body = re.sub("\s,", "", really_body)
|
||||
return really_body
|
||||
|
||||
def _replace_null_all_no_need_parameters(self, demo, no_need_parameter_attrs):
|
||||
"""
|
||||
将所有非必填参数置空
|
||||
"""
|
||||
really_body = copy.deepcopy(demo)
|
||||
for parameter_attr in no_need_parameter_attrs:
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo):
|
||||
continue
|
||||
try:
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
except Exception as err:
|
||||
log.error(err)
|
||||
raise Exception("接口参数%s无正常值,请先确认接口参数。" % parameter_attr.name)
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, really_body)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
return really_body
|
||||
|
||||
def generate_case_body_all_1102_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1102用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
need, no_need = self._distinguish_parameters_by_is_needed(parameters)
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
if no_need:
|
||||
no_need_demo_json = self._replace_null_all_no_need_parameters(demo_json, no_need)
|
||||
body_array.append([json.loads(no_need_demo_json), "PASS", "所有非必填参数均缺失"])
|
||||
for item in need:
|
||||
parameter_attr = copy.copy(item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json_copy):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json_copy):
|
||||
continue
|
||||
name = "缺少参数%s" % parameter_attr.name
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = ""
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
really_body = self._clean_all_none_json_char(really_body)
|
||||
if parameter_attr.is_need == 1:
|
||||
status = "FAIL"
|
||||
else:
|
||||
status = "PASS"
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def generate_case_body_all_1201_array(self, request_demo, parameters):
|
||||
"""
|
||||
生成1201用例对应的所有参数组合
|
||||
"""
|
||||
body_array = list()
|
||||
demo_json = json.dumps(request_demo, ensure_ascii=False)
|
||||
for item in parameters:
|
||||
parameter_attr = SetAttr(item)
|
||||
if re.search(r"\"%s\"" % parameter_attr.name, demo_json):
|
||||
check_para = r"\"%s\":\s\{|\"%s\":\s\[\{" % (parameter_attr.name, parameter_attr.name)
|
||||
if re.search(check_para, demo_json):
|
||||
continue
|
||||
for un_expect_item in self.un_expect_list.get(parameter_attr.name):
|
||||
name = "参数%s使用异常值%s" % (parameter_attr.name, un_expect_item)
|
||||
demo_json_copy = copy.copy(demo_json)
|
||||
expect_value = parameter_attr.normal_values.split(",")[0]
|
||||
if parameter_attr.type == "array":
|
||||
patten1 = r"\"%s\":\s\[\"%s\"\]" % (parameter_attr.name, expect_value)
|
||||
replace1 = '"%s": ["%s"]' % (parameter_attr.name, un_expect_item)
|
||||
else:
|
||||
patten1 = r"\"%s\":\s\"%s\"" % (parameter_attr.name, expect_value)
|
||||
patten1 = patten1.replace("{", r"\{")
|
||||
patten1 = patten1.replace("}", r"\}")
|
||||
patten1 = patten1.replace("[", r"\[")
|
||||
patten1 = patten1.replace("]", r"\]")
|
||||
patten1 = patten1.replace("$", r"\$")
|
||||
replace1 = '"%s": "%s"' % (parameter_attr.name, un_expect_item)
|
||||
really_body = re.sub(patten1, replace1, demo_json_copy)
|
||||
status = "FAIL"
|
||||
|
||||
body_array.append([json.loads(really_body), status, name])
|
||||
return body_array
|
||||
|
||||
def create_case_content(self, result_path, interface_info, parameters, demo, case_type, tags=None):
|
||||
"""
|
||||
生成用例的所有内容
|
||||
"""
|
||||
self._check_interface_confirm(interface_info)
|
||||
body_content = list()
|
||||
doc = self.generate_case_doc(interface_info, tags=tags, demo=demo)
|
||||
case_file, _ = self.generate_case_file_path(interface_info)
|
||||
interface_name = interface_info.name
|
||||
if not parameters:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, None, status="PASS")
|
||||
|
||||
else:
|
||||
try:
|
||||
body_array = self.generate_case_body_all_1001_array(demo, parameters)
|
||||
self.normal_1001 = body_array[0]
|
||||
except TypeError:
|
||||
log.warning(traceback.print_exc())
|
||||
body_array = []
|
||||
self.normal_1001 = {}
|
||||
if case_type == "1001":
|
||||
try:
|
||||
body = self.generate_case_body_content(interface_info.in_url, doc, interface_name, case_type,
|
||||
interface_info.interface_describe, body_array, status="PASS")
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
|
||||
elif case_type == "1101" or case_type == "1102" or case_type == "1201":
|
||||
body_temp_list = []
|
||||
# parameters_temp = self._generate_parameter_to_dict(parameters)
|
||||
try:
|
||||
body_array = eval(
|
||||
"self.generate_case_body_all_%s_array(self.normal_1001, parameters)" % case_type)
|
||||
if case_type == "1101":
|
||||
name = "缺少参数值"
|
||||
elif case_type == "1102":
|
||||
name = "缺少参数key和值"
|
||||
elif case_type == "1201":
|
||||
name = "参数使用异常值"
|
||||
else:
|
||||
name = ""
|
||||
body_tmp = self.generate_case_body_content(interface_info.in_url, doc, interface_name,
|
||||
case_type,
|
||||
name, body_array, status="PASS")
|
||||
body_temp_list.append(body_tmp)
|
||||
body = "".join(body_temp_list)
|
||||
except Exception as err:
|
||||
msg = "生成用例%s,%s失败原因如下:%s" % (interface_info.type, interface_info.in_url, traceback.print_exc())
|
||||
log.error(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
raise err
|
||||
body_content.append(body)
|
||||
self.file.write_content_to_case_file(case_file, "\n".join(body_content))
|
||||
msg = "接口url:%s,%s,生成case%s成功!路径为:%s" % (interface_info.type, interface_info.in_url, case_type, case_file)
|
||||
log.info(msg)
|
||||
self.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
return msg
|
||||
|
||||
def generate_case_body_content(self, url, doc, interface_name, case_type, description, body_para, status="PASS"):
|
||||
body_content = list()
|
||||
case_name = self._generate_case_name(interface_name, case_type, description)
|
||||
body_content.append(case_name)
|
||||
body_content.append(doc)
|
||||
# 如何需要自动生成1001类型用例的请求body数据,请将304/305行放开,并把306~310注释
|
||||
request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
body_content.append(request)
|
||||
# if case_type != "1001":
|
||||
# request = self._generate_case_body_content(url, body_content=body_para, status=status)
|
||||
# body_content.append(request)
|
||||
# else:
|
||||
# body_content.append(" #请自行添加用例请求数据!\n")
|
||||
return "\n".join(body_content)
|
||||
|
||||
def _generate_case_body_content(self, url, body_content=None, status="PASS"):
|
||||
body_list = []
|
||||
if body_content is None:
|
||||
try:
|
||||
body_request = " ${resp} %s" % (self.file.all_url.get(url)[1])
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp}")
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_success = " check_rf ${resp} %s" % '请自行添加检查结果'
|
||||
else:
|
||||
body_validate_code = " Should Be Equal ${resp['code']} ${200}"
|
||||
body_validate_success = " Should Be True ${resp['success']}"
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
else:
|
||||
if not body_content:
|
||||
body_create = " #request_demo无内容或者parameters在数据表中不全,请先检查再生成 \n ${body} Evaluate %s" % body_content
|
||||
body_list.append(body_create)
|
||||
count = 0
|
||||
for real_body in body_content:
|
||||
result = {"message": "", "code": 200, "success": True}
|
||||
temp = copy.copy(real_body)
|
||||
if len(real_body) == 3 and isinstance(real_body, list):
|
||||
if real_body[1] == "PASS" or real_body[1] == "FAIL":
|
||||
real_body = temp[0]
|
||||
status = temp[1]
|
||||
case_description = temp[2]
|
||||
body_list.append(" #%s" % case_description)
|
||||
try:
|
||||
module = load_module_from_file(self.file.kw.kw_file_path, self.team)
|
||||
importlib.reload(module)
|
||||
if isinstance(real_body, list):
|
||||
temp_type = "*"
|
||||
else:
|
||||
temp_type = "**"
|
||||
result = eval(
|
||||
"%s.%s().%s(%sreal_body)" % (
|
||||
"module", self.file.kw_class_name, self.file.all_url.get(url)[1], temp_type))
|
||||
except:
|
||||
msg = "#url: %s 无法通过参数得到请求结果!" % url
|
||||
log.warning(real_body)
|
||||
log.warning(msg)
|
||||
log.warning(traceback.print_exc())
|
||||
if "${time}" in str(real_body):
|
||||
body_list.append(" ${time} get time epoch")
|
||||
body_create = " ${body_%s} Evaluate %s" % (count, real_body)
|
||||
try:
|
||||
if isinstance(real_body, list):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "@{body_%s}" % count)
|
||||
elif isinstance(real_body, dict):
|
||||
body_request = " ${resp_%s} %s %s" % (
|
||||
count, self.file.all_url.get(url)[1], "&{body_%s}" % count)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
body_request = " #url: %s 无法找到对应的关键字,请检查并更新!" % url
|
||||
body_list.append(body_create)
|
||||
body_list.append(body_request)
|
||||
body_list.append(" Log ${resp_%s}" % count)
|
||||
if self.team.upper() in ["TMO"]:
|
||||
body_validate_resp = " check_rf ${resp_%s} %s" % (count, result)
|
||||
else:
|
||||
body_validate_code = " Should Be Equal as strings ${resp_%s['code']} ${%s}"
|
||||
# body_validate_success = " %s ${resp_%s['success']}"
|
||||
body_validate_success = " %s ${resp_%s['success']} %s"
|
||||
body_validate_resp = " %s ${resp_%s[\"message\"]} %s"
|
||||
if isinstance(result, str):
|
||||
body_validate_code = " #特殊接口返回!"
|
||||
body_validate_success = " #返回为bytes类型的字符码。仅检查结果是否相等!"
|
||||
body_validate_resp = " Should Be Equal as strings ${resp_%s} %s" % (count, result)
|
||||
|
||||
elif status == "PASS":
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
body_validate_success = body_validate_success % ("Should Be True", count, "")
|
||||
try:
|
||||
if result["message"]:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
else:
|
||||
try:
|
||||
body_validate_code = body_validate_code % (count, result['code'])
|
||||
except Exception as e:
|
||||
if "status" in result:
|
||||
body_validate_code = body_validate_code % (count, result['status'])
|
||||
body_validate_code = body_validate_code.replace("code", "status")
|
||||
else:
|
||||
raise KeyError("接口不中无code返回,请与开发确认!!!")
|
||||
try:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["success"])
|
||||
except Exception as err:
|
||||
if "error" in result:
|
||||
body_validate_success = body_validate_success % (
|
||||
"Should Be Equal As Strings", count, result["error"])
|
||||
body_validate_success = body_validate_success.replace("success", "error")
|
||||
else:
|
||||
raise KeyError("接口不中无success返回,请与开发确认!!!")
|
||||
try:
|
||||
if result.get("message"):
|
||||
if re.search("[a-zA-Z0-9]{32}", result.get("message", "")):
|
||||
body_validate_resp = body_validate_resp % ("should contain", count,
|
||||
re.sub("[a-zA-Z0-9]{32}", "", result["message"]))
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"should be equal as strings", count, result["message"])
|
||||
else:
|
||||
body_validate_resp = body_validate_resp % (
|
||||
"Should Be Equal as strings", count, '${EMPTY}')
|
||||
except KeyError as err:
|
||||
body_validate_resp = " #resp中没有message,请后续自行添加!"
|
||||
log.warning(err)
|
||||
log.warning("resp中没有message,请后续自行添加!")
|
||||
|
||||
body_list.append(body_validate_code)
|
||||
body_list.append(body_validate_success)
|
||||
body_list.append(body_validate_resp)
|
||||
# body_list.append(" #请自行添加data校验!")
|
||||
count += 1
|
||||
body_list.append("\n")
|
||||
return "\n".join(body_list)
|
||||
|
||||
def _generate_body_expect_type_value(self, type):
|
||||
if type.lower() == 'integer':
|
||||
value = random.randint(0, 100)
|
||||
elif type.lower() == "string":
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "date":
|
||||
value = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
elif type.lower() == "boolean":
|
||||
value = True
|
||||
elif type.lower() == "array":
|
||||
value = [1]
|
||||
else:
|
||||
value = "123"
|
||||
return value
|
||||
|
||||
def _generate_body_un_expect_type_value(self, type):
|
||||
value = ""
|
||||
if type.lower() == 'integer':
|
||||
value = "".join([random.choice(string.digits + string.ascii_letters) for i in range(5)])
|
||||
elif type.lower() == "string":
|
||||
value = random.randint(0, 10000)
|
||||
else:
|
||||
value = False
|
||||
return value
|
||||
|
||||
def _analysis_dict_to_body_value(self, dict_none, dict_data):
|
||||
"""
|
||||
生成所有请求参数的组合列表
|
||||
"""
|
||||
data_list = []
|
||||
if isinstance(dict_none, list):
|
||||
if dict_none:
|
||||
for index in range(len(dict_none)):
|
||||
data = self._analysis_dict_to_body_value(dict_none[index], dict_data[index])
|
||||
data_list.extend(data)
|
||||
else:
|
||||
data_list = dict_data
|
||||
elif isinstance(dict_none, dict):
|
||||
try:
|
||||
len_num = self._get_nums(dict_data)
|
||||
except Exception as e:
|
||||
# log.error(e)
|
||||
log.error("数据表request_parameters中参数不全,请使用interface_hunter补全参数")
|
||||
raise e
|
||||
for i in range(0, len_num):
|
||||
copy_dict = copy.copy(dict_none)
|
||||
for key, value in copy_dict.items():
|
||||
data = self._analysis_dict_to_body_value(copy_dict[key], dict_data[key])
|
||||
if not data:
|
||||
copy_dict[key] = None
|
||||
elif i >= len(data):
|
||||
copy_dict[key] = data[0]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[0]]
|
||||
else:
|
||||
copy_dict[key] = data[i]
|
||||
if isinstance(value, list):
|
||||
copy_dict[key] = [data[i]]
|
||||
|
||||
data_list.append(copy_dict)
|
||||
|
||||
else:
|
||||
data_list = dict_data
|
||||
|
||||
return data_list
|
||||
|
||||
def _get_nums(self, dict_data):
|
||||
"""
|
||||
获取所有参数对应值的最大长度
|
||||
"""
|
||||
nums_list = [0]
|
||||
if isinstance(dict_data, list):
|
||||
for item in dict_data:
|
||||
if isinstance(item, dict):
|
||||
data = self._get_nums(dict_data[0])
|
||||
nums_list.append(data)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
|
||||
elif isinstance(dict_data, dict):
|
||||
for value in dict_data.values():
|
||||
data = self._get_nums(value)
|
||||
nums_list.append(data)
|
||||
elif dict_data == None:
|
||||
nums_list.append(0)
|
||||
else:
|
||||
nums_list.append(len(dict_data))
|
||||
return max(nums_list)
|
||||
|
||||
def _analysis_case_parameters_to_value(self, interface_demo, parameters):
|
||||
"""
|
||||
根据demo将每个参数的对应值的列表写回demo
|
||||
"""
|
||||
body = copy.copy(interface_demo)
|
||||
if isinstance(body, list):
|
||||
for index in range(len(body)):
|
||||
item = body[index]
|
||||
body[index] = self._analysis_case_parameters_to_value(item, parameters)
|
||||
|
||||
elif isinstance(body, dict):
|
||||
for key, value in body.items():
|
||||
if isinstance(value, dict) or isinstance(value, list):
|
||||
if value:
|
||||
body[key] = self._analysis_case_parameters_to_value(body.get(key), parameters)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
else:
|
||||
body[key] = parameters.get(key)
|
||||
return body
|
||||
|
||||
def _analysis_case_parameters_to_dict(self, parameters):
|
||||
"""
|
||||
根据所有参数生成正常值 和异常值的列表
|
||||
"""
|
||||
expect_lenth = 0
|
||||
un_expect_lenth = 0
|
||||
parameter_expect_dict = dict()
|
||||
parameter_un_expect_dict = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
if parameter_attr.normal_values:
|
||||
if parameter_attr.normal_values.startswith("["):
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
if parameter_attr.type == "array":
|
||||
parameter_expect_dict[parameter_attr.name] = [[x] for x in parameter_attr.normal_values.split(",")]
|
||||
else:
|
||||
parameter_expect_dict[parameter_attr.name] = parameter_attr.normal_values.split(",")
|
||||
else:
|
||||
continue
|
||||
if len(parameter_expect_dict[parameter_attr.name]) > expect_lenth:
|
||||
expect_lenth = len(parameter_expect_dict[parameter_attr.name])
|
||||
|
||||
if parameter_attr.exception_values:
|
||||
parameter_un_expect_dict[parameter_attr.name] = parameter_attr.exception_values.split(",")
|
||||
else:
|
||||
# raise Exception("接口参数%s无异常值,请先确认接口参数。" % parameter_attr.name)
|
||||
parameter_un_expect_dict[parameter_attr.name] = []
|
||||
|
||||
if len(parameter_un_expect_dict[parameter_attr.name]) > un_expect_lenth:
|
||||
un_expect_lenth = len(parameter_un_expect_dict[parameter_attr.name])
|
||||
|
||||
return parameter_expect_dict, parameter_un_expect_dict, expect_lenth, un_expect_lenth
|
||||
|
||||
def generate_case_doc(self, interface_info, tags=None, demo=None):
|
||||
if demo:
|
||||
lenth, demo_json, paras_d = self.file.kw._clean_demo_to_less_key(demo)
|
||||
doc_list = list()
|
||||
doc_list.append(
|
||||
" [Documentation] {} 接口路径:{}, {}".format(interface_info.interface_describe,
|
||||
interface_info.type, interface_info.in_url))
|
||||
# doc_list.append(self.doc_content_row_temp.format("输入参数名", "参数类型", "说明"))
|
||||
# for parameters_info in parameters:
|
||||
# sub_doc = self._generate_case_doc(parameters_info)
|
||||
# doc_list.append(sub_doc)
|
||||
if tags:
|
||||
tag_list = tags.split(",")
|
||||
tag = " [Tags]"
|
||||
for tag_ in tag_list:
|
||||
tag += " %s" % tag_
|
||||
doc_list.append(tag)
|
||||
if lenth > 1:
|
||||
doc_list.append(
|
||||
" # 请求参数说明 h:放header里的参数 u:放url路径节点中的参数 d:不放在url中在请求参数 p:放在url问号后在key-value结构参数")
|
||||
return "\n".join(doc_list)
|
||||
|
||||
def _generate_case_doc(self, request_parameter):
|
||||
doc_content = list()
|
||||
parameter_attr = SetAttr(request_parameter)
|
||||
row = self.doc_content_row_temp.format(parameter_attr.name, parameter_attr.type, parameter_attr.note)
|
||||
doc_content.append(row)
|
||||
return "\n".join(doc_content)
|
||||
|
||||
def generate_case_file_path(self, interface_info):
|
||||
path_cell_list = self._generate_case_file_path_info(interface_info.in_url)
|
||||
path_cell_list.insert(0, self.file.case_dir)
|
||||
return os.path.abspath(os.path.join(*path_cell_list[:-1]) + ".robot"), path_cell_list[-1]
|
||||
|
||||
def _generate_case_file_path_info(self, url):
|
||||
head_list = []
|
||||
para_list = []
|
||||
host, url_last = re.search(r"(\S+\.cn|\S+\.com)(\S+)", url).groups()
|
||||
path_first = re.search("//([\w+\-]+)\.", host).group(1)
|
||||
head_list.append(path_first)
|
||||
search = re.findall("[\w+\-_]+|[\{\w+\-_\}]+", url_last)
|
||||
for item in search:
|
||||
if "{" in item:
|
||||
temp = item.replace("{", "").replace("}", "")
|
||||
para_list.append(temp)
|
||||
else:
|
||||
head_list.append(item)
|
||||
return head_list
|
||||
|
||||
|
||||
def run_interface_case_generate(result_path, interface, demo_info, parameters, case_instance, tags=None, normal=None):
|
||||
case_type_list = ["1001", "1101", "1102", "1201"]
|
||||
not_exist_case_type = list()
|
||||
exist_case_name = list()
|
||||
case_path, _ = case_instance.generate_case_file_path(interface)
|
||||
case_name_head = interface.name
|
||||
if parameters:
|
||||
for case_type in case_type_list:
|
||||
case_name = "-".join([case_name_head, case_type])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append(case_type)
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
else:
|
||||
case_name = "-".join([case_name_head, "1001"])
|
||||
if not case_instance.file.check_case_exist(case_path, case_name):
|
||||
not_exist_case_type.append("1001")
|
||||
else:
|
||||
exist_case_name.append(case_name)
|
||||
for item in exist_case_name:
|
||||
msg = "接口url:%s,%s,已存在用例%s,请确认!" % (interface.type, interface.in_url, item)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
if not_exist_case_type:
|
||||
if normal:
|
||||
not_exist_case_type = ["1001"]
|
||||
case_instance.file.generate_case_file(case_path)
|
||||
try:
|
||||
demo = json.loads(demo_info.request_demo)
|
||||
except Exception as err:
|
||||
log.error(traceback.print_exc())
|
||||
log.warning("the demo of interface(%s) is empty,please check." % interface.id)
|
||||
demo = dict()
|
||||
try:
|
||||
result = list()
|
||||
result.append("")
|
||||
result.append("-" * 60)
|
||||
result.append("用例生成成功!")
|
||||
for case_type in not_exist_case_type:
|
||||
body = case_instance.create_case_content(result_path, interface, parameters, demo, case_type, tags=tags)
|
||||
# result.append(body)
|
||||
result.append(body.split("!")[1])
|
||||
log.info("\n".join(result))
|
||||
except:
|
||||
log.error("could not generate case for interface id: %s, url: %s" % (interface.id, interface.in_url))
|
||||
log.error(traceback.print_exc())
|
||||
else:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % (interface.type, interface.in_url)
|
||||
log.warning(msg)
|
||||
case_instance.file.write_content_to_case_file(result_path, msg + "\n")
|
||||
3
base_framework/platform_tools/Testcase_service/readme
Normal file
3
base_framework/platform_tools/Testcase_service/readme
Normal file
@@ -0,0 +1,3 @@
|
||||
目录结构说明:
|
||||
使用者:王刚
|
||||
用途:存放自动生成RF层用例的脚本
|
||||
7
base_framework/platform_tools/__init__.py
Normal file
7
base_framework/platform_tools/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: linyupeng
|
||||
Email: linyupeng@huohua.cn
|
||||
Create Data: 2021/4/9 19:08
|
||||
"""
|
||||
266
base_framework/platform_tools/case_runner.py
Normal file
266
base_framework/platform_tools/case_runner.py
Normal file
@@ -0,0 +1,266 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import argparse
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service import SetAttr, SwaggerDBInfo, \
|
||||
KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path
|
||||
# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug
|
||||
from base_framework.platform_tools.Testcase_service.case_service import ITCaseContentOperation, \
|
||||
run_interface_case_generate, load_module_from_file
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody
|
||||
|
||||
dir_name = os.path.dirname(__file__)
|
||||
interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt")
|
||||
token_header = ["accesstoken", "user-token"]
|
||||
|
||||
|
||||
def get_all_interface_info_from_file(path):
|
||||
with open(path, encoding="UTF-8") as f:
|
||||
content = f.readlines()
|
||||
return content[1:]
|
||||
|
||||
|
||||
def write_interface_info_to_file(content, path=interface_path):
|
||||
with open(path, mode="r", encoding="UTF-8") as f:
|
||||
line = f.readline().strip("\n")
|
||||
with open(path, mode="w", encoding="UTF-8") as f1:
|
||||
f1.write(line + "\n" + content)
|
||||
|
||||
|
||||
def generate_result_file():
|
||||
file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
|
||||
result_dir = os.path.join(dir_name, "case_result")
|
||||
result_file = os.path.abspath(os.path.join(result_dir, file_name))
|
||||
if not os.path.exists(result_dir):
|
||||
os.makedirs(result_dir)
|
||||
with open(result_file, "w+", encoding="UTF-8") as f:
|
||||
f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime()))
|
||||
return result_file
|
||||
|
||||
|
||||
def write_case_result_to_case_file(file_path, content):
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
lines = f.readlines()
|
||||
text = content + "\n"
|
||||
if lines:
|
||||
if lines[-1] != "\n":
|
||||
text = "\n" + "\n" + content + "\n"
|
||||
|
||||
f.write(text)
|
||||
|
||||
|
||||
def clean_the_interface_info(result_file, interface_info):
|
||||
interface = tuple(interface_info.strip("\n").split(","))
|
||||
interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], interface[2])
|
||||
if len(interface_infos) > 1:
|
||||
msg = "接口url:%s,%s,存在多条数据,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif len(interface_infos) == 0:
|
||||
msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
interface_info_attr = SetAttr(interface_infos[0])
|
||||
if interface_info_attr.is_used == 0:
|
||||
msg = "接口url:%s,%s,%s,已不在使用,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif interface_info_attr.at_numbers > 0:
|
||||
msg = "接口url:%s,%s,%s,已存在用例,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
msg = "接口url:%s,%s,%s,开始生成接口关键字和用例!" % interface
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
return interface_info_attr
|
||||
return msg
|
||||
|
||||
|
||||
def generate_swagger_url_and_option(swagger_info):
|
||||
swagger_info_attr = SetAttr(swagger_info)
|
||||
url = swagger_info_attr.sw_url
|
||||
temp_list = url.split("v2")
|
||||
swagger_url = temp_list[0] + 'swagger-ui.html'
|
||||
option = '/v2' + temp_list[1]
|
||||
return swagger_url, option, temp_list[0]
|
||||
|
||||
|
||||
def get_demo_by_interface_info(interface_id):
|
||||
body_demo = GenerateBody(interface_id)
|
||||
request_data = body_demo.combine_request_parameters()
|
||||
if request_data["d"]:
|
||||
request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0)
|
||||
request_data["d"] = request_demo
|
||||
return request_data
|
||||
|
||||
|
||||
def clear_request_parameters(parameters):
|
||||
"""
|
||||
清除参数中作为token的参数
|
||||
"""
|
||||
parameters_result = copy.deepcopy(parameters)
|
||||
for item in parameters:
|
||||
if item.get("name").lower() in token_header:
|
||||
parameters_result.remove(item)
|
||||
return parameters_result
|
||||
|
||||
|
||||
def clear_header_token_demo(demo_info):
|
||||
"""
|
||||
清除请求头中带有token这种无用参数
|
||||
"""
|
||||
demo_copy = json.loads(copy.deepcopy(demo_info.request_demo))
|
||||
header = copy.deepcopy(demo_copy.get('h'))
|
||||
for key, _ in demo_copy.get('h').items():
|
||||
if key in token_header:
|
||||
header.pop(key)
|
||||
if not header:
|
||||
demo_copy['h'] = {}
|
||||
else:
|
||||
demo_copy['h'] = header
|
||||
demo_info.request_demo = json.dumps(demo_copy)
|
||||
return demo_info
|
||||
|
||||
|
||||
def generate_parameter_to_dict(parameters):
|
||||
"""
|
||||
将数据库查询到的参数以参数名组成字典
|
||||
"""
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
|
||||
def clean_un_use_parameter(demo, parameters):
|
||||
"""
|
||||
删除不再使用的参数
|
||||
"""
|
||||
if isinstance(demo, SetAttr):
|
||||
demo_item = json.loads(demo.request_demo)
|
||||
demo_copy = copy.deepcopy(demo_item)
|
||||
else:
|
||||
demo_item = copy.deepcopy(demo)
|
||||
demo_copy = copy.deepcopy(demo)
|
||||
parameters_dict = generate_parameter_to_dict(parameters)
|
||||
if isinstance(demo, list):
|
||||
demo_copy.clear()
|
||||
for index in range(len(demo_item)):
|
||||
demo_copy.append(clean_un_use_parameter(demo_item[index], parameters))
|
||||
elif isinstance(demo_item, dict):
|
||||
for key, values in demo_item.items():
|
||||
if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2:
|
||||
demo_copy.pop(key)
|
||||
continue
|
||||
demo_copy[key] = clean_un_use_parameter(values, parameters)
|
||||
else:
|
||||
demo_copy = demo_copy
|
||||
return demo_copy
|
||||
|
||||
|
||||
def confirm_demo_value_string(kw, case, demo_string, parameters):
|
||||
"""
|
||||
确认demo中的请求参数是不是纯值,并非json说格式
|
||||
"""
|
||||
demo = copy.deepcopy(demo_string.request_demo)
|
||||
demo_value = json.loads(demo)
|
||||
if (not demo_value["d"]) and (demo_value["d"] != 0):
|
||||
return demo_value
|
||||
if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif isinstance(demo_value["d"], list):
|
||||
if not isinstance(demo_value["d"][0], dict):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif 'empty' in demo_value['d']:
|
||||
kw.kw_array = demo_value['d'].pop('empty')
|
||||
return demo_value
|
||||
|
||||
|
||||
def confirm_correct_team_case(team):
|
||||
interface_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
try:
|
||||
module = load_module_from_file(interface_file_path, team.upper())
|
||||
real_team = team.upper()
|
||||
except Exception as err:
|
||||
real_team = team.lower()
|
||||
return real_team
|
||||
|
||||
|
||||
def run_interface_case(team, tags=None, platform=None, server=None, driver=None, normal=None):
|
||||
team = confirm_correct_team_case(team)
|
||||
kw_instance = KWFileOperation(team)
|
||||
result_file = generate_result_file()
|
||||
interface_contents = get_all_interface_info_from_file(interface_path)
|
||||
keyword = KWOperation(team, kw_instance, server)
|
||||
case = ITCaseContentOperation(team, kw_instance)
|
||||
for interface_temp in interface_contents:
|
||||
if interface_temp == "\n" or interface_temp.startswith("#"):
|
||||
continue
|
||||
interface = clean_the_interface_info(result_file, interface_temp)
|
||||
if not isinstance(interface, str):
|
||||
parameters = keyword.get_interface_parameters(interface.id)
|
||||
parameters = clear_request_parameters(parameters)
|
||||
if parameters:
|
||||
demo = get_demo_by_interface_info(interface.id)
|
||||
else:
|
||||
demo = {"d": {}, "h": {}, "p": {}, "u": {}}
|
||||
demo = [{"request_demo": json.dumps(demo)}]
|
||||
demo_info = SetAttr(demo[0])
|
||||
demo_info = clear_header_token_demo(demo_info)
|
||||
demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters))
|
||||
demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters))
|
||||
kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword)
|
||||
if kw_status:
|
||||
msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal)
|
||||
return True
|
||||
else:
|
||||
return interface
|
||||
|
||||
|
||||
def print_usage():
|
||||
parser_inst.print_usage()
|
||||
parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。")
|
||||
|
||||
|
||||
def write_platform_to_running_env(platform, team):
|
||||
if not platform:
|
||||
platform = ""
|
||||
cfg_opt = ReadConfig(env_choose_path)
|
||||
cfg_opt.set_section("run_jira_id", "huohua-podenv", platform)
|
||||
cfg_opt.set_section("run_evn_name", "current_team", team)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="请输入组名!")
|
||||
parser.add_argument("-team", dest='team', help="TO or TMO")
|
||||
parser.add_argument("-tag", dest='tags', help="标签", default=None)
|
||||
parser.add_argument("-p", dest='platform', help="独立环境", default=None)
|
||||
parser.add_argument("-s", dest='server', help="服务名", default=None)
|
||||
parser.add_argument("-d", dest='driver', help="驱动路径", default=None)
|
||||
parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None)
|
||||
args = parser.parse_args()
|
||||
global parser_inst
|
||||
parser_inst = parser
|
||||
if not args.team:
|
||||
print_usage()
|
||||
else:
|
||||
write_platform_to_running_env(args.platform, args.team)
|
||||
run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal)
|
||||
264
base_framework/platform_tools/case_runner1.py
Normal file
264
base_framework/platform_tools/case_runner1.py
Normal file
@@ -0,0 +1,264 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import argparse
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from base_framework.platform_tools.Keywords_service.keyword_service1 import SetAttr, SwaggerDBInfo, \
|
||||
KWFileOperation, log, KWOperation, run_keyword_generage, Project_Path
|
||||
# from base_framework.platform_tools.Swagger_scanner.scanner import SwaggerOnlineDebug
|
||||
from base_framework.platform_tools.Testcase_service.case_service1 import ITCaseContentOperation, \
|
||||
run_interface_case_generate, load_module_from_file
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.platform_tools.Swagger_scanner.scanner_add import GenerateBody
|
||||
|
||||
dir_name = os.path.dirname(__file__)
|
||||
interface_path = os.path.join(dir_name, "Swagger_scanner/interface.txt")
|
||||
token_header = ["accesstoken", "user-token"]
|
||||
|
||||
|
||||
def get_all_interface_info_from_file(path):
|
||||
with open(path, encoding="UTF-8") as f:
|
||||
content = f.readlines()
|
||||
return content[1:]
|
||||
|
||||
|
||||
def write_interface_info_to_file(content, path=interface_path):
|
||||
with open(path, mode="r", encoding="UTF-8") as f:
|
||||
line = f.readline().strip("\n")
|
||||
with open(path, mode="w", encoding="UTF-8") as f1:
|
||||
f1.write(line + "\n" + content)
|
||||
|
||||
|
||||
def generate_result_file():
|
||||
file_name = "reslut_%s.txt" % time.strftime("%Y_%m_%d_%H_%M_%S", time.localtime())
|
||||
result_dir = os.path.join(dir_name, "case_result")
|
||||
result_file = os.path.abspath(os.path.join(result_dir, file_name))
|
||||
if not os.path.exists(result_dir):
|
||||
os.makedirs(result_dir)
|
||||
with open(result_file, "w+", encoding="UTF-8") as f:
|
||||
f.write("开始自动生成keyword和case! 开始时间:%s\n" % time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime()))
|
||||
return result_file
|
||||
|
||||
|
||||
def write_case_result_to_case_file(file_path, content):
|
||||
with open(file_path, "a+", encoding="UTF-8") as f:
|
||||
lines = f.readlines()
|
||||
text = content + "\n"
|
||||
if lines:
|
||||
if lines[-1] != "\n":
|
||||
text = "\n" + "\n" + content + "\n"
|
||||
|
||||
f.write(text)
|
||||
|
||||
|
||||
def clean_the_interface_info(result_file, interface_info, team):
|
||||
interface = tuple(interface_info.strip("\n").split(","))
|
||||
interface_infos = SwaggerDBInfo.get_interface_info_by_url_and_type(interface[1], interface[0], team)
|
||||
if len(interface_infos) > 1:
|
||||
msg = "接口url:%s,%s,存在多条数据,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif len(interface_infos) == 0:
|
||||
msg = "接口url:%s,%s,在数据库中不存在,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
interface_info_attr = SetAttr(interface_infos[0])
|
||||
if interface_info_attr.is_used == 0:
|
||||
msg = "接口url:%s,%s,已不在使用,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
elif interface_info_attr.at_numbers > 0:
|
||||
msg = "接口url:%s,%s,已存在用例,请确认!" % interface
|
||||
log.warning(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
else:
|
||||
msg = "接口url:%s,%s,开始生成接口关键字和用例!" % interface
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
return interface_info_attr
|
||||
return msg
|
||||
|
||||
|
||||
def generate_swagger_url_and_option(swagger_info):
|
||||
swagger_info_attr = SetAttr(swagger_info)
|
||||
url = swagger_info_attr.sw_url
|
||||
temp_list = url.split("v2")
|
||||
swagger_url = temp_list[0] + 'swagger-ui.html'
|
||||
option = '/v2' + temp_list[1]
|
||||
return swagger_url, option, temp_list[0]
|
||||
|
||||
|
||||
def get_demo_by_interface_info(interface_id):
|
||||
body_demo = GenerateBody(interface_id)
|
||||
request_data = body_demo.combine_request_parameters()
|
||||
if request_data["d"]:
|
||||
request_demo = body_demo.combine_request_data(request_data["d"][0], request_data["d"][1], key=0)
|
||||
request_data["d"] = request_demo
|
||||
return request_data
|
||||
|
||||
|
||||
def clear_request_parameters(parameters):
|
||||
"""
|
||||
清除参数中作为token的参数
|
||||
"""
|
||||
parameters_result = copy.deepcopy(parameters)
|
||||
for item in parameters:
|
||||
if item.get("name").lower() in token_header:
|
||||
parameters_result.remove(item)
|
||||
return parameters_result
|
||||
|
||||
|
||||
def clear_header_token_demo(demo_info):
|
||||
"""
|
||||
清除请求头中带有token这种无用参数
|
||||
"""
|
||||
demo_copy = json.loads(copy.deepcopy(demo_info.request_demo))
|
||||
header = copy.deepcopy(demo_copy.get('h'))
|
||||
for key, _ in demo_copy.get('h').items():
|
||||
if key in token_header:
|
||||
header.pop(key)
|
||||
if not header:
|
||||
demo_copy['h'] = {}
|
||||
else:
|
||||
demo_copy['h'] = header
|
||||
demo_info.request_demo = json.dumps(demo_copy)
|
||||
return demo_info
|
||||
|
||||
|
||||
def generate_parameter_to_dict(parameters):
|
||||
"""
|
||||
将数据库查询到的参数以参数名组成字典
|
||||
"""
|
||||
dict_temp = dict()
|
||||
for parameter in parameters:
|
||||
parameter_attr = SetAttr(parameter)
|
||||
dict_temp[parameter_attr.name] = parameter_attr
|
||||
return dict_temp
|
||||
|
||||
|
||||
def clean_un_use_parameter(demo, parameters):
|
||||
"""
|
||||
删除不再使用的参数
|
||||
"""
|
||||
if isinstance(demo, SetAttr):
|
||||
demo_item = json.loads(demo.request_demo)
|
||||
demo_copy = copy.deepcopy(demo_item)
|
||||
else:
|
||||
demo_item = copy.deepcopy(demo)
|
||||
demo_copy = copy.deepcopy(demo)
|
||||
parameters_dict = generate_parameter_to_dict(parameters)
|
||||
if isinstance(demo, list):
|
||||
demo_copy.clear()
|
||||
for index in range(len(demo_item)):
|
||||
demo_copy.append(clean_un_use_parameter(demo_item[index], parameters))
|
||||
elif isinstance(demo_item, dict):
|
||||
for key, values in demo_item.items():
|
||||
if parameters_dict.get(key, None) and int(parameters_dict.get(key).is_need) == 2:
|
||||
demo_copy.pop(key)
|
||||
continue
|
||||
demo_copy[key] = clean_un_use_parameter(values, parameters)
|
||||
else:
|
||||
demo_copy = demo_copy
|
||||
return demo_copy
|
||||
|
||||
|
||||
def confirm_demo_value_string(kw, case, demo_string, parameters):
|
||||
"""
|
||||
确认demo中的请求参数是不是纯值,并非json说格式
|
||||
"""
|
||||
demo = copy.deepcopy(demo_string.request_demo)
|
||||
demo_value = json.loads(demo)
|
||||
if (not demo_value["d"]) and (demo_value["d"] != 0):
|
||||
return demo_value
|
||||
if (isinstance(demo_value["d"], str) or isinstance(demo_value["d"], int)):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
elif isinstance(demo_value["d"], list):
|
||||
if not isinstance(demo_value["d"][0], dict):
|
||||
demo_value["d"] = {parameters[0].get("name"): demo_value["d"]}
|
||||
kw.kw_string = True
|
||||
case.case_string = True
|
||||
return demo_value
|
||||
|
||||
|
||||
def confirm_correct_team_case(team):
|
||||
interface_file_path = os.path.abspath(os.path.join(Project_Path,
|
||||
"{team}".format(team=team), "library",
|
||||
"{team}_interface.py".format(team=team.upper())))
|
||||
try:
|
||||
module = load_module_from_file(interface_file_path, team.upper())
|
||||
real_team = team.upper()
|
||||
except Exception as err:
|
||||
real_team = team.lower()
|
||||
return real_team
|
||||
|
||||
|
||||
def run_interface_case(team, tags=None, server=None, normal=None):
|
||||
team = confirm_correct_team_case(team)
|
||||
kw_instance = KWFileOperation(team, server)
|
||||
result_file = generate_result_file()
|
||||
interface_contents = get_all_interface_info_from_file(interface_path)
|
||||
keyword = KWOperation(team, kw_instance)
|
||||
case = ITCaseContentOperation(team, kw_instance)
|
||||
for interface_temp in interface_contents:
|
||||
if interface_temp == "\n" or interface_temp.startswith("#"):
|
||||
continue
|
||||
interface = clean_the_interface_info(result_file, interface_temp, team)
|
||||
if not isinstance(interface, str):
|
||||
parameters = keyword.get_interface_parameters(interface.id)
|
||||
parameters = clear_request_parameters(parameters)
|
||||
if parameters:
|
||||
demo = get_demo_by_interface_info(interface.id)
|
||||
else:
|
||||
demo = {"d": {}, "h": {}, "p": {}, "u": {}}
|
||||
demo = [{"request_demo": json.dumps(demo)}]
|
||||
demo_info = SetAttr(demo[0])
|
||||
demo_info = clear_header_token_demo(demo_info)
|
||||
demo_info.request_demo = json.dumps(clean_un_use_parameter(demo_info, parameters))
|
||||
demo_info.request_demo = json.dumps(confirm_demo_value_string(keyword, case, demo_info, parameters))
|
||||
kw_status = run_keyword_generage(result_file, interface, demo_info, parameters, keyword)
|
||||
if kw_status:
|
||||
msg = "接口url:%s,%s,生成keyword成功!接下来继续生成用例!" % (interface.type, interface.in_url)
|
||||
log.info(msg)
|
||||
write_case_result_to_case_file(result_file, msg)
|
||||
run_interface_case_generate(result_file, interface, demo_info, parameters, case, tags, normal)
|
||||
return True
|
||||
else:
|
||||
return interface
|
||||
|
||||
|
||||
def print_usage():
|
||||
parser_inst.print_usage()
|
||||
parser_inst.exit(1, "生成用例失败,请根据使用帮助传入正确参数。")
|
||||
|
||||
|
||||
def write_platform_to_running_env(platform, team):
|
||||
if not platform:
|
||||
platform = ""
|
||||
cfg_opt = ReadConfig(env_choose_path)
|
||||
cfg_opt.set_section("run_jira_id", "huohua-podenv", platform)
|
||||
cfg_opt.set_section("run_evn_name", "current_team", team)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="请输入组名!")
|
||||
parser.add_argument("-team", dest='team', help="TO or TMO")
|
||||
parser.add_argument("-tag", dest='tags', help="标签", default=None)
|
||||
parser.add_argument("-p", dest='platform', help="独立环境", default=None)
|
||||
parser.add_argument("-s", dest='server', help="服务名", default=None)
|
||||
parser.add_argument("-d", dest='driver', help="驱动路径", default=None)
|
||||
parser.add_argument("-n", dest='normal', help="是否只生成正常用例", default=None)
|
||||
args = parser.parse_args()
|
||||
global parser_inst
|
||||
parser_inst = parser
|
||||
if not args.team:
|
||||
print_usage()
|
||||
else:
|
||||
write_platform_to_running_env(args.platform, args.team)
|
||||
run_interface_case(args.team, args.tags, args.platform, args.server, args.driver, args.normal)
|
||||
105
base_framework/platform_tools/case_runner10.py
Normal file
105
base_framework/platform_tools/case_runner10.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__author__ = 'huaxuemin'
|
||||
|
||||
import argparse
|
||||
from runner9 import CaseRunner
|
||||
usage_info = '''
|
||||
usage:
|
||||
$ case_runner.py -w %WORKSPACE% -c %case_path% -t tc1,tc2 -i p0,p1,p2 -e norun,del -r True
|
||||
run tag cases in workspace
|
||||
'''
|
||||
|
||||
|
||||
def usage():
|
||||
print(usage_info)
|
||||
exit(-1)
|
||||
|
||||
|
||||
def get_test_case_name(build_id):
|
||||
try:
|
||||
if build_id:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0", database="sparkatp",
|
||||
charset='utf8')
|
||||
cursor = db.cursor()
|
||||
get_sql = "SELECT test_case_id FROM sparkatp.build_params WHERE build_info_id={}".format(build_id)
|
||||
cursor.execute(get_sql)
|
||||
res = cursor.fetchone()
|
||||
test_case_name = ""
|
||||
if res:
|
||||
test_case_name = res[0].replace("(", "*").replace(")", "*").replace(" ", "*")
|
||||
cursor.close()
|
||||
db.close()
|
||||
return test_case_name
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="This is for introduction parameter")
|
||||
parser.add_argument("-w", dest='workspace', help="case_runner workspace")
|
||||
parser.add_argument("-c", dest='case_path', help="case path")
|
||||
parser.add_argument("-t", dest='test_case', default='', help="test case")
|
||||
parser.add_argument("-i", dest='include', default='', help="include tag")
|
||||
parser.add_argument("-e", dest='exclude', default='', help="exclude tag")
|
||||
parser.add_argument("-r", dest='rerun', default='true', help="rerun failed case")
|
||||
parser.add_argument("-env", dest='special_env', default='', help="special env")
|
||||
parser.add_argument("-team", dest='team', default='', help="job name with team ")
|
||||
parser.add_argument("-penv", dest='physics_env', help="QA or SIM", default='QA')
|
||||
parser.add_argument("-b", dest='business', help="hh or hhi", default='hh')
|
||||
parser.add_argument("-b_id", dest='build_info_id', help="build_info_id", default='')
|
||||
parser.add_argument("-b_url", dest='build_url', help="build_url", default='')
|
||||
parser.add_argument("-is_db", dest='is_use_db', help="is_use_db", default='0')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.workspace is None:
|
||||
usage()
|
||||
|
||||
workspace = args.workspace
|
||||
case_path = args.case_path
|
||||
test_case = args.test_case
|
||||
include = args.include
|
||||
exclude = args.exclude
|
||||
rerun = args.rerun
|
||||
special_env = args.special_env
|
||||
team = args.team.split("-")[0]
|
||||
physics_env = args.physics_env
|
||||
business = args.business
|
||||
build_info_id = args.build_info_id
|
||||
build_url = args.build_url
|
||||
is_use_db = args.is_use_db
|
||||
if str(is_use_db) == "1" and build_info_id:
|
||||
test_case_name = get_test_case_name(build_info_id)
|
||||
test_case = test_case_name if test_case_name else test_case
|
||||
|
||||
# if team not in ["CC", "EN", "H2R", "LaLive", "SCM", "TMO", "TO", "ASOP", "ASTWB", "ASORG"]:
|
||||
# team = ""
|
||||
|
||||
case_runner = CaseRunner(workspace, case_path, test_case, include, exclude, rerun, special_env, team, physics_env,
|
||||
business)
|
||||
try:
|
||||
# 更新独立环境代号到配置文件
|
||||
case_runner.update_platform()
|
||||
# 检查工作目录名字对应的端口,有就使用,没有就分配
|
||||
case_runner.assign_port()
|
||||
# 查找对应端口进程并杀掉
|
||||
case_runner.check_port()
|
||||
# 更新KWL和RF中的端口
|
||||
case_runner.update_port()
|
||||
# 更新重名测试套
|
||||
case_runner.update_suite()
|
||||
# 启动KWL
|
||||
case_runner.start_kwl()
|
||||
# 构建用例
|
||||
case_runner.run_cases()
|
||||
# 构建完成后,停止KWL
|
||||
case_runner.check_port()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
# 构建完成后,入库build_url
|
||||
case_runner.record_build_url(build_info_id, build_url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
683
base_framework/platform_tools/case_runner9.py
Normal file
683
base_framework/platform_tools/case_runner9.py
Normal file
@@ -0,0 +1,683 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
__author__ = 'huaxuemin'
|
||||
|
||||
from logbook import Logger
|
||||
import shutil
|
||||
import errno
|
||||
import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
import random
|
||||
import socket
|
||||
import configparser
|
||||
import codecs
|
||||
import time
|
||||
import json
|
||||
import re
|
||||
import requests
|
||||
|
||||
logging = Logger(__name__)
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
ROBOT_LOG_LEVEL = 'INFO'
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
|
||||
class OSType:
|
||||
WIN, LINUX, UNKNOWN = range(3)
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_type():
|
||||
import platform
|
||||
system_name = platform.system()
|
||||
if system_name.lower() == 'windows':
|
||||
return OSType.WIN
|
||||
elif system_name.lower() == 'linux':
|
||||
return OSType.LINUX
|
||||
else:
|
||||
return OSType.UNKNOWN
|
||||
|
||||
|
||||
def run_process(cmd_str, out_p=False):
|
||||
"""
|
||||
run command
|
||||
cmd_str unicode string.
|
||||
"""
|
||||
if OSType.WIN == OSType.get_type():
|
||||
# cmd_str = cmd_str.encode('gbk')
|
||||
cmd_str = cmd_str
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
cmd_str = cmd_str.encode('utf-8')
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
logging.info('cmd: %s' % cmd_str)
|
||||
print(cmd_str)
|
||||
import subprocess
|
||||
from subprocess import PIPE
|
||||
close_fds = False if OSType.WIN == OSType.get_type() else True
|
||||
if out_p:
|
||||
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=PIPE)
|
||||
p.wait()
|
||||
return p.returncode, p.stdout.read()
|
||||
else:
|
||||
p = subprocess.Popen(cmd_str, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL)
|
||||
p.wait()
|
||||
return p.returncode, None
|
||||
|
||||
|
||||
class CaseRunner:
|
||||
|
||||
def __init__(self, workspace, case_path, test_case, include, exclude, rerun="true", special_env="", team="",
|
||||
physics_env="QA", business="hh"):
|
||||
self.workspace = workspace
|
||||
self.case_path = case_path
|
||||
self.test_case = test_case
|
||||
self.include = include
|
||||
self.exclude = exclude
|
||||
self.rerun = rerun
|
||||
self.special_env = special_env
|
||||
self.team = team
|
||||
self.physics_env = physics_env
|
||||
self.business = business
|
||||
self.con = sqlite3.connect(HERE + "/case_runner.db")
|
||||
self.cur = self.con.cursor()
|
||||
self.def_port = set("9" + "".join(map(str, random.choices(range(10), k=3))) for i in range(500))
|
||||
self.env_port = None
|
||||
self.pid = None
|
||||
self.wait_time = 120
|
||||
self.all_dir_name = set()
|
||||
self.galaxy_server_name_to_swagger = {"peppa-teach-opt-cms-api": "teach-opt-cms-api",
|
||||
"peppa-teach-biz-server": "peppa-teach-biz",
|
||||
"peppa-market-server": "peppa-market",
|
||||
"peppa-teach-parker-server": "peppa-teach-parker",
|
||||
"peppa-course-server": "peppa-course"}
|
||||
self.swagger_name_to_galaxy = {"teach-opt-cms-api": "peppa-teach-opt-cms-api",
|
||||
"peppa-teach-biz": "peppa-teach-biz-server",
|
||||
"peppa-market": "peppa-market-server",
|
||||
"peppa-teach-parker": "peppa-teach-parker-server",
|
||||
"peppa-course": "peppa-course-server"}
|
||||
# 获取opengalaxy ssotoken相关变量
|
||||
self.ops_uri = "http://opengalaxy.bg.huohua.cn"
|
||||
self.showUsername = "luohong"
|
||||
self.username = "luohong"
|
||||
self.password = "Lh123456789@"
|
||||
self.sso_login_url = "https://sso.huohua.cn/authentication/form"
|
||||
self.redirect_url = "https://sso.huohua.cn/oauth/authorize?client_id=open-galaxy&response_type=code&redirect_uri=http://opengalaxy.bg.huohua.cn/api/v1/users/user/ssologin/"
|
||||
|
||||
def getSsoToken(self):
|
||||
session = requests.session()
|
||||
post_data = dict()
|
||||
post_data['showUsername'] = self.showUsername
|
||||
post_data['username'] = self.username
|
||||
post_data['password'] = self.password
|
||||
session.post(url=self.sso_login_url, data=post_data, allow_redirects=True, verify=False)
|
||||
resp = session.get(
|
||||
url=self.redirect_url,
|
||||
allow_redirects=False,
|
||||
verify=False)
|
||||
resp1 = session.get(
|
||||
url=resp.headers['Location'],
|
||||
allow_redirects=False,
|
||||
verify=False)
|
||||
ssoToken = resp1.headers["Set-Cookie"].split(";")[4].split("=")[2]
|
||||
return ssoToken
|
||||
|
||||
def update_platform(self):
|
||||
if len(self.special_env) > 0:
|
||||
config_file = str(Path(self.workspace) / "base_framework/base_config/env_choose.ini")
|
||||
fd = open(config_file, encoding='utf-8')
|
||||
data = fd.read()
|
||||
if data[:3] == codecs.BOM_UTF8:
|
||||
data = data[3:]
|
||||
files = codecs.open(config_file, "w")
|
||||
files.write(data)
|
||||
files.close()
|
||||
fd.close()
|
||||
|
||||
cf = configparser.ConfigParser(allow_no_value=True)
|
||||
cf.read(config_file, encoding='utf-8')
|
||||
cf.set("run_jira_id", "huohua-podenv", self.special_env)
|
||||
with open(config_file, 'w') as fw: # 循环写入
|
||||
cf.write(fw)
|
||||
|
||||
def assign_port(self):
|
||||
workspace_base_name = Path(self.workspace).name
|
||||
query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name)
|
||||
res_port = self.cur.execute(query_port).fetchall()
|
||||
query_all_port = "SELECT PORT FROM ENV_PORT"
|
||||
res_all_port = self.cur.execute(query_all_port).fetchall()
|
||||
not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop()
|
||||
print("not use port is {}".format(not_use_port))
|
||||
self.env_port = not_use_port
|
||||
if len(res_port) == 0:
|
||||
insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)"
|
||||
self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall()
|
||||
self.con.commit()
|
||||
else:
|
||||
update_name_port_sql = "UPDATE ENV_PORT SET PORT='{}' WHERE NAME='{}'".format(self.env_port,
|
||||
workspace_base_name)
|
||||
self.cur.execute(update_name_port_sql).fetchall()
|
||||
self.con.commit()
|
||||
|
||||
self.cur.close()
|
||||
self.con.close()
|
||||
print("use port is {}".format(self.env_port))
|
||||
|
||||
# def assign_port(self):
|
||||
# workspace_base_name = Path(self.workspace).name
|
||||
# query_port = "SELECT PORT FROM ENV_PORT WHERE NAME='{}'".format(workspace_base_name)
|
||||
# res = self.cur.execute(query_port).fetchall()
|
||||
# if len(res) == 0:
|
||||
# query_all_port = "SELECT PORT FROM ENV_PORT"
|
||||
# res_all_port = self.cur.execute(query_all_port).fetchall()
|
||||
# not_use_port = (self.def_port - set(str(item[0]) for item in res_all_port)).pop()
|
||||
# print("not use port is {}".format(not_use_port))
|
||||
# insert_name_port_sql = "INSERT INTO ENV_PORT(NAME,PORT)VALUES(?,?)"
|
||||
# self.cur.execute(insert_name_port_sql, (workspace_base_name, not_use_port)).fetchall()
|
||||
# self.con.commit()
|
||||
# self.env_port = not_use_port
|
||||
# else:
|
||||
# self.env_port = str(res[0][0])
|
||||
# self.cur.close()
|
||||
# self.con.close()
|
||||
# print("use port is {}".format(self.env_port))
|
||||
|
||||
def _get_not_used_port(self):
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
if len(res_context) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def check_port(self):
|
||||
if OSType.WIN == OSType.get_type():
|
||||
find_pid_win_cmd = 'netstat -ano | findstr {} | findstr LISTENING'.format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_win_cmd, out_p=True)
|
||||
if res_code == 0:
|
||||
print(res_context)
|
||||
if len(res_context) > 0:
|
||||
try:
|
||||
self.pid = str(res_context).split()[-1].replace("\\r\\n'", "")
|
||||
self._kill_pid()
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
if res_code == 0:
|
||||
print(res_context)
|
||||
# 获取pid
|
||||
if len(res_context) > 0:
|
||||
try:
|
||||
self.pid = str(res_context).split("\\n")[1].split()[1]
|
||||
self._kill_3_pid()
|
||||
except IndexError:
|
||||
pass
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
def _kill_3_pid(self):
|
||||
self._kill_pid()
|
||||
count = 3
|
||||
while count > 0:
|
||||
find_pid_linux_cmd = "lsof -i:{}".format(self.env_port)
|
||||
res_code, res_context = run_process(find_pid_linux_cmd, out_p=True)
|
||||
|
||||
if len(res_context) > 0:
|
||||
time.sleep(2)
|
||||
self.pid = str(res_context).split("\\n")[1].split()[1]
|
||||
self._kill_pid()
|
||||
count -= 1
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
def _kill_pid(self):
|
||||
if OSType.WIN == OSType.get_type():
|
||||
kill_pid_cmd = "taskkill /f /pid {}".format(self.pid)
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
kill_pid_cmd = "kill -9 {}".format(self.pid)
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
|
||||
res_code, res_context = run_process(kill_pid_cmd)
|
||||
if res_code:
|
||||
raise RuntimeError("kill pid: {} failed. error: {}".format(self.pid, res_context))
|
||||
|
||||
def update_port(self):
|
||||
main_py = Path(self.workspace) / "base_framework/main.py"
|
||||
tmp_py = Path(self.workspace) / "base_framework/tmp.py"
|
||||
shutil.copy(main_py, tmp_py)
|
||||
src_context = "port=9999"
|
||||
dst_context = "port={}".format(self.env_port)
|
||||
self._replace_file_context(tmp_py, main_py, src_context, dst_context)
|
||||
env_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/env.robot".format(self.team)
|
||||
tmp_robot = Path(self.workspace) / "{}/test_case/Resource/AdapterKws/tmp.robot".format(self.team)
|
||||
shutil.copy(env_robot, tmp_robot)
|
||||
src_context = "127.0.0.1:9999"
|
||||
dst_context = "127.0.0.1:{}".format(self.env_port)
|
||||
self._replace_file_context(tmp_robot, env_robot, src_context, dst_context)
|
||||
|
||||
def update_suite(self):
|
||||
case_dir = Path(self.workspace) / "{}/test_case".format(self.team)
|
||||
self._set_path(case_dir)
|
||||
|
||||
case_src_path = Path(self.case_path)
|
||||
if case_src_path.is_file():
|
||||
base_name = case_src_path.name.split(case_src_path.suffix)[0]
|
||||
if base_name.upper() in self.all_dir_name:
|
||||
self.case_path = str(case_src_path.with_name(base_name + "9" + case_src_path.suffix))
|
||||
|
||||
self._replace_path(case_dir)
|
||||
|
||||
def _set_path(self, src_path):
|
||||
for p in src_path.iterdir():
|
||||
if p.is_dir():
|
||||
self.all_dir_name.add(p.name.upper())
|
||||
self._set_path(p)
|
||||
|
||||
def _replace_path(self, src_path):
|
||||
for p in src_path.iterdir():
|
||||
if p.is_dir():
|
||||
self._replace_path(p)
|
||||
elif p.is_file():
|
||||
base_name = p.name.split(p.suffix)[0]
|
||||
if base_name.upper() in self.all_dir_name:
|
||||
p.rename(p.with_name(base_name + "9" + p.suffix))
|
||||
|
||||
def start_kwl(self):
|
||||
startup_path = Path(self.workspace) / "base_framework"
|
||||
startup = "startup.py"
|
||||
exec_cmd_params = ""
|
||||
if len(self.special_env) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-j {} ".format(self.special_env)
|
||||
if len(self.team) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-t {} ".format(self.team)
|
||||
if len(self.business) > 0:
|
||||
exec_cmd_params = exec_cmd_params + "-b {} ".format(self.business)
|
||||
if OSType.WIN == OSType.get_type():
|
||||
exec_cmd_path = "cd {} && python {} ".format(startup_path, startup)
|
||||
exec_cmd = exec_cmd_path + exec_cmd_params
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
exec_cmd_path = "cd {} && python3 {} ".format(startup_path, startup)
|
||||
exec_cmd = exec_cmd_path + exec_cmd_params
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
exec_cmd = exec_cmd[:-1]
|
||||
# import subprocess
|
||||
# from subprocess import PIPE
|
||||
# close_fds = False if OSType.WIN == OSType.get_type() else True
|
||||
# subprocess.Popen(exec_cmd, shell=True, close_fds=close_fds, stdout=subprocess.DEVNULL)
|
||||
try:
|
||||
pro = self._start_kwl_server(exec_cmd)
|
||||
if not pro.is_alive():
|
||||
print("--------pro.exec_status: ", pro.is_alive())
|
||||
pro.kill()
|
||||
time.sleep(5)
|
||||
self._kill_3_pid()
|
||||
self._start_kwl_server(exec_cmd)
|
||||
except Exception as e:
|
||||
print("--------error: ", e)
|
||||
|
||||
@staticmethod
|
||||
def _start_kwl_server(exec_cmd):
|
||||
from multiprocessing import Process
|
||||
p = Process(target=run_process, args=(exec_cmd,))
|
||||
p.daemon = True
|
||||
p.start()
|
||||
time.sleep(30)
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def _replace_file_context(src_file, dst_file, src_context, dst_context):
|
||||
with open(src_file, "r") as f_src:
|
||||
with open(dst_file, "w") as f_dst:
|
||||
for line in f_src:
|
||||
if src_context in line:
|
||||
f_dst.writelines(line.replace(src_context, dst_context))
|
||||
else:
|
||||
f_dst.writelines(line)
|
||||
|
||||
def _get_report_dir(self):
|
||||
"""
|
||||
Create %WORKSPACE%/Report 用于存放对应构建的构建日志
|
||||
"""
|
||||
report_dir = os.path.join(self.workspace, 'Report')
|
||||
if not os.path.exists(report_dir):
|
||||
logging.info(u'create report directory: %s' % report_dir)
|
||||
os.makedirs(report_dir)
|
||||
|
||||
return report_dir
|
||||
|
||||
def _get_report_ci_out_dir(self):
|
||||
"""
|
||||
Create %WORKSPACE%/Report/ci_out,用于存放构建日志
|
||||
"""
|
||||
ci_out_dir = os.path.join(self.workspace, 'Report', 'ci_out')
|
||||
if os.path.exists(ci_out_dir):
|
||||
logging.info(u'create ci output directory: %s' % ci_out_dir)
|
||||
shutil.rmtree(ci_out_dir)
|
||||
|
||||
return ci_out_dir
|
||||
|
||||
@staticmethod
|
||||
def copy_any_thing(src, dst):
|
||||
try:
|
||||
shutil.copytree(src, dst)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOTDIR:
|
||||
shutil.copy(src, dst)
|
||||
else:
|
||||
raise
|
||||
|
||||
def _wait_kwl_run(self):
|
||||
i = 0
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
while i < self.wait_time:
|
||||
try:
|
||||
s.connect(("127.0.0.1", int(self.env_port)))
|
||||
s.shutdown(2)
|
||||
s.close()
|
||||
return
|
||||
except socket.error:
|
||||
i += 1
|
||||
s.close()
|
||||
|
||||
def run_cases(self):
|
||||
"""
|
||||
run cases by workspace include exclude.
|
||||
"""
|
||||
self._wait_kwl_run()
|
||||
report_dir = os.path.join(self.workspace, 'Report')
|
||||
if os.path.exists(report_dir):
|
||||
logging.info(u'clean report directory: %s' % report_dir)
|
||||
shutil.rmtree(report_dir)
|
||||
os.makedirs(report_dir)
|
||||
|
||||
if self.rerun == 'true' or self.rerun == '':
|
||||
self._rerun_failed_cases()
|
||||
else:
|
||||
self._default_run_cases()
|
||||
|
||||
out_dir = os.path.join(self._get_report_dir(), 'out')
|
||||
if os.path.exists(out_dir):
|
||||
# copy report to ci_out.
|
||||
self.copy_any_thing(self._get_report_dir(), self._get_report_ci_out_dir())
|
||||
else:
|
||||
raise RuntimeError(u'%s directory is not exist.' % out_dir)
|
||||
|
||||
def _default_run_cases(self):
|
||||
"""
|
||||
exec robot and output log to {report_dir}/out directory.
|
||||
return command execute exit code.
|
||||
"""
|
||||
|
||||
test_case_cmdstr = []
|
||||
if len(self.test_case) > 0:
|
||||
for tc in self.test_case.split(','):
|
||||
test_case_cmdstr.append(u'--test %s' % tc)
|
||||
|
||||
includes_cmdstr = []
|
||||
if len(self.include) > 0:
|
||||
for include in self.include.split(','):
|
||||
includes_cmdstr.append(u'--include %s' % include)
|
||||
|
||||
excludes_cmdstr = []
|
||||
if len(self.exclude) > 0:
|
||||
for exclude in self.exclude.split(','):
|
||||
excludes_cmdstr.append(u'--exclude %s' % exclude)
|
||||
output_directory = os.path.join(self._get_report_dir(), 'out')
|
||||
Path(output_directory).mkdir(exist_ok=True)
|
||||
|
||||
# Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog.txt')
|
||||
os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL
|
||||
|
||||
logging.info(u'building cases...')
|
||||
res_code, _ = run_process(' '.join(
|
||||
[u'robot'] + test_case_cmdstr + excludes_cmdstr + includes_cmdstr + [u'-d', output_directory,
|
||||
self.case_path]))
|
||||
|
||||
# clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = 'NONE'
|
||||
|
||||
return res_code
|
||||
|
||||
def _rerun_failed_cases(self):
|
||||
"""
|
||||
execute robot twice(the second execute is only failed in first.)
|
||||
and remerge output to {report_dir}/out directory.
|
||||
"""
|
||||
|
||||
logging.info('rerunfailed cases mode...')
|
||||
|
||||
return_code = self._default_run_cases()
|
||||
logging.info('first run exit code: %s' % return_code)
|
||||
if not return_code:
|
||||
logging.info('first cases built successfully')
|
||||
return
|
||||
|
||||
logging.info('the first cases to build there is failure cases')
|
||||
output_directory = os.path.join(self._get_report_dir(), u'out')
|
||||
|
||||
if not os.path.exists(output_directory):
|
||||
raise RuntimeError(u'the first cases to built throw exception.')
|
||||
|
||||
output_directory_r1 = os.path.join(self._get_report_dir(), u'first_out')
|
||||
output_directory_r2 = os.path.join(self._get_report_dir(), u'second_out')
|
||||
logging.info('rename the first cases to build the output directory')
|
||||
cur_dir = os.getcwd()
|
||||
os.chdir(self._get_report_dir())
|
||||
if OSType.WIN == OSType.get_type():
|
||||
cmd_str = 'ren out first_out'
|
||||
elif OSType.LINUX == OSType.get_type():
|
||||
cmd_str = 'cp -R out first_out'
|
||||
else:
|
||||
raise RuntimeError("your os is not support.")
|
||||
os.system(cmd_str)
|
||||
os.chdir(cur_dir)
|
||||
|
||||
output_directory_cmdstr = [u'-d', output_directory_r2]
|
||||
rerun_failed_cmdstr = [u'--rerunfailed', os.path.join(output_directory_r1, u'output.xml')]
|
||||
|
||||
logging.info('rerunfailed test cases...')
|
||||
run_process(' '.join([u'robot'] + output_directory_cmdstr + rerun_failed_cmdstr + [self.case_path]))
|
||||
|
||||
if not os.path.exists(output_directory_r2):
|
||||
raise RuntimeError(u'the second cases to built throw throw exception.')
|
||||
|
||||
# Set ROBOT_SYSLOG_FILE and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = os.path.join(self._get_report_dir(), 'robot_syslog2.txt')
|
||||
os.environ['ROBOT_SYSLOG_LEVEL'] = ROBOT_LOG_LEVEL
|
||||
|
||||
logging.info('merge report...')
|
||||
run_process(' '.join([u'rebot', u'-d', output_directory, u'-o', u'output.xml', u'--merge',
|
||||
os.path.join(output_directory_r1, u'output.xml'),
|
||||
os.path.join(output_directory_r2, u'output.xml')]))
|
||||
# clear ROBOT_SYSLOG_FILE(NONE) and ROBOT_SYSLOG_LEVEL environment variable.
|
||||
os.environ['ROBOT_SYSLOG_FILE'] = 'NONE'
|
||||
|
||||
def record_build_url(self, build_id, report_url):
|
||||
try:
|
||||
if build_id:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="mysql.qa.huohua.cn", user="qa-dev", password="jaeg3SCQt0",
|
||||
database="sparkatp", charset='utf8')
|
||||
cursor = db.cursor()
|
||||
if build_id and report_url:
|
||||
update_sql = "UPDATE sparkatp.build_info set report_url='{}',status=2 WHERE id={}".format(
|
||||
report_url, build_id)
|
||||
cursor.execute(update_sql)
|
||||
cursor.fetchall()
|
||||
try:
|
||||
self.get_jacoco_report(cursor, build_id)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
db.commit()
|
||||
cursor.close()
|
||||
db.close()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_tester_by_project_id(self):
|
||||
"""
|
||||
获取project_id和tester
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
import pymysql
|
||||
db = pymysql.connect(host="10.250.200.53", user="root", password="peppa@test", database="tools",
|
||||
charset='utf8')
|
||||
cursor = db.cursor()
|
||||
|
||||
db.commit()
|
||||
cursor.close()
|
||||
db.close()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_project_id_by_build_id(self, cursor, build_id):
|
||||
try:
|
||||
if build_id:
|
||||
get_scene_id_sql = "SELECT scene_id FROM build_info where id='{}'".format(
|
||||
build_id)
|
||||
cursor.execute(get_scene_id_sql)
|
||||
scene_id_info = cursor.fetchone()
|
||||
if scene_id_info:
|
||||
scene_id = scene_id_info[0]
|
||||
get_project_id_sql = "SELECT project_id FROM scene_new where id='{}'".format(
|
||||
scene_id)
|
||||
cursor.execute(get_project_id_sql)
|
||||
project_id_info = cursor.fetchone()
|
||||
if project_id_info:
|
||||
project_id = project_id_info[0]
|
||||
return project_id
|
||||
return 0
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def get_jacoco_report(self, cursor, build_id):
|
||||
get_server_name_sql = "SELECT run_server_list FROM build_info where id='{}' and is_jacoco=1".format(build_id)
|
||||
cursor.execute(get_server_name_sql)
|
||||
server_name_info = cursor.fetchone()
|
||||
if server_name_info:
|
||||
server_name_list = eval(server_name_info[0])
|
||||
for server_name in server_name_list:
|
||||
if server_name == "PEPPA-TEACH-API" or server_name == "peppa-teach-api" or "-EXECUTOR" in server_name.upper():
|
||||
continue
|
||||
self._do_jacoco_report(server_name, build_id, cursor)
|
||||
|
||||
def _do_jacoco_report(self, project_name, build_id, cursor):
|
||||
if not self.special_env:
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, self.special_env, "master",
|
||||
self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
elif self.special_env.upper() == "NONE" or self.special_env.upper() == "QA":
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, self.special_env, "master",
|
||||
self.special_env, "1", "", "QA环境构建,不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
# 收集覆盖率报告
|
||||
import requests
|
||||
import json
|
||||
if self.team.upper() in ["CC", "LALIVE", "H2R"]:
|
||||
self.team = "CC"
|
||||
if self.team.upper() in ["SCM", "ES"]:
|
||||
self.team = "ES"
|
||||
if self.team.upper() in ["TO", "TMO"]:
|
||||
self.team = "TTS"
|
||||
current_version = self.get_branch_from_open_galaxy(self.special_env, project_name)
|
||||
if current_version == "master":
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, current_version,
|
||||
self.special_env, "1", "", "master不需要收集增量覆盖率")
|
||||
cursor.execute(insert_data)
|
||||
return
|
||||
if not current_version:
|
||||
print("-----------------: {}, 独立环境: {}, 服务名: {}".format("未在独立环境中服务,不收集覆盖率", self.special_env, project_name))
|
||||
return
|
||||
if project_name.lower() in self.galaxy_server_name_to_swagger.keys():
|
||||
project_name = self.galaxy_server_name_to_swagger[project_name.lower()]
|
||||
base_version = "master"
|
||||
url = "http://10.250.0.252:8989/cov/syncCollectionCov"
|
||||
params = {"baseVersion": base_version, "businessName": self.team, "currentVersion": current_version,
|
||||
"departmentName": "质量保障中心",
|
||||
"envName": self.special_env, "isBranch": 1, "isDiff": 2, "projectName": project_name}
|
||||
res = requests.post(url, json=params)
|
||||
if res.status_code == 200:
|
||||
if json.loads(res.text)["msg"] != "success":
|
||||
logging.error("{}收集覆盖率报告失败,err: {}".format(project_name, res.text))
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, base_version,
|
||||
self.special_env, "3", "", res.text)
|
||||
cursor.execute(insert_data)
|
||||
else:
|
||||
jacoco_report_id = json.loads(res.text)["data"]
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, int(jacoco_report_id), self.team, project_name, current_version, base_version,
|
||||
self.special_env, "0", "", "")
|
||||
cursor.execute(insert_data)
|
||||
else:
|
||||
logging.error("{}收集覆盖率报告失败,status:{},err: {}".format(project_name, str(res.status_code), res.text))
|
||||
insert_data = "INSERT INTO `sparkatp`.`build_jacoco`(`build_info_id`, `jacoco_report_id`, `team`, `server_name`, `now_version`, `base_version`, `env_name`, `status`, `report_url`, `remark`) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}');".format(
|
||||
build_id, "", self.team, project_name, current_version, base_version,
|
||||
self.special_env, "3", "", res.text)
|
||||
cursor.execute(insert_data)
|
||||
# print(res.text)
|
||||
|
||||
def get_branch_from_open_galaxy(self, special_env, server_name):
|
||||
if server_name.lower() in self.swagger_name_to_galaxy.keys():
|
||||
server_name = self.swagger_name_to_galaxy[server_name.lower()]
|
||||
try:
|
||||
headers = {
|
||||
"Api-Token": "2a1cbc25e0d183e1ec9fe5872c1433617c96da9e2905c4371d8f979479910aa1"}
|
||||
open_galaxy_url = "http://opengalaxy.bg.huohua.cn/api/v1/hcloud/tree/node/sec/application?sec_name={}".format(
|
||||
special_env)
|
||||
res = requests.get(open_galaxy_url, headers=headers)
|
||||
if res.status_code == 200:
|
||||
result = json.loads(res.text)
|
||||
for item in result["data"]["results"]:
|
||||
if item["name"].lower() == server_name.lower():
|
||||
branch = item["branch"]
|
||||
return branch
|
||||
return ""
|
||||
else:
|
||||
print("获取运维服务列表失败,status: {}, res: {}".format(res.status_code, res.text))
|
||||
return ""
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return ""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# workspace = "/root/workspaces/huaxuemin-dev"
|
||||
workspace = r"E:\huohua\auto\huaxuemin-dev"
|
||||
# case_path = "/root/workspaces/huaxuemin-dev/HuoHuaTestCase/EN/1.接口/Peppa-Eng-Live/play_back.robot"
|
||||
case_path = r"E:\huohua\auto\huaxuemin-dev\HuoHuaTestCase\EN\1.接口\Peppa-Eng-Live\play_back.robot"
|
||||
test_case = ""
|
||||
include = ""
|
||||
exclude = ""
|
||||
test = CaseRunner(workspace, case_path, test_case, include, exclude)
|
||||
# test.assign_port()
|
||||
# test.check_port()
|
||||
# test.update_port()
|
||||
# test.start_kwl()
|
||||
# test.run_cases()
|
||||
# test.check_port()
|
||||
# build_info_id = '16329'
|
||||
# build_url = 'http://10.250.200.1:8080/jenkins/view/QE_JOB/job/qe_job1/63/'
|
||||
# test.record_build_url(build_info_id, build_url)
|
||||
res = test.get_branch_from_open_galaxy("HHC-92692", "peppa-asset-server")
|
||||
print(res)
|
||||
194
base_framework/platform_tools/feishu/feishu_document_api.py
Normal file
194
base_framework/platform_tools/feishu/feishu_document_api.py
Normal file
@@ -0,0 +1,194 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import json
|
||||
import requests
|
||||
import logging
|
||||
from base_framework.public_tools.utils import Tools
|
||||
obj_tool = Tools()
|
||||
|
||||
|
||||
class FeiShuMultidimensionalTableOperations:
|
||||
def __init__(self, app_token, table_id, view_id):
|
||||
"""
|
||||
初始化飞书多维表格操作类
|
||||
Args:
|
||||
app_token: 多维表格token,登录飞书多维表格,按F12,右侧接口列表中找带token的接口,复制token
|
||||
table_id: 多维表格id,登录飞书多维表格,在浏览器地址栏中找到table=后面的值
|
||||
view_id: 多维表格视图id,登录飞书多维表格,在浏览器地址栏中找到view=后面的值
|
||||
Note:
|
||||
如果返回显示禁止访问的话,说明你的飞书表格没有给应用开通权限,需要在更多-添加应用中添加此应用,并开通权限
|
||||
这里我们使用的应用名是:QualityAssurance-Customer-CD-QA
|
||||
如果你搜多不到这个应用,说明你没有此应用的使用权限,找陈江或刚哥开通权限
|
||||
"""
|
||||
self.app_token = app_token # 表格token
|
||||
self.table_id = table_id # 表格id
|
||||
self.view_id = view_id # 表格视图id
|
||||
self.app_id = 'cli_a53b456397bdd00c' # 飞书应用的app_id,使用前,多维表格需要在更多-添加应用中添加此应用,并开通权限
|
||||
self.app_secret = 's3U4tXp9lTz7imMDQsHDNcoO4AgzXWk7' # 飞书应用的app_secret
|
||||
self.fs_login_url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
|
||||
self.http_session = requests.session()
|
||||
self.tenant_access_token = ( # 以应用身份请求api的鉴权凭证,每次获取后有效期为2小时
|
||||
self.__get_tenant_access_token_by_feishu().get("tenant_access_token"))
|
||||
self.http_session.headers.update({"Content-Type": "application/json; charset=utf-8",
|
||||
"Authorization": f"Bearer {self.tenant_access_token}",
|
||||
"User-Agent": "lark-api-explorer/v1"})
|
||||
|
||||
def __get_tenant_access_token_by_feishu(self):
|
||||
"""功能:获取飞书应用的tenant_access_token:以应用身份请求api的鉴权凭证,每次获取后有效期为2小时"""
|
||||
headers = {'Content-Type': 'application/json; charset=utf-8'}
|
||||
data = {'app_id': self.app_id, 'app_secret': self.app_secret}
|
||||
r = requests.post(self.fs_login_url, data=json.dumps(data), headers=headers, verify=False)
|
||||
r_json = r.json()
|
||||
if r_json.get('code') == 0:
|
||||
return {"expire": r_json.get('expire'), "tenant_access_token": r_json.get('tenant_access_token')}
|
||||
else:
|
||||
raise Exception(f"获取原始tenant_access_token失败{r_json}")
|
||||
|
||||
def fsmt_search_from_m_table(self, filter_dict=None, sort=None, automatic_fields=False, field_names=None):
|
||||
"""
|
||||
功能:从多维表格中查询数据,详细说明见:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/search
|
||||
Args:
|
||||
filter_dict: 过滤条件,类型:list,格式:[{"conjunction": "string", "conditions": "list"]
|
||||
conjunction: 过滤条件之间的关系,取值范围:and、or,当仅有一个条件时,也需要填写此字段
|
||||
conditions: 过滤条件,格式:[{"field_name": "string", "operator": "string", "value": "list"}]
|
||||
field_name: 筛选条件的左值,值为字段的名称,类型:string
|
||||
operator: 筛选条件的运算符,
|
||||
取值范围:is:等于
|
||||
isNot:不等于
|
||||
contains:包含
|
||||
doesNotContain:不包含
|
||||
isEmpty:为空
|
||||
isNotEmpty:不为空
|
||||
isGreater:大于
|
||||
isGreaterEqual:大于等于
|
||||
isLess:小于
|
||||
isLessEqual:小于等于
|
||||
like:LIKE 运算符。暂未支持
|
||||
in:IN 运算符。暂未支持
|
||||
value: 筛选条件的右值,值为字段的值,类型:list
|
||||
若查询条件是时间格式,且为具体时间或日期时,格式为:["ExactDate", "13位的时间戳"]
|
||||
同时也支持时间段查询,格式为:["具体的时间段描述字符"],如:
|
||||
["Today"]:今天
|
||||
["Tomorrow"]: 明天
|
||||
["Yesterday"]:昨天
|
||||
["CurrentWeek"]:本周
|
||||
["LastWeek"]:上周
|
||||
["CurrentMonth"]:本月
|
||||
["LastMonth"]:上月
|
||||
["TheLastWeek"]:过去七天内
|
||||
["TheNextWeek"]:未来七天内
|
||||
["TheLastMonth"]:过去三十天内
|
||||
["TheNextMonth"]:未来三十天内
|
||||
sort: 排序条件,类型:list,格式:[{"field_name": "string", "desc": "bool"}]
|
||||
field_name: 排序字段的名称,类型:string
|
||||
desc: 是否倒序排序,类型:bool, 默认值:False
|
||||
automatic_fields: 控制是否返回自动计算的字段, true 表示返回
|
||||
field_names: 指定本次查询返回记录中包含哪些字段
|
||||
Returns: {"code": 0, "msg": "",
|
||||
"data": {"items": "入参中自定义的返回字段",
|
||||
"has_more": "bool,是否还有更多项",
|
||||
"page_token": "boolean, 分页标记,当 has_more 为 true 时,会同时返回新的 page_token,否则不返回 page_token",
|
||||
"total": "int, 总数"}}
|
||||
"""
|
||||
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/search"
|
||||
.format(self.app_token, self.table_id))
|
||||
request_data = {"view_id": self.view_id,
|
||||
"filter": filter_dict,
|
||||
"automatic_fields": automatic_fields,
|
||||
"sort": sort,
|
||||
"field_names": field_names}
|
||||
try:
|
||||
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
|
||||
except Exception as e:
|
||||
raise Exception(f"调用飞书接口{url}失败:{e}")
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
|
||||
|
||||
def fsmt_insert_data_to_m_table(self, m_table_column_dict):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_create
|
||||
api文档地址:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_create
|
||||
"""
|
||||
url = ("https://open.feishu.cn/open-apis/bitable/v1/apps/{}/tables/{}/records/batch_create"
|
||||
.format(self.app_token, self.table_id))
|
||||
request_data = {"records": []}
|
||||
for column_dict in m_table_column_dict:
|
||||
request_data["records"].append({"fields": column_dict})
|
||||
try:
|
||||
rs = self.http_session.post(url, data=json.dumps(request_data), verify=False)
|
||||
except Exception as e:
|
||||
raise Exception(f"调用飞书接口{url}失败:{e}")
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
raise Exception(f"调用飞书接口{url}失败:\n{rs.status_code}, {rs.text}")
|
||||
|
||||
def fsmt_update_one_data_to_m_table(self, record_id, m_table_column_dict):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/:record_id/update
|
||||
api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/update
|
||||
"""
|
||||
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{self.table_id}/records/{record_id}"""
|
||||
data = {"fields": m_table_column_dict}
|
||||
logging.info(f"更新数据请求参数为:{data}")
|
||||
rs = self.http_session.put(url, data=json.dumps(data), verify=False)
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
logging.info(f"更新异常,异常返回结果为:{rs}")
|
||||
|
||||
|
||||
def batch_update_data(self, table_id, update_records_data_list):
|
||||
"""
|
||||
https://open.feishu.cn/open-apis/bitable/v1/apps/:app_token/tables/:table_id/records/batch_update
|
||||
api文档:https://open.feishu.cn/document/server-docs/docs/bitable-v1/app-table-record/batch_update
|
||||
"""
|
||||
url = f"""https://open.feishu.cn/open-apis/bitable/v1/apps/{self.app_token}/tables/{table_id}/records/batch_update"""
|
||||
data = {"records": update_records_data_list}
|
||||
logging.info(f"批量更新数据请求参数为:{data}")
|
||||
rs = self.http_session.post(url, data=json.dumps(data), verify=False)
|
||||
rj_json = rs.json()
|
||||
if rj_json.get('code') == 0:
|
||||
return rj_json
|
||||
else:
|
||||
logging.info(f"批量更新异常,异常返回结果为:{rs}")
|
||||
raise Exception(f"调用飞书接口{url}失败:{rs}")
|
||||
|
||||
def delete_data(self):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
fs = FeiShuMultidimensionalTableOperations(app_token="HssibyRf4anbpxsA3G2c1IPwnic",
|
||||
table_id="tblVbNoZE1RI3Hdo",
|
||||
view_id="vewx9lK9hI")
|
||||
filter_dict = {"conjunction": "and",
|
||||
"conditions": [{"field_name": "时间", "operator": "is", "value": ["Today"]}]}
|
||||
sort = [{"field_name": "时间", "desc": True}]
|
||||
field_names = ["时间", "入班", "换班", "补课", "月份"]
|
||||
|
||||
rsp = fs.fsmt_search_from_m_table(filter_dict=filter_dict, sort=sort, field_names=field_names)
|
||||
|
||||
if rsp.get("code") == 0:
|
||||
if int(rsp.get("data").get("total")) == 0:
|
||||
ci_data = [['2024-07-24', 7022, 2040, 105]]
|
||||
request_data = []
|
||||
for item in ci_data:
|
||||
ci_date = obj_tool.get_format_date(r_time="{} 00:00:00".format(item[0]), r_type=13)
|
||||
request_data.append({"时间": ci_date, "入班": item[1], "换班": item[2], "补课": item[3],
|
||||
"月份": "{}月".format(int(item[0].split("-")[1]))})
|
||||
for item in request_data:
|
||||
print(item)
|
||||
# request_data = [{"时间": 1721664000000, "入班": 123, "换班": 124, "补课": 125, "月份": "7月"},
|
||||
# {"时间": 1721750400000, "入班": 223, "换班": 224, "补课": 225, "月份": "7月"}]
|
||||
rsp = fs.fsmt_insert_data_to_m_table(m_table_column_dict=request_data)
|
||||
print(rsp)
|
||||
else:
|
||||
print("数据已存在,本次不写入...")
|
||||
else:
|
||||
print("查询失败:{}".format(rsp))
|
||||
204
base_framework/platform_tools/kibana/logstashlog_kibana.py
Normal file
204
base_framework/platform_tools/kibana/logstashlog_kibana.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
功能:通过kibana查询logstash日志
|
||||
进度:待完善....
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
|
||||
class LogstashLogKibana:
|
||||
def __init__(self, k_user="wuyonggang", k_pwd="Mima@123"):
|
||||
self.kibana_host = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch"
|
||||
self.kibana_user = k_user
|
||||
self.kibana_pwd = k_pwd
|
||||
self.r_header = {'kbn-version': '7.14.2',
|
||||
'Content-Type': 'application/json; charset=gbk',
|
||||
'sec-ch-ua-mobile': r'?0'}
|
||||
self.request = requests.session()
|
||||
self._login_kibana()
|
||||
|
||||
def _login_kibana(self):
|
||||
url = "https://logstashlog-kibana.qc.huohua.cn/internal/security/login"
|
||||
payload = json.dumps({"providerType": "basic",
|
||||
"providerName": "basic",
|
||||
"currentURL": "https://logstashlog-kibana.qc.huohua.cn/login?msg=LOGGED_OUT",
|
||||
"params": {"username": self.kibana_user, "password": self.kibana_pwd}
|
||||
})
|
||||
resp = self.request.post(url, headers=self.r_header, data=payload)
|
||||
print(resp)
|
||||
|
||||
def _format_message(self, message):
|
||||
"""
|
||||
格式化消息
|
||||
:param message: 消息
|
||||
:return: 格式化后的消息
|
||||
"""
|
||||
|
||||
|
||||
def _get_time_tamp(self, minute=10):
|
||||
|
||||
# 获取当前时间
|
||||
current_time = datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||
# 计算十分钟前的时间
|
||||
ten_minutes_ago = current_time - timedelta(minutes=minute)
|
||||
# 格式化时间为 Elasticsearch 时间戳格式
|
||||
gte_timestamp = ten_minutes_ago.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
|
||||
lte_timestamp = current_time.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
|
||||
return gte_timestamp, lte_timestamp
|
||||
|
||||
def lk_query_kibana_log(self, minutes=15, app_name=None, querys=None):
|
||||
|
||||
if querys is None:
|
||||
querys = ['java.lang.NullPointerException']
|
||||
if not (isinstance(querys, list) or isinstance(querys, dict)):
|
||||
raise ValueError('querys:{},必须为列表或者字典'.format(querys))
|
||||
gte_timestamp, lte_timestamp = self._get_time_tamp(minute=minutes)
|
||||
query_con = []
|
||||
if isinstance(querys, dict):
|
||||
query_cons = {"bool": {"should": [{"match_phrase": querys}], "minimum_should_match": 1}}
|
||||
else:
|
||||
for query in querys:
|
||||
if len(querys) == 1:
|
||||
query_cons = {
|
||||
"multi_match": {
|
||||
"type": "phrase",
|
||||
"query": query,
|
||||
"lenient": True
|
||||
}}
|
||||
else:
|
||||
query_con.append({
|
||||
"multi_match": {
|
||||
"type": "phrase",
|
||||
"query": query,
|
||||
"lenient": True
|
||||
}
|
||||
})
|
||||
query_cons = {
|
||||
"bool": {
|
||||
"filter": query_con}}
|
||||
|
||||
url = "https://logstashlog-kibana.qc.huohua.cn/internal/bsearch"
|
||||
payload = json.dumps({
|
||||
"batch": [
|
||||
{
|
||||
"request": {
|
||||
"params": {
|
||||
"index": "logstash-qc-logstashlog*",
|
||||
"body": {
|
||||
"size": 10000,
|
||||
"sort": [
|
||||
{
|
||||
"@timestamp": {
|
||||
"order": "desc",
|
||||
"unmapped_type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": True,
|
||||
"fields": [
|
||||
{
|
||||
"field": "*",
|
||||
"include_unmapped": "true"
|
||||
},
|
||||
{
|
||||
"field": "@timestamp",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "end_data",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "end_date",
|
||||
"format": "strict_date_optional_time"
|
||||
},
|
||||
{
|
||||
"field": "start_date",
|
||||
"format": "strict_date_optional_time"
|
||||
}
|
||||
],
|
||||
"aggs": {
|
||||
"2": {
|
||||
"date_histogram": {
|
||||
"field": "@timestamp",
|
||||
"fixed_interval": "30m",
|
||||
"time_zone": "Asia/Shanghai",
|
||||
"min_doc_count": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"script_fields": {},
|
||||
"stored_fields": [
|
||||
"*"
|
||||
],
|
||||
"runtime_mappings": {},
|
||||
"_source": False,
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [],
|
||||
"filter": [
|
||||
query_cons,
|
||||
{
|
||||
"range": {
|
||||
"@timestamp": {
|
||||
"gte": gte_timestamp,
|
||||
"lte": lte_timestamp,
|
||||
"format": "strict_date_optional_time"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"match_phrase": {
|
||||
"APP_NAME": app_name
|
||||
}
|
||||
}
|
||||
],
|
||||
"should": [],
|
||||
"must_not": []
|
||||
}
|
||||
},
|
||||
"highlight": {
|
||||
"pre_tags": [
|
||||
"@kibana-highlighted-field@"
|
||||
],
|
||||
"post_tags": [
|
||||
"@/kibana-highlighted-field@"
|
||||
],
|
||||
"fields": {
|
||||
"*": {}
|
||||
},
|
||||
"fragment_size": 2147483647
|
||||
}
|
||||
},
|
||||
"track_total_hits": True,
|
||||
"preference": 1708481407352
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
# "sessionId": "473ee7d3-be00-411e-a925-71e2f669a230",
|
||||
"isRestore": False,
|
||||
"strategy": "ese",
|
||||
"isStored": False
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
print(payload)
|
||||
response = self.request.post(url, headers=self.r_header, data=payload)
|
||||
# binary_data = response.text
|
||||
# 解码为字符串并解析为 JSON 对象
|
||||
json_string = response.text
|
||||
json_data = json.loads(json_string)
|
||||
print(json_data)
|
||||
# print(response.json())
|
||||
# path = os.path.join(self.data_path_list, file_name)
|
||||
# with open(path, 'w+', encoding='utf-8') as f:
|
||||
# f.write(response.text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
lk = LogstashLogKibana()
|
||||
lk.lk_query_kibana_log(minutes=15, app_name="peppa-sparkle-scheduler",
|
||||
querys=['java.lang.NullPointerException'])
|
||||
55
base_framework/platform_tools/wyg_bingfa.py
Normal file
55
base_framework/platform_tools/wyg_bingfa.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import concurrent.futures
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
|
||||
cost_time = []
|
||||
ALL_TIMES = 10000
|
||||
|
||||
def make_request(url, method='GET', data=None):
|
||||
try:
|
||||
start_time = int(time.time() * 1000)
|
||||
if method.upper() == 'POST':
|
||||
response = requests.post(url, json=data, headers={"content-type": "application/json;charset=UTF-8"})
|
||||
else: # 默认使用GET方法
|
||||
response = requests.get(url, params=data)
|
||||
end_time = int(time.time() * 1000)
|
||||
elapsed_time = end_time - start_time
|
||||
cost_time.append(elapsed_time)
|
||||
return response.text, elapsed_time
|
||||
except requests.RequestException as e:
|
||||
return f"Request failed: {e}", None
|
||||
|
||||
def main():
|
||||
r_url = {'url': 'https://swagger.qa.huohua.cn/peppa-teach-timetable-server/timetableStudentServiceApi/queryListByIds',
|
||||
'method': 'POST', 'data': [225838679]}
|
||||
requests_list = [r_url] * ALL_TIMES
|
||||
|
||||
m_start_time = int(time.time() * 1000)
|
||||
success_times = 0
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
|
||||
future_to_req = {executor.submit(make_request, req['url'], req['method'], req['data']): req for req in requests_list}
|
||||
for future in concurrent.futures.as_completed(future_to_req):
|
||||
req = future_to_req[future]
|
||||
try:
|
||||
data, elapsed_time = future.result()
|
||||
if data and json.loads(data).get('code') == 200:
|
||||
success_times += 1
|
||||
# print(f"URL: {req['url']}\nMethod: {req['method']}\nData: {req['data']}\nResponse:\n{data}\nElapsed time: {elapsed_time:.2f} seconds\n")
|
||||
print("-----:本次耗时{}ms".format(elapsed_time))
|
||||
except Exception as e:
|
||||
print(f"Error fetching {req['url']}: {e}")
|
||||
m_end_time = int(time.time() * 1000)
|
||||
|
||||
all_times = (m_end_time - m_start_time)/1000
|
||||
tps = ALL_TIMES / all_times
|
||||
print("共计访问接口:{}次,成功访问接口:{}次,持续时长:{}秒,tps: {}"
|
||||
.format(len(cost_time), success_times, all_times, tps))
|
||||
print("成功访问接口:{}次".format(success_times))
|
||||
print("单次最大耗时:{}ms".format(max(cost_time)))
|
||||
print("单次最小耗时:{}ms".format(min(cost_time)))
|
||||
print("单次平均耗时:{}ms".format(sum(cost_time) / len(cost_time)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
403
base_framework/platform_tools/zendao_tools/handle_bug.py
Normal file
403
base_framework/platform_tools/zendao_tools/handle_bug.py
Normal file
@@ -0,0 +1,403 @@
|
||||
import requests
|
||||
import hashlib
|
||||
import json
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
import time
|
||||
|
||||
|
||||
def send_to_feishu(webhook_url, content, keyword="bug"):
|
||||
"""
|
||||
发送消息到飞书机器人
|
||||
|
||||
Args:
|
||||
webhook_url: 飞书机器人的webhook地址
|
||||
content: 要发送的消息内容
|
||||
keyword: 关键词,需要包含在消息中
|
||||
"""
|
||||
try:
|
||||
# 构建消息体
|
||||
message = {
|
||||
"msg_type": "text",
|
||||
"content": {
|
||||
"text": f"{keyword}\n{content}"
|
||||
}
|
||||
}
|
||||
|
||||
# 发送请求
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
response = requests.post(webhook_url,
|
||||
data=json.dumps(message),
|
||||
headers=headers,
|
||||
timeout=10)
|
||||
|
||||
if response.status_code == 200:
|
||||
print(f"✅ 消息发送成功到飞书")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败: {response.status_code}")
|
||||
print(f"响应: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 发送飞书消息异常: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def get_all_bugs_and_send():
|
||||
"""获取所有产品的Bug并发送到飞书"""
|
||||
base_url = "http://39.170.26.156:8888"
|
||||
username = "qiaoxinjiu"
|
||||
password = "Qiao123456"
|
||||
|
||||
# 飞书配置
|
||||
feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302"
|
||||
keyword = "bug"
|
||||
|
||||
print("获取禅道Bug列表")
|
||||
print("=" * 50)
|
||||
|
||||
# 登录
|
||||
session = requests.Session()
|
||||
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
login_resp = session.post(f"{base_url}/api.php/v1/tokens",
|
||||
json={"account": username, "password": password_md5},
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
if login_resp.status_code not in [200, 201]:
|
||||
error_msg = f"❌ 禅道登录失败: {login_resp.status_code}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
token = login_resp.json().get('token')
|
||||
session.headers.update({'Token': token})
|
||||
|
||||
print("✅ 登录成功")
|
||||
|
||||
# 获取所有产品
|
||||
products_resp = session.get(f"{base_url}/api.php/v1/products")
|
||||
if products_resp.status_code != 200:
|
||||
error_msg = f"❌ 获取产品列表失败: {products_resp.status_code}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
products = products_resp.json().get('products', [])
|
||||
print(f"📦 共 {len(products)} 个产品")
|
||||
|
||||
all_bugs = []
|
||||
|
||||
# 遍历每个产品获取Bug(只获取产品ID为2的)
|
||||
for product in products:
|
||||
product_id = product.get('id')
|
||||
product_name = product.get('name')
|
||||
|
||||
# 只获取产品ID为2的
|
||||
if product_id != 2:
|
||||
continue
|
||||
|
||||
print(f"\n获取产品 '{product_name}' 的Bug...")
|
||||
|
||||
# 获取HTML页面
|
||||
bugs_url = f"{base_url}/bug-browse-{product_id}.html"
|
||||
bugs_resp = session.get(bugs_url, params={'product': product_id, 'limit': 100})
|
||||
|
||||
if bugs_resp.status_code == 200:
|
||||
try:
|
||||
html = bugs_resp.text
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
|
||||
# 找 bug 列表的 table
|
||||
table = (
|
||||
soup.find("table", id="bugList")
|
||||
or soup.find("table", class_="table")
|
||||
or soup.find("table")
|
||||
)
|
||||
if not table:
|
||||
print(" ❌ 未找到 bug 列表 table")
|
||||
continue
|
||||
|
||||
# 解析表头
|
||||
header_tr = table.find("tr")
|
||||
if not header_tr:
|
||||
print(" ❌ 未找到表头行")
|
||||
continue
|
||||
|
||||
header_map = {}
|
||||
for idx, th in enumerate(header_tr.find_all(["th", "td"])):
|
||||
text = th.get_text(strip=True)
|
||||
if not text:
|
||||
continue
|
||||
if "ID" == text or text == "编号":
|
||||
header_map["id"] = idx
|
||||
elif "标题" in text:
|
||||
header_map["title"] = idx
|
||||
elif "状态" in text:
|
||||
header_map["status"] = idx
|
||||
elif "严重" in text:
|
||||
header_map["severity"] = idx
|
||||
elif "优先" in text or "优先级" in text:
|
||||
header_map["pri"] = idx
|
||||
elif "指派" in text:
|
||||
header_map["assignedTo"] = idx
|
||||
elif "创建" in text or "打开" in text:
|
||||
header_map["openedDate"] = idx
|
||||
|
||||
bugs = []
|
||||
|
||||
# 遍历表体行
|
||||
for tr in table.find_all("tr")[1:]:
|
||||
tds = tr.find_all("td")
|
||||
if not tds:
|
||||
continue
|
||||
|
||||
# id
|
||||
bug_id = tr.get("data-id")
|
||||
if not bug_id and "id" in header_map and header_map["id"] < len(tds):
|
||||
bug_id = tds[header_map["id"]].get_text(strip=True)
|
||||
if not bug_id:
|
||||
continue
|
||||
|
||||
# 标题
|
||||
title = ""
|
||||
if "title" in header_map and header_map["title"] < len(tds):
|
||||
cell = tds[header_map["title"]]
|
||||
link = cell.find("a")
|
||||
title = (link or cell).get_text(strip=True)
|
||||
|
||||
# 状态
|
||||
status = ""
|
||||
if "status" in header_map and header_map["status"] < len(tds):
|
||||
status = tds[header_map["status"]].get_text(strip=True)
|
||||
|
||||
# 严重程度
|
||||
severity = ""
|
||||
if "severity" in header_map and header_map["severity"] < len(tds):
|
||||
sev_cell = tds[header_map["severity"]]
|
||||
severity = sev_cell.get_text(strip=True)
|
||||
if not severity:
|
||||
sev_span = sev_cell.find("span")
|
||||
if sev_span and sev_span.get("title"):
|
||||
severity = sev_span.get("title").strip()
|
||||
|
||||
# 优先级
|
||||
pri = ""
|
||||
if "pri" in header_map and header_map["pri"] < len(tds):
|
||||
pri = tds[header_map["pri"]].get_text(strip=True)
|
||||
|
||||
# 指派给
|
||||
assigned_to = ""
|
||||
if "assignedTo" in header_map and header_map["assignedTo"] < len(tds):
|
||||
assigned_to = tds[header_map["assignedTo"]].get_text(strip=True)
|
||||
|
||||
# 创建/打开日期
|
||||
opened_date = ""
|
||||
if "openedDate" in header_map and header_map["openedDate"] < len(tds):
|
||||
opened_date = tds[header_map["openedDate"]].get_text(strip=True)
|
||||
|
||||
bug = {
|
||||
"id": bug_id,
|
||||
"title": title,
|
||||
"statusName": status,
|
||||
"severity": severity,
|
||||
"pri": pri,
|
||||
"assignedToName": assigned_to,
|
||||
"openedDate": opened_date,
|
||||
"product_name": product_name,
|
||||
}
|
||||
bugs.append(bug)
|
||||
|
||||
print(f" ✅ 解析出 {len(bugs)} 个Bug")
|
||||
all_bugs.extend(bugs)
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ 解析失败: {e}")
|
||||
else:
|
||||
print(f" ❌ 获取失败: {bugs_resp.status_code}")
|
||||
|
||||
# 处理并发送统计信息
|
||||
if all_bugs:
|
||||
# 计算统计数据
|
||||
today_str = datetime.today().strftime("%Y-%m-%d")
|
||||
today_md_str = datetime.today().strftime("%m-%d")
|
||||
|
||||
total_bugs = len(all_bugs)
|
||||
|
||||
# 今日新增Bug
|
||||
today_bugs = [
|
||||
b for b in all_bugs
|
||||
if (
|
||||
today_str in str(b.get("openedDate", "")).strip()
|
||||
or today_md_str in str(b.get("openedDate", "")).strip()
|
||||
)
|
||||
]
|
||||
|
||||
# 未关闭Bug(状态不包含"已关闭"、"已解决"等)
|
||||
open_bugs = [
|
||||
b for b in all_bugs
|
||||
if not any(kw in str(b.get("statusName", "")).lower() for kw in ["closed", "resolved", "done", "cancel"])
|
||||
and not any(kw in str(b.get("statusName", "")) for kw in ["已关闭", "已解决", "已完成", "已取消"])
|
||||
]
|
||||
|
||||
# 按指派人员统计
|
||||
assigned_stats = {}
|
||||
for bug in all_bugs:
|
||||
assigned = bug.get("assignedToName", "未指派")
|
||||
assigned_stats[assigned] = assigned_stats.get(assigned, 0) + 1
|
||||
|
||||
# 按状态统计
|
||||
status_stats = {}
|
||||
for bug in all_bugs:
|
||||
status = bug.get("statusName", "未知")
|
||||
status_stats[status] = status_stats.get(status, 0) + 1
|
||||
|
||||
# 构建飞书消息
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
message = f"🐛 禅道Bug统计报告\n"
|
||||
message += f"⏰ 时间: {current_time}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"📈 Bug统计概览:\n"
|
||||
message += f" • 总Bug数: {total_bugs}\n"
|
||||
message += f" • 今日新增: {len(today_bugs)}\n"
|
||||
message += f" • 未关闭Bug: {len(open_bugs)}\n"
|
||||
message += f"\n📋 状态分布:\n"
|
||||
|
||||
# 添加状态统计
|
||||
for status, count in sorted(status_stats.items()):
|
||||
message += f" • {status}: {count}个\n"
|
||||
|
||||
message += f"\n👥 指派人员统计:\n"
|
||||
# 添加指派统计(前5名)
|
||||
sorted_assigned = sorted(assigned_stats.items(), key=lambda x: x[1], reverse=True)[:5]
|
||||
for person, count in sorted_assigned:
|
||||
message += f" • {person}: {count}个\n"
|
||||
|
||||
# 今日新增Bug详情
|
||||
if today_bugs:
|
||||
message += f"\n🆕 今日新增Bug详情 ({len(today_bugs)}个):\n"
|
||||
today_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True)
|
||||
for bug in today_bugs[:10]: # 只显示前10个
|
||||
bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html"
|
||||
message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n"
|
||||
message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n"
|
||||
|
||||
# 未关闭Bug详情(前5个)
|
||||
if open_bugs:
|
||||
open_bugs.sort(key=lambda x: int(x.get('id', 0)), reverse=True)
|
||||
message += f"\n⚠️ 最新未关闭Bug (前5个):\n"
|
||||
for bug in open_bugs[:5]:
|
||||
bug_url = f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html"
|
||||
message += f" [#{bug.get('id')}] {bug.get('title', '')[:30]}...\n"
|
||||
message += f" 状态: {bug.get('statusName', '未知')} | 严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')}\n"
|
||||
|
||||
message += f"\n🔗 禅道地址: {base_url}"
|
||||
|
||||
# 打印到控制台
|
||||
print(f"\n{'=' * 80}")
|
||||
print(message)
|
||||
print(f"{'=' * 80}")
|
||||
|
||||
# 发送到飞书
|
||||
print("\n发送消息到飞书...")
|
||||
success = send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
if success:
|
||||
print(f"✅ Bug统计报告已发送到飞书")
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败")
|
||||
else:
|
||||
message = f"📭 禅道Bug统计报告\n"
|
||||
message += f"⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"✅ 当前没有Bug数据\n"
|
||||
message += f"🔗 禅道地址: {base_url}"
|
||||
|
||||
print(message)
|
||||
send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
|
||||
def send_simple_report():
|
||||
"""发送简化的Bug统计报告到飞书"""
|
||||
base_url = "http://39.170.26.156:8888"
|
||||
username = "qiaoxinjiu"
|
||||
password = "Qiao123456"
|
||||
|
||||
# 飞书配置
|
||||
feishu_webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c7288ada-1c0c-472a-b652-a475a9586302"
|
||||
keyword = "bug"
|
||||
|
||||
print("获取禅道Bug统计")
|
||||
print("=" * 50)
|
||||
|
||||
try:
|
||||
# 登录
|
||||
session = requests.Session()
|
||||
password_md5 = hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
login_resp = session.post(f"{base_url}/api.php/v1/tokens",
|
||||
json={"account": username, "password": password_md5},
|
||||
headers={'Content-Type': 'application/json'})
|
||||
|
||||
if login_resp.status_code not in [200, 201]:
|
||||
error_msg = f"❌ 禅道登录失败"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
token = login_resp.json().get('token')
|
||||
session.headers.update({'Token': token})
|
||||
print("✅ 登录成功")
|
||||
|
||||
# 获取产品ID=2的Bug页面
|
||||
bug_url = f"{base_url}/bug-browse-2.html"
|
||||
response = session.get(bug_url)
|
||||
|
||||
if response.status_code != 200:
|
||||
error_msg = f"❌ 无法获取Bug页面"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
return
|
||||
|
||||
# 简单统计Bug数量
|
||||
import re
|
||||
bug_matches = re.findall(r'bug-view-(\d+)\.html', response.text)
|
||||
total_bugs = len(set(bug_matches)) # 去重
|
||||
|
||||
# 构建简单消息
|
||||
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
message = f"🐛 禅道Bug速报\n"
|
||||
message += f"⏰ 时间: {current_time}\n"
|
||||
message += f"📊 产品: 智慧运营平台V1.0\n"
|
||||
message += f"================================\n"
|
||||
message += f"📈 当前总Bug数: {total_bugs}\n"
|
||||
message += f"🔗 查看详情: {bug_url}\n"
|
||||
message += f"\n💡 提示: 详细统计请查看禅道系统"
|
||||
|
||||
print(f"发现 {total_bugs} 个Bug")
|
||||
|
||||
# 发送到飞书
|
||||
success = send_to_feishu(feishu_webhook, message, keyword)
|
||||
|
||||
if success:
|
||||
print(f"✅ 简版Bug报告已发送到飞书")
|
||||
else:
|
||||
print(f"❌ 飞书消息发送失败")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"❌ 获取Bug统计异常: {str(e)}"
|
||||
print(error_msg)
|
||||
send_to_feishu(feishu_webhook, error_msg, keyword)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 运行完整版本(带详细统计)
|
||||
get_all_bugs_and_send()
|
||||
|
||||
# 或者运行简化版本(只发速报)
|
||||
# send_simple_report()
|
||||
237
base_framework/platform_tools/zendao_tools/zendao_api.py
Normal file
237
base_framework/platform_tools/zendao_tools/zendao_api.py
Normal file
@@ -0,0 +1,237 @@
|
||||
from __future__ import annotations
|
||||
|
||||
"""
|
||||
ZenDao bug list crawler.
|
||||
|
||||
Design overview:
|
||||
1. Read credentials + crawl settings from zendao_config.ini.
|
||||
2. Use ZenDao REST API (`api.php/v1/accessTokens`) to obtain a short-lived token.
|
||||
3. Pull paginated bug records under the configured product via `/api.php/v1/products/{product_id}/bugs`.
|
||||
4. Export normalized bug data to CSV (default) or JSON, usable by other automation.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, List, Sequence
|
||||
|
||||
import requests
|
||||
from requests import Response, Session
|
||||
|
||||
|
||||
BUG_EXPORT_FIELDS: Sequence[str] = (
|
||||
"id",
|
||||
"title",
|
||||
"status",
|
||||
"severity",
|
||||
"pri",
|
||||
"openedBy",
|
||||
"openedDate",
|
||||
"assignedTo",
|
||||
"resolvedBy",
|
||||
"resolution",
|
||||
"lastEditedDate",
|
||||
)
|
||||
DEFAULT_CONFIG_PATH = Path(__file__).with_name("zendao_config.ini")
|
||||
|
||||
|
||||
class ZenDaoAPIError(RuntimeError):
|
||||
"""Raised when ZenDao returns an unexpected payload."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class ZenDaoSettings:
|
||||
base_url: str
|
||||
username: str
|
||||
password: str
|
||||
product_id: int
|
||||
per_page: int = 50
|
||||
max_pages: int = 10
|
||||
verify_ssl: bool = False
|
||||
timeout: int = 10
|
||||
auth_mode: str = "password" # password | token
|
||||
api_token: str | None = None
|
||||
auth_endpoint: str = "api.php/v1/tokens"
|
||||
token_header: str = "Token"
|
||||
password_hash: str = "md5" # md5 | plain
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path: Path, section: str = "zendao") -> "ZenDaoSettings":
|
||||
parser = configparser.ConfigParser()
|
||||
if not path.exists():
|
||||
raise FileNotFoundError(f"Config file not found: {path}")
|
||||
|
||||
parser.read(path, encoding="utf-8")
|
||||
if section not in parser:
|
||||
raise KeyError(f"Section [{section}] not found in {path}")
|
||||
|
||||
cfg = parser[section]
|
||||
return cls(
|
||||
base_url=cfg.get("base_url", "").rstrip("/"),
|
||||
username=cfg.get("username", ""),
|
||||
password=cfg.get("password", ""),
|
||||
product_id=cfg.getint("product_id", fallback=0),
|
||||
per_page=cfg.getint("per_page", fallback=50),
|
||||
max_pages=cfg.getint("max_pages", fallback=10),
|
||||
verify_ssl=cfg.getboolean("verify_ssl", fallback=False),
|
||||
timeout=cfg.getint("timeout", fallback=10),
|
||||
auth_mode=cfg.get("auth_mode", "password").lower(),
|
||||
api_token=cfg.get("api_token"),
|
||||
auth_endpoint=cfg.get("auth_endpoint", "api.php/v1/tokens"),
|
||||
token_header=cfg.get("token_header", "Token"),
|
||||
password_hash=cfg.get("password_hash", "md5").lower(),
|
||||
)
|
||||
|
||||
def validate(self) -> None:
|
||||
missing = [
|
||||
name
|
||||
for name, value in (
|
||||
("base_url", self.base_url),
|
||||
("username", self.username),
|
||||
("password", self.password),
|
||||
)
|
||||
if not value
|
||||
]
|
||||
if missing:
|
||||
raise ValueError(f"Missing config values: {', '.join(missing)}")
|
||||
if self.product_id <= 0:
|
||||
raise ValueError("product_id must be > 0")
|
||||
if self.auth_mode not in {"password", "token"}:
|
||||
raise ValueError("auth_mode must be 'password' or 'token'")
|
||||
if self.password_hash not in {"md5", "plain"}:
|
||||
raise ValueError("password_hash must be 'md5' or 'plain'")
|
||||
if self.auth_mode == "token" and not self.api_token:
|
||||
raise ValueError("api_token required when auth_mode=token")
|
||||
|
||||
|
||||
class ZenDaoClient:
|
||||
def __init__(self, settings: ZenDaoSettings, session: Session | None = None) -> None:
|
||||
self.settings = settings
|
||||
self.session: Session = session or requests.Session()
|
||||
self._token: str | None = None
|
||||
|
||||
def authenticate(self) -> str:
|
||||
if self.settings.auth_mode == "token":
|
||||
if not self.settings.api_token:
|
||||
raise ValueError("api_token missing in config")
|
||||
self.session.headers.update({self.settings.token_header: self.settings.api_token})
|
||||
self._token = self.settings.api_token
|
||||
return self._token
|
||||
|
||||
payload = {
|
||||
"account": self.settings.username,
|
||||
"password": self._format_password(self.settings.password),
|
||||
}
|
||||
response = self._request("post", self.settings.auth_endpoint, json=payload)
|
||||
data = response.json()
|
||||
token = data.get("token") or data.get("accessToken")
|
||||
if not token:
|
||||
raise ZenDaoAPIError(f"Unexpected token response: {data}")
|
||||
|
||||
self._token = token
|
||||
self.session.headers.update({self.settings.token_header: token})
|
||||
return token
|
||||
|
||||
def _format_password(self, password: str) -> str:
|
||||
if self.settings.password_hash == "md5":
|
||||
return hashlib.md5(password.encode()).hexdigest()
|
||||
return password
|
||||
|
||||
def fetch_bugs_page(self, page: int = 1) -> List[Dict]:
|
||||
params = {"page": page, "limit": self.settings.per_page}
|
||||
endpoint = f"api.php/v1/products/{self.settings.product_id}/bugs"
|
||||
response = self._request("get", endpoint, params=params)
|
||||
payload = response.json()
|
||||
bugs = payload.get("bugs") or payload.get("data")
|
||||
if bugs is None:
|
||||
raise ZenDaoAPIError(f"Unexpected bug payload: {payload}")
|
||||
return list(bugs)
|
||||
|
||||
def fetch_all_bugs(self) -> List[Dict]:
|
||||
all_bugs: List[Dict] = []
|
||||
for page in range(1, self.settings.max_pages + 1):
|
||||
page_bugs = self.fetch_bugs_page(page)
|
||||
if not page_bugs:
|
||||
break
|
||||
all_bugs.extend(page_bugs)
|
||||
if len(page_bugs) < self.settings.per_page:
|
||||
break
|
||||
return all_bugs
|
||||
|
||||
def _request(self, method: str, path: str, **kwargs) -> Response:
|
||||
url = f"{self.settings.base_url}/{path.lstrip('/')}"
|
||||
kwargs.setdefault("timeout", self.settings.timeout)
|
||||
kwargs.setdefault("verify", self.settings.verify_ssl)
|
||||
response = self.session.request(method=method, url=url, **kwargs)
|
||||
response.raise_for_status()
|
||||
return response
|
||||
|
||||
|
||||
def normalize_bug(bug: Dict, fields: Sequence[str] = BUG_EXPORT_FIELDS) -> Dict[str, str]:
|
||||
normalized = {}
|
||||
for field in fields:
|
||||
value = bug.get(field, "")
|
||||
if isinstance(value, dict):
|
||||
value = value.get("realname") or value.get("account") or value
|
||||
normalized[field] = value if isinstance(value, str) else str(value)
|
||||
return normalized
|
||||
|
||||
|
||||
def write_json(path: Path, bugs: Iterable[Dict]) -> None:
|
||||
path.write_text(json.dumps(list(bugs), ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def write_csv(path: Path, bugs: Iterable[Dict], fields: Sequence[str]) -> None:
|
||||
bugs = list(bugs)
|
||||
if not bugs:
|
||||
path.write_text("", encoding="utf-8")
|
||||
return
|
||||
|
||||
with path.open("w", encoding="utf-8", newline="") as csv_file:
|
||||
writer = csv.DictWriter(csv_file, fieldnames=fields)
|
||||
writer.writeheader()
|
||||
for bug in bugs:
|
||||
writer.writerow(normalize_bug(bug, fields))
|
||||
|
||||
|
||||
def run_crawler(config: Path, output: Path) -> Path:
|
||||
settings = ZenDaoSettings.from_file(config)
|
||||
settings.validate()
|
||||
|
||||
client = ZenDaoClient(settings)
|
||||
client.authenticate()
|
||||
bugs = client.fetch_all_bugs()
|
||||
|
||||
if output.suffix.lower() == ".json":
|
||||
write_json(output, bugs)
|
||||
else:
|
||||
write_csv(output, bugs, BUG_EXPORT_FIELDS)
|
||||
return output
|
||||
|
||||
|
||||
def parse_args(argv: Sequence[str]) -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Fetch bug list from ZenDao.")
|
||||
parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG_PATH, help="Path to zendao_config.ini")
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=Path,
|
||||
default=Path("bugs.csv"),
|
||||
help="Output file path (.csv or .json)",
|
||||
)
|
||||
return parser.parse_args(argv)
|
||||
|
||||
|
||||
def main(argv: Sequence[str] | None = None) -> None:
|
||||
args = parse_args(argv or sys.argv[1:])
|
||||
output = run_crawler(args.config, args.output)
|
||||
print(f"Bug list saved to: {output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
14
base_framework/platform_tools/zendao_tools/zendao_config.ini
Normal file
14
base_framework/platform_tools/zendao_tools/zendao_config.ini
Normal file
@@ -0,0 +1,14 @@
|
||||
[zendao]
|
||||
base_url = http://39.170.26.156:8888
|
||||
username = qiaoxinjiu
|
||||
password = Qiao123456
|
||||
product_id = 1
|
||||
per_page = 50
|
||||
max_pages = 10
|
||||
verify_ssl = false
|
||||
timeout = 10
|
||||
auth_mode = password
|
||||
api_token =
|
||||
auth_endpoint = api.php/v1/tokens
|
||||
token_header = Token
|
||||
password_hash = md5
|
||||
41
base_framework/public_tools/__init__.py
Normal file
41
base_framework/public_tools/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/9/21 18:39
|
||||
"""
|
||||
import json
|
||||
|
||||
|
||||
def check_resp(is_check, resp):
|
||||
if is_check:
|
||||
assert resp["code"] == 200, "{}, exp code: {}".format(resp, 200)
|
||||
assert resp["success"] is True, "{}, exp code: {}".format(resp, True)
|
||||
assert resp["message"] == "", "{}, exp code: {}".format(resp, "")
|
||||
assert resp["data"] != "", "{}, exp code: {}".format(resp, "")
|
||||
|
||||
|
||||
def custom_check_resp(is_check, assert_list):
|
||||
"""传参数例子:asert_list = [(f"{resp}['code']", 0), (f"{resp}['message']", ""), (f"{resp}['success']", "True")]"""
|
||||
if is_check:
|
||||
for al in assert_list:
|
||||
assert str(eval(al[0])) == str(al[1]), "{}, exp code: {}".format(eval(al[0]), al[1])
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
def convert_json(parames):
|
||||
temp_json = json.dumps(parames)
|
||||
temp_json1 = temp_json.replace("\"NULL\"", "null")
|
||||
kwargs = json.loads(temp_json1)
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
def get_user(kwargs):
|
||||
if kwargs.get("user", None):
|
||||
user = kwargs.pop("user")
|
||||
else:
|
||||
user = None
|
||||
|
||||
return user, kwargs
|
||||
157
base_framework/public_tools/apollo.py
Normal file
157
base_framework/public_tools/apollo.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*-coding:utf-8-*-
|
||||
import requests, time
|
||||
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.get_token import LazyProperty
|
||||
from base_framework.public_tools.read_config import InitConfig
|
||||
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
class Apollo:
|
||||
def __init__(self):
|
||||
self.server = None
|
||||
self.session = None
|
||||
self.config = InitConfig()
|
||||
self.show_username = self.config.show_username
|
||||
self.password = self.config.password
|
||||
self.apollo_url = self.config.apollo_url
|
||||
self.apollo_host = self.config.apollo_host
|
||||
self.current_evn = self.config.current_evn
|
||||
|
||||
# @LazyProperty
|
||||
def apollo_login(self):
|
||||
post_data = dict()
|
||||
post_data['login-submit'] = '登录'
|
||||
post_data['username'] = self.show_username
|
||||
post_data['password'] = self.password
|
||||
req_session = requests.Session()
|
||||
resp = req_session.post(url=self.apollo_url, data=post_data)
|
||||
return req_session
|
||||
|
||||
def __get_project__(self):
|
||||
pass
|
||||
|
||||
def apollo_get_config_value(self, app_id, name_space, key, cluster='default'):
|
||||
"""
|
||||
功能:读取apollo上的配置
|
||||
Args:
|
||||
app_id: url地址里的appid
|
||||
name_space: 页面分组的命名空间,如:application,dict.config,teach.common等
|
||||
key: 具体的配置key
|
||||
cluster: 集群名,如:default,hhi等
|
||||
Returns: 具体的配置值
|
||||
"""
|
||||
return self.get_apollo_config_by_project_key(server=app_id, project=name_space, key=key, cluster=cluster)
|
||||
|
||||
def get_apollo_config_by_project_key(self, project, key, cluster='default', server=""):
|
||||
"""
|
||||
返回{id: 8812, namespaceId: 378, key: "", value: "", comment: "#线上不要此配置", lineNum: 1,…}
|
||||
"""
|
||||
self.session = self.apollo_login()
|
||||
if server:
|
||||
self.server = server
|
||||
req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces" % (
|
||||
self.apollo_host, self.server, self.current_evn, cluster)
|
||||
obj_log.info("get apollo url is : %s" % req_url)
|
||||
resp = self.session.get(url=req_url).json()
|
||||
# obj_log.info("apollo value is : %s" % resp)
|
||||
for item in resp:
|
||||
base_info = item.get('baseInfo')
|
||||
items = item.get('items')
|
||||
if not base_info['namespaceName'].lower() == project.lower():
|
||||
continue
|
||||
for key_info in items:
|
||||
item_detail = key_info["item"]
|
||||
if item_detail["key"] == key:
|
||||
return key_info["item"]
|
||||
else:
|
||||
obj_log.info("无法找到key:%s" % key)
|
||||
return None
|
||||
|
||||
def set_apollo_config_by_project_key(self, project, key, value, cluster='default', server="", set_type='append',
|
||||
evn_name=""):
|
||||
"""
|
||||
功能:设置apollo配置项
|
||||
Args:
|
||||
server: 项目名,也称appid
|
||||
project: 属性名
|
||||
key: 键值key
|
||||
value: 具体要设置的值
|
||||
cluster: 集群,如:default,hhi等
|
||||
set_type: 设置类型:append-追加,new-重置
|
||||
Returns:
|
||||
"""
|
||||
if evn_name:
|
||||
self.current_evn = evn_name
|
||||
self.session = self.apollo_login()
|
||||
if server:
|
||||
self.server = server
|
||||
req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % (
|
||||
self.apollo_host, self.server, self.current_evn, cluster, project)
|
||||
obj_log.info("set apollo url is : %s" % req_url)
|
||||
items = self.get_apollo_config_by_project_key(project, key, cluster)
|
||||
if items is None:
|
||||
items = dict()
|
||||
items["value"] = value
|
||||
items["key"] = key
|
||||
items["tableViewOperType"] = 'create'
|
||||
items["addItemBtnDisabled"] = True
|
||||
|
||||
else:
|
||||
if set_type == 'new':
|
||||
items["value"] = value
|
||||
elif set_type == 'append':
|
||||
if str(value) in str(items["value"]):
|
||||
obj_log.info("apollo配置已存在,本次跳过....")
|
||||
else:
|
||||
items["value"] = items["value"] + ",{}".format(value)
|
||||
items["tableViewOperType"] = 'update'
|
||||
obj_log.info("set apollo parameters is : %s" % items)
|
||||
update_resp = self.session.put(url=req_url, json=items)
|
||||
obj_log.info("set apollo resp is : %s" % update_resp)
|
||||
|
||||
assert update_resp.status_code == 200
|
||||
obj_log.info("为key:%s设置value:%s成功" % (key, value))
|
||||
release_body = dict()
|
||||
release_time_stamp = time.localtime()
|
||||
release_time = '%s-release' % time.strftime("%Y%m%d%H%M%S", release_time_stamp)
|
||||
release_body["isEmergencyPublish"] = False
|
||||
release_body["releaseComment"] = ""
|
||||
release_body["releaseTitle"] = release_time
|
||||
release_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % (
|
||||
self.apollo_host, self.server, self.current_evn, cluster, project)
|
||||
resp = self.session.post(url=release_url, json=release_body)
|
||||
assert resp.status_code == 200
|
||||
time.sleep(5)
|
||||
|
||||
def add_new_apollo_config_by_project_key(self, project, key, value, cluster='default', server=""):
|
||||
self.session = self.apollo_login()
|
||||
if server:
|
||||
self.server = server
|
||||
items = dict()
|
||||
items["value"] = value
|
||||
items["key"] = key
|
||||
items["tableViewOperType"] = 'create'
|
||||
items["addItemBtnDisabled"] = True
|
||||
req_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/item" % (
|
||||
self.apollo_host, self.server, self.current_evn, cluster, project)
|
||||
update_resp = self.session.put(url=req_url, json=items)
|
||||
assert update_resp.status_code == 200
|
||||
release_body = dict()
|
||||
release_time_stamp = time.localtime()
|
||||
release_time = '%s-release' % time.strftime("%Y%m%d%H%M%S", release_time_stamp)
|
||||
release_body["isEmergencyPublish"] = False
|
||||
release_body["releaseComment"] = ""
|
||||
release_body["releaseTitle"] = release_time
|
||||
release_url = "%s/apps/%s/envs/%s/clusters/%s/namespaces/%s/releases" % (
|
||||
self.apollo_host, self.server, self.current_evn, cluster, project)
|
||||
resp = self.session.post(url=release_url, json=release_body)
|
||||
assert resp.status_code == 200
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ap = Apollo()
|
||||
print(ap.apollo_get_config_value(app_id='peppa-teach-api', name_space='application',
|
||||
key='new_ticket_leave_logic_switch'))
|
||||
8
base_framework/public_tools/custom_error.py
Normal file
8
base_framework/public_tools/custom_error.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 存放自定义异常类
|
||||
|
||||
class BusinessError(Exception):
|
||||
"""功能:用于AITA项目识别是业务异常,需返给前端做展示"""
|
||||
def __init__(self, message="这里是自定义的业务异常"):
|
||||
self.message = message
|
||||
super().__init__(self.message)
|
||||
31
base_framework/public_tools/db_config.py
Normal file
31
base_framework/public_tools/db_config.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/11/10 10:26
|
||||
"""
|
||||
import pymysql
|
||||
|
||||
DB_TEST_HOST = "mysql.qa.huohua.cn"
|
||||
DB_TEST_PORT = 3306
|
||||
DB_TEST_DBNAME = "crmthirdparty"
|
||||
DB_TEST_USER = "qa-dev"
|
||||
DB_TEST_PASSWORD = "jaeg3SCQt0"
|
||||
# 数据库连接编码
|
||||
DB_CHARSET = "utf8"
|
||||
# mincached : 启动时开启的闲置连接数量(缺省值 0 开始时不创建连接)
|
||||
DB_MIN_CACHED = 10
|
||||
# maxcached : 连接池中允许的闲置的最多连接数量(缺省值 0 代表不闲置连接池大小)
|
||||
DB_MAX_CACHED = 20
|
||||
# maxshared : 共享连接数允许的最大数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用
|
||||
DB_MAX_SHARED = 20
|
||||
# maxconnecyions : 创建连接池的最大数量(缺省值 0 代表不限制)
|
||||
DB_MAX_CONNECYIONS = 100
|
||||
# blocking : 设置在连接池达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误<toMany......> 其他代表阻塞直到连接数减少,连接被分配)
|
||||
DB_BLOCKING = True
|
||||
# maxusage : 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用).当达到最大数时,连接会自动重新连接(关闭和重新打开)
|
||||
DB_MAX_USAGE = 0
|
||||
# setsession : 一个可选的SQL命令列表用于准备每个会话,如["set datestyle to german", ...]
|
||||
DB_SET_SESSION = None
|
||||
# creator : 使用连接数据库的模块
|
||||
DB_CREATOR = pymysql
|
||||
300
base_framework/public_tools/db_dbutils_init.py
Normal file
300
base_framework/public_tools/db_dbutils_init.py
Normal file
@@ -0,0 +1,300 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/11/6 17:34
|
||||
"""
|
||||
import pymysql
|
||||
import pymongo
|
||||
# from DBUtils.PooledDB import PooledDB
|
||||
from dbutils.pooled_db import PooledDB
|
||||
import psycopg2
|
||||
from psycopg2 import pool
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from base_framework.public_tools.read_config import InitConfig
|
||||
from base_framework.public_tools.read_config import ReadConfig, get_current_config
|
||||
# from base_framework.public_tools import db_config as config
|
||||
from base_framework.base_config.current_pth import *
|
||||
|
||||
"""
|
||||
@功能:创建数据库连接池
|
||||
"""
|
||||
as_db = ['ZZYY']
|
||||
|
||||
|
||||
class PgConnectionPool(InitConfig):
|
||||
pool = None
|
||||
pool_cache = dict()
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
super().__init__()
|
||||
self.DB_SSL = False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 创建数据库连接conn和游标cursor
|
||||
def __enter__(self):
|
||||
self.conn = self.__getconn()
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
def __getconn(self, choose_db=None):
|
||||
# 如果未指定数据库,使用默认配置
|
||||
if not choose_db:
|
||||
choose_db = 'default'
|
||||
|
||||
try:
|
||||
self.pool = self.pool_cache[choose_db]
|
||||
except KeyError:
|
||||
rc = ReadConfig(config_file_path)
|
||||
|
||||
# 根据choose_db值获取对应的PostgreSQL配置
|
||||
if choose_db == 'default':
|
||||
db_host = rc.get_value(sections='PostgreSQL', options='db_test_host')
|
||||
db_port = rc.get_value(sections='PostgreSQL', options='db_test_port')
|
||||
db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname')
|
||||
db_user = rc.get_value(sections='PostgreSQL', options='db_test_user')
|
||||
db_password = rc.get_value(sections='PostgreSQL', options='db_test_password')
|
||||
db_min_cached = rc.get_value(sections='PostgreSQL', options='db_min_cached')
|
||||
db_max_cached = rc.get_value(sections='PostgreSQL', options='db_max_cached')
|
||||
db_max_shared = rc.get_value(sections='PostgreSQL', options='db_max_shared')
|
||||
db_max_connecyions = rc.get_value(sections='PostgreSQL', options='db_max_connecyions')
|
||||
db_max_usage = rc.get_value(sections='PostgreSQL', options='db_max_usage')
|
||||
else:
|
||||
# 可以扩展其他数据库连接
|
||||
db_host = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_host')
|
||||
db_port = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_port')
|
||||
db_name = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_name')
|
||||
db_user = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_user')
|
||||
db_password = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_password')
|
||||
db_min_cached = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_min_cached')
|
||||
db_max_cached = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_cached')
|
||||
db_max_shared = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_shared')
|
||||
db_max_connecyions = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_connecyions')
|
||||
db_max_usage = rc.get_value(sections='PostgreSQL', options=f'db_{choose_db}_max_usage')
|
||||
|
||||
# PostgreSQL连接池配置
|
||||
try:
|
||||
print("=" * 80)
|
||||
print("PostgreSQL连接池配置信息:")
|
||||
print(" 主机(Host): {}".format(db_host))
|
||||
print(" 端口(Port): {}".format(db_port))
|
||||
print(" 数据库名(Database): {}".format(db_name))
|
||||
print(" 用户名(User): {}".format(db_user))
|
||||
print(" 密码(Password): {} (已隐藏)".format('*' * len(db_password) if db_password else 'None'))
|
||||
print(" 最小缓存连接数(MinCached): {}".format(db_min_cached))
|
||||
print(" 最大缓存连接数(MaxCached): {}".format(db_max_cached))
|
||||
print(" 最大共享连接数(MaxShared): {}".format(db_max_shared))
|
||||
print(" 最大连接数(MaxConnections): {}".format(db_max_connecyions))
|
||||
print(" 最大使用次数(MaxUsage): {}".format(db_max_usage))
|
||||
print(" SSL模式(SSLMode): {}".format('require' if self.DB_SSL else 'disable'))
|
||||
print(" 连接超时(ConnectTimeout): 30秒")
|
||||
print("=" * 80)
|
||||
|
||||
self.pool = PooledDB(
|
||||
creator=psycopg2,
|
||||
host=db_host,
|
||||
port=int(db_port),
|
||||
user=db_user,
|
||||
password=db_password,
|
||||
database=db_name,
|
||||
mincached=int(db_min_cached),
|
||||
maxcached=int(db_max_cached),
|
||||
maxshared=int(db_max_shared),
|
||||
maxconnections=int(db_max_connecyions),
|
||||
blocking=True,
|
||||
maxusage=int(db_max_usage),
|
||||
setsession=None,
|
||||
# PostgreSQL特定参数
|
||||
sslmode='require' if self.DB_SSL else 'disable',
|
||||
connect_timeout=30,
|
||||
keepalives=1,
|
||||
keepalives_idle=30,
|
||||
keepalives_interval=10,
|
||||
keepalives_count=5
|
||||
)
|
||||
self.pool_cache[choose_db] = self.pool
|
||||
print("PostgreSQL连接池创建成功")
|
||||
except Exception as e:
|
||||
error_msg = """
|
||||
PostgreSQL连接池创建失败!
|
||||
连接配置信息:
|
||||
主机(Host): {}
|
||||
端口(Port): {}
|
||||
数据库名(Database): {}
|
||||
用户名(User): {}
|
||||
密码(Password): {} (已隐藏)
|
||||
SSL模式(SSLMode): {}
|
||||
连接超时(ConnectTimeout): 30秒
|
||||
错误详情: {}
|
||||
""".format(
|
||||
db_host, db_port, db_name, db_user,
|
||||
'*' * len(db_password) if db_password else 'None',
|
||||
'require' if self.DB_SSL else 'disable',
|
||||
str(e)
|
||||
)
|
||||
print(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
try:
|
||||
return self.pool.connection()
|
||||
except Exception as e:
|
||||
# 尝试获取连接配置信息用于错误提示
|
||||
try:
|
||||
rc = ReadConfig(config_file_path)
|
||||
db_host = rc.get_value(sections='PostgreSQL', options='db_test_host')
|
||||
db_port = rc.get_value(sections='PostgreSQL', options='db_test_port')
|
||||
db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname')
|
||||
db_user = rc.get_value(sections='PostgreSQL', options='db_test_user')
|
||||
except:
|
||||
db_host = db_port = db_name = db_user = 'unknown'
|
||||
error_msg = """
|
||||
PostgreSQL连接获取失败!
|
||||
连接配置信息:
|
||||
主机(Host): {}
|
||||
端口(Port): {}
|
||||
数据库名(Database): {}
|
||||
用户名(User): {}
|
||||
错误详情: {}
|
||||
""".format(db_host, db_port, db_name, db_user, str(e))
|
||||
print(error_msg)
|
||||
raise Exception(error_msg)
|
||||
|
||||
# 释放连接池资源
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.cursor:
|
||||
self.cursor.close()
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
|
||||
# 获取连接和游标(返回字典形式的结果)
|
||||
def getconn(self, choose_db=None):
|
||||
conn = self.__getconn(choose_db=choose_db)
|
||||
# 使用RealDictCursor返回字典形式的游标
|
||||
cursor = conn.cursor(cursor_factory=RealDictCursor)
|
||||
return cursor, conn
|
||||
|
||||
|
||||
class MyConnectionPool(InitConfig):
|
||||
pool = None
|
||||
pool_cache = dict()
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
super().__init__()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.current_business = get_current_config(section='run_evn_name', key='current_business')
|
||||
|
||||
# 创建数据库连接conn和游标cursor
|
||||
def __enter__(self):
|
||||
self.conn = self.__getconn()
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
def __getconn(self, choose_db=None):
|
||||
current_team = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options='current_team')
|
||||
if not choose_db: # 没有指定,则按小组默认设置
|
||||
if current_team.upper() in as_db and choose_db is None:
|
||||
choose_db = 'as'
|
||||
elif current_team.upper() == "SE" and choose_db is None:
|
||||
choose_db = 'se'
|
||||
elif current_team.upper() == "XUEDAU" and choose_db is None:
|
||||
choose_db = 'xdu'
|
||||
elif current_team.upper() not in as_db and choose_db is None and self.current_business == 'hh':
|
||||
choose_db = 'hh'
|
||||
elif current_team.upper() not in as_db and choose_db is None and self.current_business == 'hhi':
|
||||
choose_db = 'hhi'
|
||||
try:
|
||||
self.pool = self.pool_cache[choose_db]
|
||||
except Exception as e:
|
||||
rc = ReadConfig(config_file_path)
|
||||
if choose_db == 'as':
|
||||
db_host = rc.get_value(sections='Mysql', options='db_as_svr')
|
||||
elif choose_db == 'se':
|
||||
db_host = rc.get_value(sections='Mysql', options='db_se_svr')
|
||||
elif choose_db == 'xdu':
|
||||
db_host = rc.get_value(sections='Mysql', options='db_xdu_svr')
|
||||
self.DB_TEST_USER = rc.get_value(sections='Mysql', options='db_xdu_user')
|
||||
self.DB_TEST_PASSWORD = rc.get_value(sections='Mysql', options='db_xdu_password')
|
||||
elif choose_db == 'hh' or choose_db == 'huohua':
|
||||
db_host = rc.get_value(sections='Mysql', options='db_hh_svr')
|
||||
elif choose_db == 'hhi':
|
||||
db_host = rc.get_value(sections='Mysql', options='db_hhi_svr')
|
||||
elif choose_db == 'hh.qa': # 自动化和信息化的数据都走huohua
|
||||
db_host = 'mysql.qa.huohua.cn'
|
||||
elif not choose_db: # 没有传入则默认走huohua
|
||||
choose_db = 'hh.qa'
|
||||
db_host = 'mysql.qa.huohua.cn'
|
||||
else:
|
||||
raise Exception("当前仅支持hh,hhi,as,se四个数据库服务器,而你选择的是:{}".format(choose_db))
|
||||
default_db = 'sys'
|
||||
self.pool = PooledDB(
|
||||
creator=pymysql,
|
||||
host=db_host,
|
||||
port=int(self.DB_TEST_PORT),
|
||||
user=self.DB_TEST_USER,
|
||||
passwd=self.DB_TEST_PASSWORD,
|
||||
db=default_db,
|
||||
mincached=int(self.DB_MIN_CACHED),
|
||||
maxcached=int(self.DB_MAX_CACHED),
|
||||
maxshared=int(self.DB_MAX_SHARED),
|
||||
maxconnections=int(self.DB_MAX_CONNECYIONS),
|
||||
blocking=True,
|
||||
maxusage=int(self.DB_MAX_USAGE),
|
||||
setsession=None,
|
||||
use_unicode=True,
|
||||
charset=self.DB_CHARSET
|
||||
)
|
||||
self.pool_cache[choose_db] = self.pool
|
||||
return self.pool.connection()
|
||||
|
||||
# 释放连接池资源
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.cursor.close()
|
||||
self.conn.close()
|
||||
|
||||
# 关闭连接归还给链接池
|
||||
# def close(self):
|
||||
# self.cursor.close()
|
||||
# self.conn.close()
|
||||
|
||||
# 从连接池中取出一个连接
|
||||
def getconn(self, choose_db=None):
|
||||
conn = self.__getconn(choose_db=choose_db)
|
||||
# 字典形式返回
|
||||
cursor = conn.cursor(pymysql.cursors.DictCursor)
|
||||
return cursor, conn
|
||||
|
||||
|
||||
# 获取连接池,实例化
|
||||
def get_my_connection():
|
||||
return MyConnectionPool()
|
||||
|
||||
|
||||
def get_pg_connection():
|
||||
return PgConnectionPool()
|
||||
|
||||
|
||||
class MongoConnectionPool(InitConfig):
|
||||
def __init__(self):
|
||||
try:
|
||||
# super().__init__()
|
||||
super(MongoConnectionPool, self).__init__()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def mongo_connect(self):
|
||||
try:
|
||||
self.connect_ = pymongo.MongoClient(host=self.MONGO_HOST,
|
||||
port=int(self.MONGO_PORT),
|
||||
username=self.MONGO_USER,
|
||||
password=self.MONGO_PASSWORD,
|
||||
authSource="hulk_teach_marketing"
|
||||
)
|
||||
except Exception as e:
|
||||
raise Exception("mongdb连接失败:{}".format(e))
|
||||
return self.connect_
|
||||
|
||||
|
||||
def get_my_mongo_connection():
|
||||
return MongoConnectionPool()
|
||||
168
base_framework/public_tools/edu_user_helper.py
Normal file
168
base_framework/public_tools/edu_user_helper.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# encoding: utf-8
|
||||
# @Time : 2022/4/18 上午10:36
|
||||
# @Author : chenjiang
|
||||
# @Site :
|
||||
# @File : edu_user_helper.py
|
||||
|
||||
import requests
|
||||
# import py_eureka_client.eureka_client as eureka_client
|
||||
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
from base_framework.public_tools.my_faker import MyFaker
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.utils import Tools
|
||||
|
||||
obj_log = log.get_logger()
|
||||
obj_my_faker = MyFaker()
|
||||
obj_my_sql_helper = MySqLHelper()
|
||||
obj_tools = Tools()
|
||||
|
||||
|
||||
# def get_ip_by_server_name(env_name, service_name, type):
|
||||
# """
|
||||
# 根据服务名称,环境,as/hh
|
||||
# :param env_name: 环境信息
|
||||
# :param service_name: 服务名称
|
||||
# :param type: as/hh
|
||||
# :return:
|
||||
# """
|
||||
# all_school_eureka_server= 'http://eureka.qa.allschool.com/eureka/'
|
||||
# hh_eureka_server = 'http://eureka.qa.huohua.cn/eureka/'
|
||||
# try:
|
||||
# if type.lower() == 'as':
|
||||
# eureka_server = all_school_eureka_server
|
||||
# else:
|
||||
# eureka_server = hh_eureka_server
|
||||
#
|
||||
# eureka_client.init(
|
||||
# eureka_server=eureka_server,
|
||||
# app_name="ASC--",
|
||||
# instance_ip="127.0.0.1",
|
||||
# instance_port=8080)
|
||||
# client = eureka_client.get_client()
|
||||
# app = client.applications.get_application(service_name)
|
||||
# ip_list = []
|
||||
# for app_in in app.up_instances:
|
||||
# if app_in.metadata.get('ver').lower() == env_name.lower() and app_in.ipAddr:
|
||||
# ip_list.append(app_in.ipAddr)
|
||||
# else:
|
||||
# pass
|
||||
# if ip_list:
|
||||
# return ip_list
|
||||
# else:
|
||||
# obj_log.info("未获取到ip")
|
||||
# return False
|
||||
# except Exception as e:
|
||||
# obj_log.info(e)
|
||||
# return False
|
||||
|
||||
|
||||
class EDUUserHelper:
|
||||
"""
|
||||
用户中心用户相关操作
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_unregistered_phone(self):
|
||||
"""
|
||||
| 功能说明: | 获取未注册的手机号 |
|
||||
| 输入参数: | |
|
||||
| 返回参数: | phone |
|
||||
| 作者信息: | 陈江 | 2022/4/18 |
|
||||
"""
|
||||
phone = obj_my_faker.gen_phone_number()
|
||||
if phone:
|
||||
is_exit = obj_my_sql_helper.select_one(
|
||||
'SELECT id FROM `ucenter`.`user_profile` WHERE `phone` = \'{}\''.format(phone[0]))
|
||||
if is_exit:
|
||||
obj_log.info('该{}手机号码已经存在,正在重新获取'.format(phone))
|
||||
self.get_unregistered_phone()
|
||||
else:
|
||||
return phone[0]
|
||||
else:
|
||||
obj_log.error('生成手机号码失败')
|
||||
return False
|
||||
|
||||
def get_unused_email(self):
|
||||
"""
|
||||
| 功能说明: | 获取未使用的邮箱 |
|
||||
| 输入参数: | |
|
||||
| 返回参数: | email|
|
||||
| 作者信息: | 陈江 | 2022/4/18 |
|
||||
"""
|
||||
email = obj_my_faker.gen_email()
|
||||
if email:
|
||||
is_exit = obj_my_sql_helper.select_one(
|
||||
'SELECT id FROM `ucenter`.`user_contact` WHERE `contact_info` = \'{}\''.format(email))
|
||||
if is_exit:
|
||||
obj_log.info('该{}邮箱已经存在,正在重新获取'.format(email))
|
||||
self.get_unused_email()
|
||||
else:
|
||||
return email
|
||||
else:
|
||||
obj_log.error('生成邮箱失败')
|
||||
return False
|
||||
|
||||
def get_sms_code_by_phone(self, phone):
|
||||
"""
|
||||
| 功能说明: | 根据手机号码获取短信验证码 |
|
||||
| 输入参数: | phone | 手机号|
|
||||
| 返回参数: | 验证码 |
|
||||
| 作者信息: | 陈江 | 2022/4/18 |
|
||||
"""
|
||||
|
||||
# 根据phone获取phone_code
|
||||
phone_server_ip = obj_tools.get_container_ip_from_eureka('PHONE-SERVER', need_jira_id='qa',
|
||||
eureka_url='http://eureka.qa.huohua.cn')
|
||||
if phone_server_ip.get('container_ip'):
|
||||
url = 'http://{}:8080/encrypt/regdata?biztype=phone&uid=123456&sourceData={}'.format(
|
||||
phone_server_ip.get('container_ip'), phone)
|
||||
response = requests.post(url=url) # 三个参数
|
||||
response_json = response.json()
|
||||
else:
|
||||
obj_log.info('未获取到phone-server的ip')
|
||||
return False
|
||||
if response_json.get('data'):
|
||||
msg = obj_my_sql_helper.select_all(
|
||||
'SELECT msg FROM `push_service`.`sms` WHERE `phone_code` = \'{}\' ORDER BY id DESC LIMIT 10 '.format(
|
||||
response_json.get('data')))
|
||||
if msg:
|
||||
for m in msg:
|
||||
try:
|
||||
if ',' in m.get('msg'):
|
||||
msg_split = m.get('msg').split(',')[0]
|
||||
elif ',' in m.get('msg'):
|
||||
msg_split = m.get('msg').split(',')[0]
|
||||
else:
|
||||
return False
|
||||
if ':' in msg_split:
|
||||
code = msg_split.split(':')[1].strip(' ')
|
||||
elif ':' in msg_split:
|
||||
code = msg_split.split(':')[1].strip(' ')
|
||||
else:
|
||||
import re
|
||||
pattern = r'\b(\d{4,6})\b.*?verification code'
|
||||
match = re.search(pattern, msg_split)
|
||||
if match:
|
||||
verification_code = match.group(1)
|
||||
return verification_code
|
||||
return False
|
||||
return code
|
||||
except Exception as e:
|
||||
return e
|
||||
else:
|
||||
obj_log.info('未找到发送的短信记录')
|
||||
return False
|
||||
else:
|
||||
obj_log.info('获取{}该手机号的phonecode失败'.format(phone))
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = EDUUserHelper()
|
||||
print(a.get_sms_code_by_phone('13563963497'))
|
||||
# c = {'msg': 'SMS verification code: 6378, valid within 10 minutes, please ignore if you are not operating by yourself.'}
|
||||
#
|
||||
# print(c.get('msg').split(',')[0].split(':')[1].strip(' '))
|
||||
85
base_framework/public_tools/es_api.py
Normal file
85
base_framework/public_tools/es_api.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
|
||||
import requests
|
||||
from base_framework.public_tools.custom_error import BusinessError
|
||||
|
||||
class ElasticsearchApi:
|
||||
"""
|
||||
| 功能说明: | 查询Elasticsearch |
|
||||
| 作者信息: | 作者 huaxuemin |
|
||||
| 修改时间: | 2022-12-05 |
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.es_url = "http://es-cn-zvp2bgn8a004cbosn.elasticsearch.aliyuncs.com:9200"
|
||||
self.headers = {"Authorization": "Basic ZWxhc3RpYzpMa3N3aV5hbEl3"}
|
||||
|
||||
def kw_es_query(self, es_query_body, index="user_personas_index_qa", es_url=None, headers=None):
|
||||
"""
|
||||
| 功能说明: | 查询es |
|
||||
| 输入参数: |
|
||||
| | es_query_body | es请求参数,符合es语法 |
|
||||
| | index | 索引 |
|
||||
| | es_url | es_url |
|
||||
| 返回参数: | json |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2022-12-05 |
|
||||
说明:根据es语法查询es
|
||||
"""
|
||||
es_url = es_url if es_url else self.es_url
|
||||
req_es_url = es_url + "/" + index + "/" + "_search"
|
||||
headers = self.headers.update(headers) if headers else self.headers
|
||||
try:
|
||||
res = requests.post(req_es_url, json=es_query_body, headers=headers)
|
||||
return json.loads(res.text)
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
def kw_es_replace(self, es_id, es_replace_body, index="user_personas_index_qa", es_url=None, headers=None):
|
||||
"""
|
||||
| 功能说明: | 根据id替换es数据 |
|
||||
| 输入参数: |
|
||||
| | es_id | es_id |
|
||||
| | es_query_body | es请求参数,符合es语法 |
|
||||
| | index | 索引 |
|
||||
| | es_url | es_url |
|
||||
| 返回参数: | json |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2022-12-05 |
|
||||
说明:根据es语法替换es数据
|
||||
"""
|
||||
es_url = es_url if es_url else self.es_url
|
||||
req_es_url = es_url + "/" + index + "/" + "_doc" + "/" + es_id
|
||||
headers = self.headers.update(headers) if headers else self.headers
|
||||
try:
|
||||
res = requests.post(req_es_url, json=es_replace_body, headers=headers)
|
||||
return json.loads(res.text)
|
||||
except Exception as e:
|
||||
return e
|
||||
|
||||
def kw_dbs_to_es(self, index_name, env="qa", es_svr="db-to-es"):
|
||||
"""
|
||||
| 功能说明 | 同步某个索引的es数据 |
|
||||
| 请求参数名 | 说明 | 类型 | 是否必填 | 如无要求时的值 |
|
||||
| index_name | 索引名称 | string | 0 | peppa-classes-supply |
|
||||
| env | 环境,qa,sim | string | 0 | qa |
|
||||
| es_svr | ES服务,db-to-es,teach-to-es等 | string | 0 | db-to-es |
|
||||
:return
|
||||
详见: https://tm.huohua.cn/162891389180100609/articles/215540479291179010
|
||||
教务接口切换ES详见apollo配置:peppa-teach-common teach.common change.to.es
|
||||
"""
|
||||
if env not in ("qa", "sim"):
|
||||
raise BusinessError("只支持qa和sim")
|
||||
resp = requests.get(url="http://{}.{}.huohua.cn?index={}".format(es_svr, env, index_name))
|
||||
if resp.status_code != 200:
|
||||
raise Exception('同步失败,错误信息:{}'.format(resp.text))
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
es = ElasticsearchApi()
|
||||
# print(es.kw_dbs_to_es(index_name='peppa-classes-supply'))
|
||||
q_sql = {"query":{"bool":{"must":[{"term":{"classes.business_region":"101"}},{"term":{"classes.year":"2023"}}],"must_not":[],"should":[]}},"from":0,"size":10,"sort":[{"classes.id":{"order":"desc"}}],"aggs":{}}
|
||||
q_url = "http://10.250.200.194:9200"
|
||||
rsp = es.kw_es_query(es_query_body=q_sql, es_url=q_url, index="peppa-classes-supply2")
|
||||
print(rsp)
|
||||
|
||||
243
base_framework/public_tools/eureka_api.py
Normal file
243
base_framework/public_tools/eureka_api.py
Normal file
@@ -0,0 +1,243 @@
|
||||
# -*-coding:utf-8-*-
|
||||
import requests
|
||||
import re
|
||||
import configparser
|
||||
import time
|
||||
import os
|
||||
|
||||
from base_framework.platform_tools.Message_service.Feishu_api import FeiShuMessage
|
||||
from base_framework.public_tools.read_config import get_current_config,get_zhyy_config
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
from base_framework.base_config.current_pth import HERE
|
||||
from bs4 import BeautifulSoup
|
||||
from base_framework.public_tools.utils import Tools
|
||||
from lxml import etree
|
||||
|
||||
from base_framework.public_tools import log
|
||||
|
||||
obj_log = log.get_logger()
|
||||
obj_tool = Tools()
|
||||
|
||||
|
||||
class EurekaAPI:
|
||||
def __init__(self):
|
||||
self.business = get_current_config(section="run_evn_name", key="current_business")
|
||||
self.team = get_current_config(section="run_evn_name", key="current_team")
|
||||
self.evn = get_current_config(section="run_evn_name", key="current_evn")
|
||||
self.jira_id = get_current_config(section="run_jira_id", key="huohua-podenv")
|
||||
self.is_ip_from_ini = get_current_config(section="is_ip_from_ini", key="is_ip_from_ini")
|
||||
|
||||
self.server_list = list()
|
||||
self.message = FeiShuMessage(team='AUTOMATION')
|
||||
if self.jira_id == '':
|
||||
self.jira_id = 'qa'
|
||||
self.server_to_domain = {"PEPPA-CORE-API": "https://core-api.qa.huohua.cn/"}
|
||||
self.sim_server_to_domain = {"PEPPA-TEACH-API": "https://teach-api.sim.huohua.cn/"}
|
||||
|
||||
def __get_eureka_url_from_db(self, team):
|
||||
"""
|
||||
| 功能 | 从DB中获取服务对应的eureka_url,供get_container_ip_from_eureka函数使用 |
|
||||
"""
|
||||
sql_str = "select eureka_url as hh, eureka_url_hhi as hhi " \
|
||||
"from sparkatp.swagger_info where team='{}';".format(team)
|
||||
res = MySqLHelper().select_one(sql=sql_str)
|
||||
return res[self.business]
|
||||
|
||||
def __get_eureka_info_by_type(self, eureka_type):
|
||||
"""
|
||||
| 功能 | 根据eureka类型,返回对应的url和配置文件名 |
|
||||
"""
|
||||
if eureka_type.lower() == "hh":
|
||||
eureka_url = "http://eureka.{}.huohua.cn/".format(self.evn.lower())
|
||||
eureka_file_name = "eureka_{}_huohua_cn.ini".format(self.evn.lower())
|
||||
elif eureka_type.lower() == "vsl":
|
||||
eureka_url = "http://eureka.{}.visparklearning.com/".format(self.evn.lower())
|
||||
eureka_file_name = "eureka_{}_visparklearning_com.ini".format(self.evn.lower())
|
||||
elif eureka_type.lower() == "as":
|
||||
eureka_url = "http://eureka.{}.allschool.com/".format(self.evn.lower())
|
||||
eureka_file_name = "eureka_{}_allschool_com.ini".format(self.evn.lower())
|
||||
elif eureka_type.lower() == "ec":
|
||||
eureka_url = "http://eureka-core.{}.huohua.cn/".format(self.evn.lower())
|
||||
eureka_file_name = "eureka_core_{}_huohua_cn.ini".format(self.evn.lower())
|
||||
elif eureka_type.lower() == "xdu":
|
||||
eureka_url = "http://eureka.{}.xuedau.com/".format(self.evn.lower())
|
||||
eureka_file_name = "eureka_{}_xuedau_com.ini".format(self.evn.lower())
|
||||
else:
|
||||
raise Exception("eureka类型仅支持HH,VSL,AS,EC和XDU,但你的输入的是:{}".format(eureka_type))
|
||||
return eureka_url, eureka_file_name
|
||||
|
||||
def get_all_server_ip_from_eureka(self, eureka="HH"):
|
||||
"""
|
||||
| 功能说明: | 获取Eureka全部服务的IP,并写入对应文件中文件中 |
|
||||
| 输入参数: | eureka | 类型:HH | http://eureka.qa.huohua.cn/ |
|
||||
| | | 类型:VSL | http://eureka.qa.visparklearning.com/ |
|
||||
| | | 类型:AS | http://eureka.qa.allschool.com/ |
|
||||
| | | 类型:EC | http://eureka-core.qa.huohua.cn/ |
|
||||
| | | 类型:XDU | http://eureka.qa.xuedau.com/ |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 谯新久 | 2022.03.27 |
|
||||
"""
|
||||
eureka_url, eureka_file_name = self.__get_eureka_info_by_type(eureka_type=eureka)
|
||||
server_ip_path = os.path.join(HERE, eureka_file_name)
|
||||
|
||||
temp_text = requests.get(url=eureka_url).content
|
||||
soup = BeautifulSoup(temp_text.decode('utf-8'), "html.parser")
|
||||
all_container_ip = soup.find_all(href=re.compile("actuator/info"))
|
||||
tree_dict = dict()
|
||||
for temp in all_container_ip:
|
||||
server = list(temp.parent.parent)[1].get_text()
|
||||
server_ip = list(temp)
|
||||
if server in tree_dict.keys():
|
||||
tree_dict[server].extend(server_ip)
|
||||
else:
|
||||
tree_dict[server] = server_ip
|
||||
cof = configparser.ConfigParser()
|
||||
cof.read(server_ip_path, encoding='utf-8')
|
||||
eureka_section = self.business
|
||||
if eureka_section not in cof.sections():
|
||||
cof.add_section(section=eureka_section)
|
||||
for svr_name in tree_dict:
|
||||
cof.set(section=eureka_section, option=svr_name, value=str(tree_dict[svr_name]))
|
||||
with open(server_ip_path, 'w') as fw: # 循环写入
|
||||
cof.write(fw)
|
||||
time.sleep(2) # 等待5s,让ip写入到文件中去
|
||||
|
||||
def get_server_ip_from_config(self, server_name, eureka="HH"):
|
||||
"""
|
||||
| 功能说明: | 获取server_ip.ini文件中获取server_name对应的IP |
|
||||
| 输入参数: | server_name | 服务名 |
|
||||
| | eureka | 类型:HH | http://eureka.qa.huohua.cn/ |
|
||||
| | | 类型:VSL | http://eureka.qa.visparklearning.com/ |
|
||||
| | | 类型:AS | http://eureka.qa.allschool.com/ |
|
||||
| | | 类型:EC | http://eureka-core.qa.huohua.cn/ |
|
||||
| 返回参数: | string | 如:10.10.10.10:8080 |
|
||||
| 作者信息: | 谯新久 | 2022.03.27 |
|
||||
特别说明:
|
||||
| 1 | 独立环境取startup.py启动时传入的值,没有对应的独立环境则取qa的ip返回 |
|
||||
| 2 | 当同一独立环境存在多个ip时,打印error日志后,返回第一个ip |
|
||||
"""
|
||||
eureka_url, eureka_file_name = self.__get_eureka_info_by_type(eureka_type=eureka)
|
||||
server_ip_path = os.path.join(HERE, eureka_file_name)
|
||||
ip_list = get_current_config(file_path=server_ip_path, section=self.business, key=server_name.lower())
|
||||
if self.evn.lower() == "sim":
|
||||
ip_list = ip_list.replace("${server.port}", "8080") # 替换成8080端口
|
||||
if self.jira_id in ip_list:
|
||||
jira = self.jira_id
|
||||
elif "sim" in ip_list:
|
||||
jira = "sim"
|
||||
elif "no" in ip_list:
|
||||
jira = "no"
|
||||
else:
|
||||
jira = ""
|
||||
elif self.jira_id in ip_list:
|
||||
jira = self.jira_id
|
||||
elif 'qa' in ip_list:
|
||||
jira = 'qa'
|
||||
elif 'groot' in ip_list:
|
||||
jira = 'groot'
|
||||
else:
|
||||
jira = 'qa'
|
||||
if ip_list != 'server not exist':
|
||||
if jira and jira not in ip_list:
|
||||
if server_name.lower() not in self.server_list:
|
||||
self.server_list.append(server_name.lower())
|
||||
message = "{}在启动startup时发现服务{}在独立环境{}未部署成功,请确认。".format(self.team, server_name, jira)
|
||||
self.message.send_text(message)
|
||||
|
||||
obj_ip = []
|
||||
ip_str = ip_list.replace('[', '').replace(']', '').replace('\'', '').replace(' ', '')
|
||||
ip_list = ip_str.split(',')
|
||||
for ip in ip_list:
|
||||
if jira and jira in ip: # 找到对应独立环境的ip
|
||||
if ip.startswith('10.'): # 跳过非
|
||||
obj_ip.append(ip)
|
||||
if self.evn.lower() == "sim": # sim环境只有一个ip时,直接返回次ip
|
||||
if not obj_ip and len(ip_list) == 1:
|
||||
ips = ip_list[0].split(":")
|
||||
return "{}:{}".format(ips[0], ips[1])
|
||||
if self.evn.lower() != "sim" and len(obj_ip) > 1: # sim环境不判断是否有多个部署
|
||||
obj_log.error("eureka中含有{}个{}的{}环境,请检查.........".format(len(obj_ip), server_name, jira))
|
||||
elif len(obj_ip) == 0:
|
||||
obj_log.error("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, jira))
|
||||
return "server not exist"
|
||||
# return obj_ip[0].rstrip("{}".format(jira))[:-1]
|
||||
return obj_ip[0].split("{}".format(jira))[0].rstrip(":")
|
||||
else:
|
||||
obj_log.error("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, jira))
|
||||
return "server not exist"
|
||||
|
||||
def get_server_url_from_config(self, server_name, eureka="HH", is_domain=True):
|
||||
"""
|
||||
| 功能说明: | 从对应文件中获取server_name对应的IP,组装成url后返回 |
|
||||
| 输入参数: | server_name | 服务名 |
|
||||
| | eureka | 类型:HH | http://eureka.qa.huohua.cn/ |
|
||||
| | | 类型:VSL | http://eureka.qa.visparklearning.com/ |
|
||||
| | | 类型:AS | http://eureka.qa.allschool.com/ |
|
||||
| | | 类型:EC | http://eureka-core.qa.huohua.cn/ |
|
||||
| | | 类型:XDU | http://eureka.qa.xuedau.com/ |
|
||||
| | is_domain | 是否域名,True域名,False IP |
|
||||
| 返回参数: | string | 如:http://10.10.10.10:8080/ |
|
||||
| 作者信息: | 谯新久 | 2022.03.27 |
|
||||
特别说明:
|
||||
| 1 | 独立环境取startup.py启动时传入的值,没有对应的独立环境则取qa的ip返回 |
|
||||
| 2 | 当同一独立环境存在多个ip时,打印error日志后,返回第一个ip |
|
||||
"""
|
||||
if is_domain:
|
||||
if self.evn == "SIM":
|
||||
if server_name.upper() in self.sim_server_to_domain.keys():
|
||||
return self.sim_server_to_domain[server_name.upper()]
|
||||
return "https://swagger.sim.huohua.cn/{}/".format(server_name.lower())
|
||||
if server_name.upper() in self.server_to_domain.keys():
|
||||
return self.server_to_domain[server_name.upper()]
|
||||
return "https://swagger.qa.huohua.cn/{}/".format(server_name.lower())
|
||||
else:
|
||||
# eureka_url, _ = self.__get_eureka_info_by_type(eureka_type=eureka)
|
||||
# ip_res = obj_tool.get_container_ip_from_eureka(server_name=server_name, jira_id_dev=self.jira_id,
|
||||
# eureka_url=eureka_url)
|
||||
# if ip_res:
|
||||
# ip_info = ip_res["container_ip"]
|
||||
# url_info = "http://" + ip_info + ":8080/"
|
||||
# return url_info
|
||||
|
||||
# # if self.is_ip_from_ini == "true":
|
||||
ip_info = self.get_server_ip_from_config(server_name=server_name.lower(), eureka=eureka)
|
||||
if ip_info != 'server not exist':
|
||||
url_info = "http://" + ip_info + "/"
|
||||
return url_info
|
||||
else:
|
||||
raise Exception("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, self.jira_id))
|
||||
|
||||
def get_url_from_config(self, is_domain=True):
|
||||
"""
|
||||
| 功能说明: | 从对应文件中获取对应环境的域名,组装成url后返回 |
|
||||
| 输入参数: | 环境 | 服务名 |
|
||||
| | is_domain | 是否域名,True域名,False IP |
|
||||
| 返回参数: | string | 如:http://10.10.10.10:8080/ |
|
||||
| 作者信息: | 谯新久 | 2026.01.15 |
|
||||
"""
|
||||
if is_domain:
|
||||
env = self.evn.upper()
|
||||
team = self.team.lower()
|
||||
domain_url = get_zhyy_config(section=env, key=team)
|
||||
return domain_url
|
||||
else:
|
||||
#todo 暂无ip
|
||||
return
|
||||
|
||||
# # if self.is_ip_from_ini == "true":
|
||||
# ip_info = self.get_server_ip_from_config(server_name=server_name.lower(), eureka=eureka)
|
||||
# if ip_info != 'server not exist':
|
||||
# url_info = "http://" + ip_info + "/"
|
||||
# return url_info
|
||||
# else:
|
||||
# raise Exception("eureka中未找到{}的{}相关的ip信息,请检查.........".format(server_name, self.jira_id))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
er = EurekaAPI()
|
||||
# er.get_all_server_ip_from_eureka
|
||||
# er.get_all_server_ip_from_eureka(eureka="VSL")
|
||||
res = er.get_server_url_from_config(server_name='peppa-scm-server', is_domain=False)
|
||||
print(res)
|
||||
# res = er.get_server_url_from_config(server_name='sparkedu-api', eureka="VSL")
|
||||
# print(res)
|
||||
272
base_framework/public_tools/excel_api.py
Normal file
272
base_framework/public_tools/excel_api.py
Normal file
@@ -0,0 +1,272 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
# @File: operation_Xlsx.py
|
||||
# @Description: excle的基本操作
|
||||
# @Author: WenQing
|
||||
# @Date: 2021-08-26 11:15:08
|
||||
import os
|
||||
import xlrd, xlwt
|
||||
import pandas as pd
|
||||
from xlutils.copy import copy
|
||||
import openpyxl
|
||||
from base_framework.base_config.current_pth import env_choose_path
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
|
||||
|
||||
class ExcelApi:
|
||||
def __init__(self):
|
||||
self.p_here = os.path.dirname(os.path.abspath(__file__))
|
||||
self.c_team = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options='current_team')
|
||||
self.up_file_path = os.path.abspath(os.path.join(self.p_here, '../../{}/library/UpFile/'.format(self.c_team)))
|
||||
|
||||
def excel_create_excel_file(self, file_name, file_dict_data=None, file_path=None):
|
||||
"""
|
||||
功能:按列将数据写入excel文件,没有文件时就新建文件
|
||||
Args:
|
||||
file_name: 文件名
|
||||
file_dict_data: 文件内容,字典类型,格式:{'title1':[], 'title2':[],..... 'titleN':[],}
|
||||
file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中
|
||||
Returns:
|
||||
"""
|
||||
if not file_path:
|
||||
full_path = self.up_file_path + os.sep + file_name
|
||||
else:
|
||||
full_path = file_path + os.sep + file_name
|
||||
if not file_dict_data:
|
||||
file_dict_data = {}
|
||||
df = pd.DataFrame(file_dict_data)
|
||||
# 将 DataFrame 的数据保存到 Excel 文件中
|
||||
df.to_excel(full_path, index=False)
|
||||
|
||||
def excel_create_file_by_column(self, file_name, file_dict_data=None, file_path=None):
|
||||
"""
|
||||
功能:按列将数据写入excel文件,没有文件时就新建文件
|
||||
Args:
|
||||
file_name: 文件名
|
||||
file_dict_data: 文件内容,字典类型,格式:{'title1':[], 'title2':[],..... 'titleN':[],}
|
||||
file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中
|
||||
Returns:
|
||||
"""
|
||||
if not file_path:
|
||||
full_path = self.up_file_path + os.sep + file_name
|
||||
else:
|
||||
full_path = file_path + os.sep + file_name
|
||||
if not file_dict_data:
|
||||
file_dict_data = {}
|
||||
df = pd.DataFrame(file_dict_data)
|
||||
# 将 DataFrame 的数据保存到 Excel 文件中
|
||||
df.to_excel(full_path, index=False)
|
||||
|
||||
def excel_write_file_by_line(self, file_name, line_data=None, file_path=None, w_type='new'):
|
||||
"""
|
||||
功能:按行将数据写入excel文件,没有文件时就新建文件
|
||||
Args:
|
||||
file_name: 文件名
|
||||
line_data: 文件内容,列表类型,格式:[[字段1,字段2],[字段1,字段2]]
|
||||
file_path: 文件路径,没有时默认放在小组目录下的UpFile文件夹中
|
||||
w_type: 写入类型:new-重写,append-追加
|
||||
"""
|
||||
if not file_path:
|
||||
full_path = self.up_file_path + os.sep + file_name
|
||||
else:
|
||||
full_path = file_path + os.sep + file_name
|
||||
if w_type == 'new':
|
||||
self.write_xlsx(path=full_path, sheet_name="Sheet1", value=line_data)
|
||||
elif w_type == 'append':
|
||||
self.append_xlsx(path=full_path, value=line_data)
|
||||
else:
|
||||
raise Exception("目前仅支持new和append两种模式,而当前传入的是;{}".format(w_type))
|
||||
|
||||
def excel_replace_cell_value(self, file_name, replace_dict, sheet_name=None, skip_first_row=True):
|
||||
"""
|
||||
功能:替换excel文件中的指定单元格的值
|
||||
Args:
|
||||
file_name: 文件名,如果文件不在UpFile文件夹中,需要传入完整路径
|
||||
sheet_name: 工作表名称,默认为全部工作表
|
||||
replace_dict: 替换的数据,字典类型,格式:{'old_txt_1': 'new_txt_1', 'old_txt_2': 'new_txt_2',..... }
|
||||
skip_first_row: 是否跳过第一行,默认跳过,用于第一行是标题的情况
|
||||
Returns:
|
||||
"""
|
||||
if "/" not in file_name: # 如果没有路径,就默认在UpFile文件夹中
|
||||
file_name = self.up_file_path + os.sep + file_name
|
||||
# 读取指定工作表的数据
|
||||
wb = openpyxl.load_workbook(file_name)
|
||||
# 遍历所有要替换的数据
|
||||
for old_text, new_text in replace_dict.items():
|
||||
# 遍历所有工作表
|
||||
for sheet in wb.worksheets:
|
||||
# 遍历工作表中的所有行和列
|
||||
for row in sheet.iter_rows():
|
||||
if skip_first_row: # 跳过第一行
|
||||
skip_first_row = False
|
||||
continue
|
||||
for cell in row:
|
||||
if cell.value == old_text:
|
||||
cell.value = new_text
|
||||
wb.save(file_name)
|
||||
|
||||
def excel_read_columns(self, file_name, sheet_name, column_names):
|
||||
"""
|
||||
功能:按列读取excel文件的数据
|
||||
Args:
|
||||
file_name: 文件名,如果文件不在UpFile文件夹中,需要传入完整路径
|
||||
sheet_name: 工作表名称
|
||||
column_names: 列名列表,格式:['title1', 'title2',..... 'titleN']
|
||||
Returns:
|
||||
返回指定列的数据,格式为:[[字段1,字段2],[字段1,字段2]]
|
||||
"""
|
||||
if "/" not in file_name: # 如果没有路径,就默认在UpFile文件夹中
|
||||
file_name = self.up_file_path + os.sep + file_name
|
||||
# 读取指定工作表的数据
|
||||
df = pd.read_excel(file_name, sheet_name=sheet_name)
|
||||
|
||||
# 检查指定列是否存在
|
||||
missing_columns = [col for col in column_names if col not in df.columns]
|
||||
if missing_columns:
|
||||
raise ValueError(f"Columns {missing_columns} do not exist in the sheet '{sheet_name}'.")
|
||||
|
||||
# 返回指定列的数据
|
||||
return df[column_names].values.tolist()
|
||||
|
||||
def read_asDict(self, path):
|
||||
"""
|
||||
| 功能说明: | 读取excle,输出字典格式|
|
||||
| 传入参数: | 读取文件路径 |
|
||||
| 返回数据: | 表头字段作为key,单元格值作为value |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
self.table = pd.read_excel(path)
|
||||
data = []
|
||||
for i in self.table.index.values:
|
||||
data_dict = self.table.loc[i].to_dict()
|
||||
data.append(data_dict)
|
||||
return data
|
||||
|
||||
def read_asList(self, path, sheetname=None):
|
||||
"""
|
||||
| 功能说明: | 读取excle和sheetname,输出列表格式|
|
||||
| 传入参数: | 读取excle和sheetname |
|
||||
| 返回数据: | 列表[] |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
wb = openpyxl.load_workbook(path)
|
||||
if not sheetname:
|
||||
sheets = wb.sheetnames
|
||||
sheetname = sheets[0]
|
||||
ws = wb[sheetname]
|
||||
rows = ws.rows
|
||||
columns = ws.columns
|
||||
data = []
|
||||
for row in rows:
|
||||
line = [col.value for col in row]
|
||||
data.append(line)
|
||||
return data
|
||||
|
||||
def read_xls_txt(self, path):
|
||||
"""
|
||||
| 功能说明: | 读取excle的第一列放在一个列表中|
|
||||
| 传入参数: | 读取excle路径 |
|
||||
| 返回数据: | 列表[] |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
data = pd.read_excel(path, header=None)
|
||||
data_list = []
|
||||
nrows = data.shape[0]
|
||||
for irow in range(nrows):
|
||||
data_list.append(data.iloc[irow, 0])
|
||||
string = ''
|
||||
for i in range(len(data_list)):
|
||||
string += data_list[i] + ','
|
||||
return string
|
||||
|
||||
def write_xls(self, value, sheetname, path):
|
||||
"""
|
||||
| 功能说明: | 创建一个xls的文件并且写入数据 |
|
||||
| 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] |
|
||||
|sheetname:sheet名称 |
|
||||
|path:写入文件路径 |
|
||||
| 返回数据: | |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
index = len(value) # 获取需要写入数据的行数
|
||||
workbook = xlwt.Workbook() # 新建一个工作簿
|
||||
sheet = workbook.add_sheet(sheetname=sheetname) # 在工作簿中新建一个sheetname的表格
|
||||
for i in range(0, index):
|
||||
for j in range(0, len(value[i])):
|
||||
sheet.write(i, j, value[i][j])
|
||||
workbook.save(path)
|
||||
# return "写入成功!"
|
||||
|
||||
def append_xls(self, value, path):
|
||||
"""
|
||||
| 功能说明: | 对xls文件第一个sheet内容进行追加 |
|
||||
| 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] |
|
||||
|path:写入文件路径 |
|
||||
| 返回数据: | |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
index = len(value) # 获取需要写入数据的行数
|
||||
workbook = xlrd.open_workbook(path) # 打开工作簿
|
||||
sheets = workbook.sheet_names() # 获取所有表格
|
||||
worksheet = workbook.sheet_by_name(sheets[0]) # 获取所有表格中的第一个表格
|
||||
rows_old = worksheet.nrows # 获取表格的总行数
|
||||
new_workbook = copy(workbook) # 把原文件复制一份
|
||||
new_worksheet = new_workbook.get_sheet(0) # 获取副本第一个表格
|
||||
for i in range(0, index):
|
||||
for j in range(0, len(value[i])):
|
||||
new_worksheet.write(i + rows_old, j, value[i][j])
|
||||
new_workbook.save(path)
|
||||
# return "追加成功!!"
|
||||
|
||||
def write_xlsx(self, path, sheet_name, value):
|
||||
"""
|
||||
| 功能说明: | 创建一个xls的文件并且写入数据 |
|
||||
| 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] |
|
||||
|sheetname:sheet名称 |
|
||||
|path:写入文件路径 |
|
||||
| 返回数据: | |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
index = len(value)
|
||||
workbook = openpyxl.Workbook()
|
||||
sheet = workbook.active
|
||||
sheet.title = sheet_name
|
||||
for i in range(0, index):
|
||||
for j in range(0, len(value[i])):
|
||||
sheet.cell(row=i + 1, column=j + 1, value=str(value[i][j]))
|
||||
workbook.save(path)
|
||||
return "写入成功!!"
|
||||
|
||||
def append_xlsx(self, path, value):
|
||||
"""
|
||||
| 功能说明: | 对xls文件第一个sheet内容进行追加 |
|
||||
| 传入参数: | value:传入列表,格式:[[字段1,字段2],[字段1,字段2],[字段1,字段2],[字段1,字段2]] |
|
||||
|path:写入文件路径 |
|
||||
| 返回数据: | |
|
||||
| 作者信息: | 作者 文青 | 修改时间 |
|
||||
举例说明:
|
||||
"""
|
||||
data = openpyxl.load_workbook(path)
|
||||
sheets = data.sheetnames
|
||||
table = data[sheets[0]]
|
||||
for i in value:
|
||||
table.append(i)
|
||||
data.save(path)
|
||||
return "追加成功!!"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ea = ExcelApi()
|
||||
f_data = {"教师ID": [1, 2, 3, 4],
|
||||
"教师姓名": [11, 22, 33, 44],
|
||||
"课程ID": [111, 222, 333, 444],
|
||||
"课程名称": [1111, 2222, 3333, 4444],
|
||||
"更新类型(1.新增;0.删除,填写数字)": [0, 1, 0, 1]
|
||||
}
|
||||
ea.excel_create_excel_file(file_name="测试大盘A标签.xlsx", file_dict_data=f_data)
|
||||
|
||||
911
base_framework/public_tools/get_token.py
Normal file
911
base_framework/public_tools/get_token.py
Normal file
@@ -0,0 +1,911 @@
|
||||
import json
|
||||
from urllib import parse
|
||||
|
||||
import requests
|
||||
from retrying import retry
|
||||
from base_framework.public_tools.read_config import InitConfig
|
||||
from base_framework.public_tools.read_config import ReadConfig, get_current_config, get_current_env
|
||||
from base_framework.base_config.current_pth import *
|
||||
from requests.packages.urllib3.connectionpool import InsecureRequestWarning
|
||||
from base_framework.public_tools import log
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from Crypto.PublicKey import RSA
|
||||
import base64
|
||||
|
||||
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
import time
|
||||
import re
|
||||
# get_cfg = ReadConfig(env_choose_path)
|
||||
token_dict = dict()
|
||||
student_token_cache = {}
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
class LazyProperty:
|
||||
def __init__(self, fun):
|
||||
self.fun = fun
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
if instance is None:
|
||||
return self
|
||||
value = self.fun(instance)
|
||||
setattr(instance, self.fun.__name__, value)
|
||||
return value
|
||||
|
||||
|
||||
class LoginSys(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
try:
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.host = host
|
||||
self.curt_user = self.current_user
|
||||
self.env = get_current_env()
|
||||
|
||||
@LazyProperty
|
||||
def get_session(self):
|
||||
"""
|
||||
获取SSO session
|
||||
:return:
|
||||
"""
|
||||
sys_type = self.host.split(".")
|
||||
if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
api_url = self.curt_sso_url
|
||||
elif 'hulk-content-audit-server' in sys_type or 'hulk-content-manage-gateway' in sys_type or 'hulk-class-help-manager' in sys_type or 'hulk-ark-gateway' in sys_type or 'manage-api' in sys_type :
|
||||
api_url = self.all_school_sso_url
|
||||
else:
|
||||
api_url = self.curt_sso_url
|
||||
post_data = dict()
|
||||
post_data['showUsername'] = self.show_username
|
||||
post_data['username'] = self.username
|
||||
post_data['password'] = self.password
|
||||
req_session = requests.session()
|
||||
resp = req_session.post(
|
||||
url=api_url,
|
||||
data=post_data,
|
||||
allow_redirects=False,
|
||||
verify=False)
|
||||
return req_session
|
||||
|
||||
@LazyProperty
|
||||
def get_code(self):
|
||||
"""
|
||||
获取code
|
||||
:return:
|
||||
"""
|
||||
post_data = dict()
|
||||
sys_type = self.host.split(".")
|
||||
if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
post_data['client_id'] = "vispark-crm"
|
||||
post_data['response_type'] = 'code'
|
||||
post_data['redirect_uri'] = self.crm_vispark_url
|
||||
authorization = self.cc_auth
|
||||
# elif sys_type == "smm":
|
||||
# post_data['client_id'] = "sms-manage"
|
||||
# post_data['response_type'] = 'code'
|
||||
# post_data['redirect_uri'] = self.sms_url
|
||||
if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
api_url = self.get_code_url
|
||||
elif 'hulk-content-audit-server' in sys_type or 'hulk-content-manage-gateway' in sys_type or 'hulk-class-help-manager' in sys_type or 'hulk-ark-gateway' in sys_type or 'manage-api' in sys_type:
|
||||
api_url = self.get_all_school_code_url
|
||||
else:
|
||||
api_url = self.get_code_url
|
||||
req_session = self.get_session
|
||||
if 'mq-console' in sys_type:
|
||||
allow_redirects = True
|
||||
else:
|
||||
allow_redirects = False
|
||||
obj_log.info(api_url)
|
||||
obj_log.info(post_data)
|
||||
resp = req_session.get(
|
||||
url=api_url,
|
||||
params=post_data,
|
||||
allow_redirects=allow_redirects,
|
||||
verify=False)
|
||||
if 'mq-console' in sys_type:
|
||||
return [req_session, 'MQ']
|
||||
code_temp = resp.headers
|
||||
obj_log.info(code_temp)
|
||||
code = code_temp.get('location')
|
||||
obj_log.info(code)
|
||||
if 'client_id=gmp' in code:
|
||||
return code
|
||||
try:
|
||||
code = code.split("=")[-1]
|
||||
except Exception:
|
||||
raise IndexError("获取token失败,请检查环境或者登录信息是否正确!")
|
||||
resp_list = [code, post_data['redirect_uri'], authorization]
|
||||
return resp_list
|
||||
|
||||
# @LazyProperty
|
||||
def get_token(self, type_name=None):
|
||||
"""
|
||||
获取token
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
token = token_dict[self.host].get(self.curt_user, None)
|
||||
except KeyError as e:
|
||||
token = False
|
||||
if token:
|
||||
return token
|
||||
else:
|
||||
token_dict_temp = dict()
|
||||
sys_type = self.host.split(".")
|
||||
|
||||
post_data = dict()
|
||||
temp_code = self.get_code
|
||||
if isinstance(temp_code, str):
|
||||
session = self.get_session
|
||||
resp = session.get(url=temp_code)
|
||||
for key, value in session.cookies.items():
|
||||
if key == 'peppa_sso_token':
|
||||
token = value
|
||||
break
|
||||
token_header = {"accesstoken": token}
|
||||
session.headers.update(token_header)
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
session.headers.update(podenv)
|
||||
token_dict_temp[self.curt_user] = session
|
||||
token_dict[self.host] = token_dict_temp
|
||||
return session
|
||||
|
||||
if temp_code[1] == 'MQ': # MQ特殊处理
|
||||
return temp_code[0]
|
||||
post_data['code'] = temp_code[0]
|
||||
post_data['redirect_uri'] = temp_code[1]
|
||||
post_data['grant_type'] = 'authorization_code'
|
||||
req_session = self.get_session
|
||||
authorization = temp_code[2]
|
||||
if authorization:
|
||||
header = {'authorization': authorization}
|
||||
if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
resp = req_session.get(
|
||||
url=api_url+'&code='+temp_code[0], data=post_data, headers=header, allow_redirects=False, verify=False)
|
||||
elif 'visparklearning' in sys_type and 'manage-gw' in sys_type:
|
||||
resp = req_session.get(url=api_url + '&code=' + temp_code[0], data=post_data, headers=header,
|
||||
allow_redirects=False, verify=False)
|
||||
else:
|
||||
resp = req_session.post(
|
||||
url=api_url, data=post_data, headers=header, allow_redirects=False, verify=False)
|
||||
if 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
token_temp = parse.parse_qs(parse.urlparse(resp.headers['location']).query)
|
||||
elif 'visparklearning' in sys_type and 'manage-gw' in sys_type:
|
||||
token_temp = parse.parse_qs(parse.urlparse(resp.headers['location']).query)
|
||||
else:
|
||||
token_temp = resp.json()
|
||||
if type_name == 'BFF': # bff架构使用
|
||||
token = {"token": token_temp['access_token']}
|
||||
elif 'visparklearning' in sys_type and ('cc-manage' or 'cti-manage' in sys_type):
|
||||
token = {"accesstoken": token_temp['peppa_sso_token'][0]}
|
||||
elif 'visparklearning' in sys_type and 'manage-gw' in sys_type:
|
||||
token = {"accesstoken": token_temp['peppa_sso_token'][0]}
|
||||
else:
|
||||
if "access_token" not in token_temp:
|
||||
obj_log.warning("---------获取token失败!-------")
|
||||
obj_log.warning("type_name:{}\ntoken_temp:{}".format(type_name, token_temp))
|
||||
obj_log.warning("-----------------------------")
|
||||
token = {"accesstoken": token_temp['access_token']}
|
||||
req_session.headers.update(token)
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
req_session.headers.update(podenv)
|
||||
token_dict_temp[self.curt_user] = req_session
|
||||
token_dict[self.host] = token_dict_temp
|
||||
return req_session
|
||||
else:
|
||||
if 'kunpeng' in post_data['redirect_uri']: # 鲲鹏/mp系统登录
|
||||
req_session.get(url=post_data['redirect_uri'] + '&code=' + temp_code[0], verify=False)
|
||||
return req_session
|
||||
elif 'allschool-mp' in post_data['redirect_uri']:
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
req_session.headers.update(podenv)
|
||||
req_session.get(url=post_data['redirect_uri'] + '&code=' + temp_code[0], verify=False)
|
||||
access_token = req_session.cookies.get("peppa_sso_token")
|
||||
req_session.headers.update({"accesstoken": access_token})
|
||||
return req_session
|
||||
run_session = self.sso_oauth_login(session=req_session, code=temp_code[0])
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
run_session.headers.update(podenv)
|
||||
token_dict_temp[self.curt_user] = run_session
|
||||
token_dict[self.host] = token_dict_temp
|
||||
return run_session
|
||||
|
||||
def sso_oauth_login(self, type='manage', session=None, code=None):
|
||||
"""
|
||||
| 功能说明: | |
|
||||
| 输入参数: | | |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
举例说明:
|
||||
| sso_oauth_login | |
|
||||
"""
|
||||
if type == 'manage':
|
||||
url = self.manage_url + '?code=' + code
|
||||
elif type == 'opengalaxy': # 独立环境发布系统
|
||||
url = self.opengalaxy_url + '?code=' + code
|
||||
session.get(url=url, allow_redirects=True, verify=False)
|
||||
return session
|
||||
|
||||
def ug_hhi_sso_login_by_session_id(self):
|
||||
"""
|
||||
ug的manage后台登录获取sessionId
|
||||
:return:
|
||||
"""
|
||||
req_session = requests.session()
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
# sso登录
|
||||
url_form = self.curt_sso_url
|
||||
obj_log.info(url_form)
|
||||
post_data = dict()
|
||||
post_data['showUsername'] = self.show_username
|
||||
post_data['username'] = self.username
|
||||
post_data['password'] = self.password
|
||||
res_form = req_session.post(url=url_form, data=post_data, allow_redirects=False, verify=False)
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_url_form:{}".format(url_form))
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_res_form:{}".format(res_form.headers))
|
||||
|
||||
|
||||
# 进行sso.qa.sparkedu.com/oauth/authorize
|
||||
url_authorize = "{}?client_id=manage&response_type=code&redirect_uri={}".format(
|
||||
self.get_code_url, self.manage_url)
|
||||
res_authorize = req_session.get(url_authorize, allow_redirects=False)
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_url_authorize:{}".format(url_authorize))
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_res_authorize:{}".format(res_authorize.headers))
|
||||
|
||||
# 获取到res_authorize的location地址(主要是用到code)
|
||||
url_sso_oauth_login = res_authorize.headers.get('location')
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_url_sso_oauth_login:{}".format(url_sso_oauth_login))
|
||||
res_url_sso_oauth_login = req_session.get(url_sso_oauth_login, allow_redirects=False)
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_res_url_sso_oauth_login:{}".format(res_url_sso_oauth_login.headers))
|
||||
|
||||
# 因为环境问题所以多请求一次域名
|
||||
res_manage_host = req_session.get(self.manage_host)
|
||||
obj_log.info("ug_hhi_sso_login_by_session_id_url_manage:{}".format(self.manage_host))
|
||||
obj_log.info(
|
||||
"ug_hhi_sso_login_by_session_id_res_manage_host:{}".format(res_manage_host.headers))
|
||||
|
||||
token_dict_temp = dict()
|
||||
token_dict_temp[self.curt_user] = req_session
|
||||
token_dict[self.host] = token_dict_temp
|
||||
|
||||
return req_session
|
||||
|
||||
def ug_sso_login_by_starlight(self):
|
||||
"""
|
||||
ug的starlight.qa.huohua.cn登录
|
||||
:return:
|
||||
"""
|
||||
req_session = requests.session()
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
# 访问starlight.qa.huohua.cn/生成JSESSION_STARLIGHT
|
||||
starlight_url = self.starlight_url
|
||||
obj_log.info(starlight_url)
|
||||
req_session.get(starlight_url, allow_redirects=False)
|
||||
|
||||
# sso登录
|
||||
url_form = self.curt_sso_url
|
||||
obj_log.info(url_form)
|
||||
post_data = dict()
|
||||
post_data['showUsername'] = self.show_username
|
||||
post_data['username'] = self.username
|
||||
post_data['password'] = self.password
|
||||
res_form = req_session.post(url=url_form, data=post_data, allow_redirects=False, verify=False)
|
||||
obj_log.info(f'res_form的header:{res_form.headers}')
|
||||
|
||||
# 进行sso.qa.sparkedu.com/oauth/authorize
|
||||
url_authorize = "{}?client_id=starlight&response_type=code&redirect_uri={}".format(
|
||||
self.get_code_url, self.suyang_manage_sso_login_url)
|
||||
res_authorize = req_session.get(url_authorize, allow_redirects=False)
|
||||
obj_log.info(f"res_authorize的header:{res_authorize.headers.get('location')}")
|
||||
|
||||
# 获取到res_authorize的location地址(主要是用到code)
|
||||
req_session.get(res_authorize.headers.get('location'), allow_redirects=False)
|
||||
|
||||
# 访问https://starlight.qa.huohua.cn/
|
||||
req_session.get(self.starlight_url, allow_redirects=False)
|
||||
|
||||
token_dict_temp = dict()
|
||||
token_dict_temp[self.curt_user] = req_session
|
||||
token_dict[self.host] = token_dict_temp
|
||||
return req_session
|
||||
|
||||
class SparkleLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=None, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=None, current_evn=current_evn)
|
||||
self.pc_token = None
|
||||
self.session = requests.session()
|
||||
# self.jira_id = get_cfg.get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
# def sparkle_pc_login(self, phone, passwd=123456):
|
||||
def sparkle_pc_login(self, phone, passwd='Mima@123'):
|
||||
if token_dict.get(str(phone)):
|
||||
return token_dict.get(str(phone))
|
||||
url = self.sparkle_pc_token_url
|
||||
data = {"username": phone, "userType": 2, "password": str(passwd)}
|
||||
self.session.headers.update({"Authorization": self.spark_pc_auth})
|
||||
resp = json.loads(self.session.post(url, params=data).content)
|
||||
header = {"account-token": resp.get("data").get("accessToken")}
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
self.session.headers.update(podenv)
|
||||
self.session.headers.update(header)
|
||||
token_dict[str(phone)] = self.session
|
||||
return self.session
|
||||
|
||||
|
||||
class AgentApiLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=None, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=None, current_evn=current_evn)
|
||||
self.agent_api_token = None
|
||||
self.session = requests.session()
|
||||
# self.jira_id = get_cfg.get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def agent_api_login(self, phone, authCode, countryCode=86):
|
||||
if token_dict.get(str(phone)):
|
||||
return token_dict.get(str(phone))
|
||||
url = '{}/login/loginByAuthCode'.format(self.hhr_api_host)
|
||||
data = {"phone": str(phone), "authCode": str(authCode), "countryCode": str(countryCode)}
|
||||
resp = json.loads(self.session.post(url, json=data).content)
|
||||
header = {"user-token": resp.get("data").get("token")}
|
||||
if self.jira_id:
|
||||
podenv = {"huohua-podenv": self.jira_id}
|
||||
self.session.headers.update(podenv)
|
||||
self.session.headers.update(header)
|
||||
token_dict[str(phone)] = self.session
|
||||
return self.session
|
||||
|
||||
class MarketApiLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
InitConfig.__init__(self, run_user_name=None, current_evn=current_evn)
|
||||
self.session = requests.session()
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def market_api_login(self, user=None):
|
||||
login_url = self.market_url
|
||||
result = parse.urlparse(url=login_url ,allow_fragments=True)
|
||||
host = result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
if host in choose_user:
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name' ,options=host)
|
||||
self.current_user = default_user
|
||||
if user:
|
||||
self.current_user = user
|
||||
try:
|
||||
login_user = ReadConfig().get_value(sections=self.current_user ,options='username')
|
||||
login_password = ReadConfig().get_value(sections=self.current_user ,options='password')
|
||||
except Exception as e:
|
||||
login_user = ReadConfig(astwb_config).get_value(sections=self.current_user ,options='username')
|
||||
login_password = ReadConfig(astwb_config).get_value(sections=self.current_user ,options='password')
|
||||
|
||||
post_data = {'phone': login_user ,'password': login_password}
|
||||
resp = self.session.post(url=login_url ,json=post_data , verify=False,
|
||||
headers={'huohua-podenv': self.jira_id})
|
||||
|
||||
if resp.status_code != 200:
|
||||
raise KeyError('获取 市场管理投放系统 token失败!')
|
||||
set_cookie = resp.headers['set-cookie'].split(";")[-1].split(",")[1]
|
||||
headers = {"Cookie":"gray_id=3077c7810744b47fc06ec513cf07801e; gray_tag=stable; "+set_cookie}
|
||||
self.session.headers.update(headers)
|
||||
return self.session
|
||||
|
||||
|
||||
|
||||
class AllSchool(InitConfig):
|
||||
def __init__(self, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def all_school_token(self, user=None, login_type=1):
|
||||
"""
|
||||
:param user: 登录用户
|
||||
:param login_type: 登录系统 1:教师工作台
|
||||
2:机构工作台
|
||||
3:选课平台
|
||||
:return: token
|
||||
"""
|
||||
if login_type == 1:
|
||||
login_url = self.all_school_url
|
||||
elif login_type == 2:
|
||||
login_url = self.all_school_org_url
|
||||
elif login_type == 3:
|
||||
login_url = self.find_classes_url
|
||||
result = parse.urlparse(url=login_url, allow_fragments=True)
|
||||
host = result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
if host in choose_user:
|
||||
if login_type == 2:
|
||||
host = 'org-' + host
|
||||
elif login_type == 3:
|
||||
host = 'find-' + host
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name', options=host)
|
||||
self.current_user = default_user
|
||||
if user:
|
||||
self.current_user = user
|
||||
try:
|
||||
login_user = ReadConfig().get_value(sections=self.current_user, options='username')
|
||||
login_password = ReadConfig().get_value(sections=self.current_user, options='password')
|
||||
except Exception as e:
|
||||
login_user = ReadConfig(astwb_config).get_value(sections=self.current_user, options='username')
|
||||
login_password = ReadConfig(astwb_config).get_value(sections=self.current_user, options='password')
|
||||
if int(login_type) == 3:
|
||||
post_data = {"data": {"email": login_user, "password": login_password, "countryCode": "86",
|
||||
"loginType": "EMAIL_PSW"}}
|
||||
else:
|
||||
post_data = {'email': login_user, 'password': login_password}
|
||||
as_token = requests.post(url=login_url, json=post_data, verify=False,
|
||||
headers={'huohua-podenv': self.jira_id})
|
||||
rtn_temp = as_token.json()
|
||||
if as_token.status_code != 200:
|
||||
raise KeyError('获取 allSchool token失败!')
|
||||
user_token = rtn_temp['data']['token']
|
||||
return user_token
|
||||
|
||||
|
||||
class ParentLogin(InitConfig):
|
||||
def __init__(self,host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
@retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def parent_send_msg(self, phone, authType=2, countryCode=None,
|
||||
sessionId=None, **kwargs):
|
||||
"""
|
||||
| 功能说明: | M站(学生端)发送短信 |
|
||||
| 输入参数: | phone | 手机号 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
举例说明:
|
||||
| m_send_msg | |
|
||||
"""
|
||||
url = self.m_host + '/passport/auth_code/send'
|
||||
post_data = {
|
||||
"phone": phone,
|
||||
"authType": authType,
|
||||
"countryCode": countryCode,
|
||||
"isLogin": False,
|
||||
"sessionId": sessionId}
|
||||
resp = requests.post(
|
||||
url=url,
|
||||
json=post_data,
|
||||
verify=False)
|
||||
time.sleep(2)
|
||||
return resp
|
||||
|
||||
def get_parent_sms_code(self, phone):
|
||||
"""
|
||||
M站获取手机验证码
|
||||
:param phone:电话号码
|
||||
:return: token
|
||||
"""
|
||||
auth_url = self.curt_sso_url
|
||||
get_code_url = self.get_code_url
|
||||
token_url = self.get_token_url
|
||||
sms_url = "https://smm.qa.huohua.cn/sms/log/page/all?WHERE.%5BEQ%5Dphone={}&WHERE.%5BEQ%5DtypeName=&WHERE.%5BEQ%5Dchannel=&WHERE.%5BEQ%5DgatewayType=&WHERE.%5BGT%5DcreateTime=&WHERE.%5BLT%5DcreateTime=&WHERE.%5BGT%5DsubmitTime=&WHERE.%5BLT%5DsubmitTime=&pageNum=1&pageSize=20&orderBy=id%20desc".format(
|
||||
phone)
|
||||
session = requests.session()
|
||||
session.post(url=auth_url, verify=False,
|
||||
data={'showUsername': 'liuruiquan', 'username': 'liuruiquan@huohua.cn', 'password': 'lrq5823LRQ'})
|
||||
get_code = session.get(url=get_code_url, verify=False,
|
||||
params={'client_id': 'crmnew', 'response_type': 'code',
|
||||
'redirect_uri': 'https://crmv2.qa.huohua.cn'}, allow_redirects=False)
|
||||
code_temp = get_code.headers
|
||||
code = code_temp['location']
|
||||
code = code.split("=")[-1]
|
||||
token_get = session.post(url=token_url, verify=False, data={'code': code, 'redirect_uri': 'https://crmv2.qa.huohua.cn',
|
||||
'grant_type': 'authorization_code'},
|
||||
headers={'authorization': 'Basic Y3JtbmV3OmNybW5ldw=='})
|
||||
token_temp = token_get.json()
|
||||
token = {"accesstoken": token_temp['access_token']}
|
||||
sms_temp = session.post(url=sms_url, verify=False, headers=token).json()
|
||||
smm_code = sms_temp['list'][0]['msg']
|
||||
reg = re.compile(r"(?<=手机短信验证码:)\d+")
|
||||
match = reg.search(smm_code)
|
||||
smm_code = match.group(0)
|
||||
return smm_code
|
||||
|
||||
def get_parent_token(self, phone=None):
|
||||
"""
|
||||
M站token获取
|
||||
:param phone:电话号码
|
||||
:return: token
|
||||
"""
|
||||
login_url = "http://m.qa.huohua.cn/passport/login"
|
||||
result = parse.urlparse(url=login_url, allow_fragments=True)
|
||||
host = 'parent-' + result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
if host in choose_user:
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name', options=host)
|
||||
if phone:
|
||||
user = ReadConfig().get_value(sections=phone, options='username')
|
||||
self.current_user = user
|
||||
else:
|
||||
phone = ReadConfig().get_value(
|
||||
sections=default_user, options='username')
|
||||
self.current_user = phone
|
||||
if student_token_cache.get(self.current_user):
|
||||
return student_token_cache[self.current_user]
|
||||
send_code = self.parent_send_msg(phone=self.current_user).json()
|
||||
# if not send_code['success']:
|
||||
# raise ValueError(send_code)
|
||||
verify_code = self.get_parent_sms_code(phone=self.current_user)
|
||||
post_data = {"phone": self.current_user, "authCode": verify_code, "countryCode": None, "loginType": 1,
|
||||
"userType": 1,
|
||||
"subjectType": 0}
|
||||
m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id})
|
||||
if m_token.status_code == 200:
|
||||
token = m_token.json()
|
||||
token = token['data']['token']
|
||||
student_token_cache[self.current_user] = token
|
||||
return token
|
||||
else:
|
||||
raise KeyError('获取 parent token失败!')
|
||||
|
||||
def get_parent_token_by_pwd(self, phone=None):
|
||||
env_name = get_current_config(section='run_evn_name', key='current_business')
|
||||
obj_log.info("env_name:{}".format(env_name))
|
||||
if env_name and env_name=='hhi':
|
||||
login_url = "https://hhi-user-auth-api.qa.visparklearning.com/login"
|
||||
elif 'hhi' in phone.split('-'):
|
||||
login_url = "https://pst-gw.qa.huohua.cn/passport/login"
|
||||
else:
|
||||
login_url = "http://m.qa.huohua.cn/passport/login"
|
||||
result = parse.urlparse(url=login_url, allow_fragments=True)
|
||||
host = 'parent-' + result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
obj_log.info("host:{},choose_user:{}".format(host, choose_user))
|
||||
if host in choose_user:
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name', options=host)
|
||||
if phone.split('-')[1]:
|
||||
self.current_user = phone.split('-')[1]
|
||||
else:
|
||||
phone = ReadConfig(config_file_path).get_value(
|
||||
sections=default_user, options='username')
|
||||
self.current_user = phone
|
||||
else:
|
||||
phone = ReadConfig(config_file_path).get_value(
|
||||
sections='lzp', options='username')
|
||||
self.current_user = phone
|
||||
if student_token_cache.get(self.current_user):
|
||||
return student_token_cache[self.current_user]
|
||||
# 支持传入海外区号用户
|
||||
if '+' in self.current_user :
|
||||
phone_list =self.current_user.split("+")
|
||||
countryCode = phone_list[1]
|
||||
self.current_user = phone_list[0]
|
||||
else:
|
||||
countryCode = 86
|
||||
post_data = {"phone": self.current_user, "password": "A123456", "countryCode": countryCode, "loginType": 2,
|
||||
"userType": 1,
|
||||
"subjectType": 0}
|
||||
|
||||
try:
|
||||
m_token = requests.post(url=login_url, json=post_data, verify=False,
|
||||
headers={'huohua-podenv': self.jira_id})
|
||||
if m_token.status_code != 200:
|
||||
raise KeyError('获取 parent token失败!')
|
||||
token = m_token.json()
|
||||
token = token['data']['token']
|
||||
student_token_cache[self.current_user] = token
|
||||
return token
|
||||
except :
|
||||
raise TypeError('获取 parent token失败!')
|
||||
|
||||
def get_xdu_parent_token_by_pwd(self, phone=None, host=None):
|
||||
env_name = get_current_config(section='run_evn_name', key='current_business')
|
||||
obj_log.info("env_name:{}".format(env_name))
|
||||
login_url = 'http://'+host+"/app/sign/phone"
|
||||
result = parse.urlparse(url=login_url, allow_fragments=True)
|
||||
host = result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
obj_log.info("host:{},choose_user:{}".format(host, choose_user))
|
||||
if host in choose_user:
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name', options=host)
|
||||
post_data = {"phone": phone, "verifyCode": 1111, "countryCode": 86, "type": 1}
|
||||
try:
|
||||
m_token = requests.post(url=login_url, json=post_data, verify=False,
|
||||
headers={'huohua-podenv': self.jira_id})
|
||||
if m_token.status_code != 200:
|
||||
raise KeyError('获取 parent token失败!')
|
||||
token = m_token.json()
|
||||
token = token['data']['token']
|
||||
student_token_cache[self.current_user] = token
|
||||
return token
|
||||
except :
|
||||
raise TypeError('获取 parent token失败!')
|
||||
|
||||
|
||||
class StudentLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def get_student_token_by_pwd(self, phone=None):
|
||||
env_name = get_current_config(section='run_evn_name', key='current_business')
|
||||
if env_name and env_name=='hhi':
|
||||
login_url = "https://hhi-core-api.qa.visparklearning.com/token"
|
||||
else:
|
||||
login_url = "https://core-api.qa.huohua.cn/token"
|
||||
result = parse.urlparse(url=login_url, allow_fragments=True)
|
||||
host = result.hostname
|
||||
choose_user = ReadConfig(env_choose_path).get_options('run_user_name')
|
||||
if host in choose_user:
|
||||
default_user = ReadConfig(env_choose_path).get_value(
|
||||
sections='run_user_name', options=host)
|
||||
if phone:
|
||||
self.current_user = phone
|
||||
else:
|
||||
phone = ReadConfig(config_file_path).get_value(
|
||||
sections=default_user, options='username')
|
||||
self.current_user = phone
|
||||
else:
|
||||
phone = ReadConfig(config_file_path).get_value(
|
||||
sections='lzp', options='username')
|
||||
self.current_user = phone
|
||||
if student_token_cache.get(self.current_user):
|
||||
return student_token_cache[self.current_user]
|
||||
# 支持传入海外区号用户
|
||||
if '+' in self.current_user :
|
||||
phone_list =self.current_user.split("+")
|
||||
countryCode = phone_list[1]
|
||||
self.current_user = phone_list[0]
|
||||
else:
|
||||
countryCode = 86
|
||||
post_data = {"phone": self.current_user, "password": "A123456", "countryCode": countryCode, "authCodeType":2,"loginType": 2}
|
||||
try:
|
||||
m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id})
|
||||
|
||||
if m_token.status_code != 200:
|
||||
raise KeyError('获取 student token失败!')
|
||||
token = m_token.json()
|
||||
token = token['data']['token']
|
||||
student_token_cache[self.current_user] = token
|
||||
return token
|
||||
except :
|
||||
raise TypeError('获取 student token失败!')
|
||||
|
||||
|
||||
class SparkEduLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
team_name = ReadConfig(env_choose_path).get_value(sections='run_evn_name', options="current_team")
|
||||
|
||||
def get_spark_edu_graphic_code(self):
|
||||
"""
|
||||
功能:获取图形验证码
|
||||
"""
|
||||
code_url = "https://api.qa.sparkedu.com/third/passport/captcha"
|
||||
post_data = {"width":240,"height":90}
|
||||
resp = requests.post(url=code_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id})
|
||||
resp = resp.json()
|
||||
return resp['data']['captchaId']
|
||||
|
||||
def check_spark_edu_graphic_code(self, captcha_id, captcha='1234',):
|
||||
"""
|
||||
功能:校验图形验证码
|
||||
captcha_id:图形验证码id,由get_spark_edu_graphic_code返回
|
||||
captcha:图形验证码,默认1234,需事先配置好
|
||||
"""
|
||||
check_url = "https://api.qa.sparkedu.com/third/passport/captcha/check"
|
||||
post_data = {"captchaId":captcha_id,"captcha":"1234"}
|
||||
resp = requests.post(url=check_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id})
|
||||
resp = resp.json()
|
||||
print(resp)
|
||||
|
||||
def spark_edu_login_by_code(self, phone=None, auth_code=None,
|
||||
auth_code_type=17, country_code=86, language="zh-HK", platform="1",
|
||||
login_type=1, user_type=1):
|
||||
"""
|
||||
功能:验证码登录
|
||||
phone: 登录用户手机号
|
||||
auth_code:验证码,这里默认验证码为1234,需提前配置
|
||||
其余参数:默认
|
||||
"""
|
||||
login_url = "https://api.qa.sparkedu.com/third/passport/login"
|
||||
if not phone:
|
||||
# 如果没有传入phone,则从小组配置文件中读取
|
||||
team_config = os.path.abspath(os.path.join(ROOT_PATH, 'UBJ/library/Config/team_config.ini'))
|
||||
phone = ReadConfig(team_config).get_value(sections='default_spark_edu_user', options='phone')
|
||||
if not auth_code:
|
||||
# 如果没有传入验证码,则从小组配置文件中读取
|
||||
team_config = os.path.abspath(os.path.join(ROOT_PATH, 'UBJ/library/Config/team_config.ini'))
|
||||
auth_code = ReadConfig(team_config).get_value(sections='default_spark_edu_user', options='auth_code')
|
||||
post_data = {"phone":phone,
|
||||
"authCode":auth_code,
|
||||
"countryCode":country_code,
|
||||
"authCodeType":auth_code_type,
|
||||
"language":language,
|
||||
"platform":platform,
|
||||
"loginType":login_type,
|
||||
"userType":user_type}
|
||||
m_token = requests.post(url=login_url, json=post_data, verify=False, headers={'huohua-podenv': self.jira_id})
|
||||
if m_token.status_code == 200:
|
||||
token = m_token.json()
|
||||
return token['data']['token']
|
||||
else:
|
||||
print(m_token.content)
|
||||
raise KeyError('获取 student token失败!')
|
||||
|
||||
|
||||
class SparkSaasLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini'))
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def login_biz_saas(self, phone="", email="", password="", country_code="", auth_code="9999"):
|
||||
"""
|
||||
Args:
|
||||
phone:
|
||||
email:
|
||||
password: 密码,
|
||||
country_code: 86, config.country_code - 目前有问题,暂时写到86
|
||||
auth_code: 验证码
|
||||
Returns:
|
||||
"""
|
||||
url = "https://sc-saas-api.qa.huohua.cn/api/session/integration/login"
|
||||
team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini'))
|
||||
a = ReadConfig(team_config).get_value(sections='saas_org_info', options='SAAS_BELONG_SCHOOL')
|
||||
country_code = ReadConfig(team_config).get_value(sections='saas_org_info', options='COUNTRY_CODE')
|
||||
data = {
|
||||
}
|
||||
if phone and password == "":
|
||||
data["authCode"] = auth_code
|
||||
data["loginType"] = 2 # 登录类型
|
||||
data["phone"] = str(phone)
|
||||
data["countryCode"] = country_code
|
||||
elif phone and password:
|
||||
data["password"] = password
|
||||
data["loginType"] = 1 # 登录类型
|
||||
data["phone"] = str(phone)
|
||||
data["countryCode"] = country_code
|
||||
elif email and password:
|
||||
data["password"] = password
|
||||
data["loginType"] = 3 # 登录类型
|
||||
data["email"] = email
|
||||
elif phone == "" and password == "":
|
||||
phone = ReadConfig(team_config).get_value(sections='saas_org_info', options='SAAS_NAME')
|
||||
data["authCode"] = auth_code
|
||||
data["loginType"] = 2 # 登录类型
|
||||
data["phone"] = str(phone)
|
||||
data["countryCode"] = country_code
|
||||
print(str(country_code),str(phone),str(auth_code))
|
||||
else:
|
||||
print("登录方式错误...")
|
||||
return False
|
||||
|
||||
print("开始登录 saas 系统..., 打印登录信息: {}".format(phone))
|
||||
response = requests.post(url=url, json=data, verify=False, headers={'huohua-podenv': self.jira_id, "Content-Type": "application/json;charset=UTF-8"})
|
||||
if response:
|
||||
pass
|
||||
else:
|
||||
print("登录 SaaS 系统失败...{}".format(response))
|
||||
return False
|
||||
print(response.text)
|
||||
|
||||
token = response.json()['data']['token']
|
||||
status = response.status_code
|
||||
if status:
|
||||
if status == 200 and token:
|
||||
print("登录 SaaS 系统成功..., Token: {}".format(token[0]))
|
||||
print("写入到config配置文件...")
|
||||
return token
|
||||
else:
|
||||
print("登录 SaaS 系统失败...{}".format(response))
|
||||
return False
|
||||
else:
|
||||
print("登录 SaaS 系统失败...{}".format(response))
|
||||
return False
|
||||
|
||||
class SparkSaasTeacherLogin(InitConfig):
|
||||
def __init__(self, host=None, curt_user=None, current_evn=None):
|
||||
# super().__init__(run_user_name=curt_user, current_evn=current_evn)
|
||||
InitConfig.__init__(self, run_user_name=curt_user, current_evn=current_evn)
|
||||
self.team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini'))
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
|
||||
def rsa_encrypt(self, text):
|
||||
"""
|
||||
Args:
|
||||
text:
|
||||
Returns:
|
||||
"""
|
||||
public_key = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGsNh8Pu1zY9SrblsJDuITHQ+ObAyjGbQARgh3TQtaz5vYQ+avTZJJsJLxiZ5hpf1/5OCb3QZRzbWebrk9cgU892GGc8MDBtH7l/I1GrZq2mY9LO4wWjA0gC7qK0o3yqAwwNyh3uXkSsN6KlpXnac1G1o+Sjlr1P1IXoB4I5MlAQIDAQAB"
|
||||
public_key_str = "-----BEGIN PUBLIC KEY-----\n{}\n-----END PUBLIC KEY-----".format(public_key)
|
||||
|
||||
# 字符串指定编码(转为bytes)
|
||||
text = text.encode('utf-8')
|
||||
# 构建公钥对象
|
||||
cipher_public = PKCS1_v1_5.new(RSA.importKey(public_key_str))
|
||||
# 加密(bytes)
|
||||
text_encrypted = cipher_public.encrypt(text)
|
||||
# base64编码,并转为字符串
|
||||
text_encrypted_base64 = base64.b64encode(text_encrypted).decode()
|
||||
return text_encrypted_base64
|
||||
|
||||
def spark_login_teacher(self, phone, auth_code='9999', auth_code_type=2, country_code="", pass_word="abc123", login_type="验证码"):
|
||||
"""
|
||||
Args:
|
||||
phone:
|
||||
auth_code:
|
||||
auth_code_type:
|
||||
country_code:
|
||||
login_type: 验证码,密码
|
||||
pass_word: 密码
|
||||
Returns: token
|
||||
"""
|
||||
team_config = os.path.abspath(os.path.join(ROOT_PATH, 'BIZ/library/Config/team_config.ini'))
|
||||
country_code = ReadConfig(team_config).get_value(sections='saas_org_info', options='TEACHER_COUNTRY_CODE')
|
||||
data = {
|
||||
"authCodeType": auth_code_type,
|
||||
"countryCode": country_code
|
||||
}
|
||||
|
||||
phone_encrypt = SparkSaasTeacherLogin().rsa_encrypt(str(phone))
|
||||
data["phone"] = phone_encrypt
|
||||
|
||||
if login_type == "验证码":
|
||||
print("教师端验证码登录")
|
||||
url = "https://sc-mce-teacher-api.qa.huohua.cn/api/session"
|
||||
data["authCode"] = auth_code
|
||||
print(data)
|
||||
|
||||
else:
|
||||
print("教师端密码登录")
|
||||
url = "https://sc-mce-teacher-api.qa.huohua.cn/api/session/phone_pass"
|
||||
pass_word_encrypt = SparkSaasTeacherLogin().rsa_encrypt(pass_word)
|
||||
data["password"] = pass_word
|
||||
|
||||
print("开始登录 教师端 系统..., 打印登录手机号码, {}".format(phone))
|
||||
# response = self.request.http_request(url, method='post', json=data)
|
||||
response = requests.post(url=url, json=data, verify=False, headers={'huohua-podenv': self.jira_id, "Content-Type": "application/json;charset=UTF-8"})
|
||||
if response:
|
||||
pass
|
||||
else:
|
||||
print("登录 教师端 系统失败...{}".format(response))
|
||||
return False
|
||||
token = response.json()['data']['token']
|
||||
status = response.status_code
|
||||
if status:
|
||||
if status and token:
|
||||
print("登录 教师端 系统成功..., Token: {}".format(token))
|
||||
print("写入到config配置文件...")
|
||||
return token
|
||||
else:
|
||||
print("登录 教师端 系统失败...{}".format(response))
|
||||
return False
|
||||
else:
|
||||
print("登录 教师端 系统失败...{}".format(response))
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
test = LoginSys()
|
||||
id = test.ug_sso_login_by_starlight()
|
||||
print(id)
|
||||
headers = {"Content-Type": "application/json;charset=UTF-8"}
|
||||
kwargs = {"lessonQueryModel":{},"pageModel":{"pageNum":1,"pageSize":10}}
|
||||
url = "https://starlight.qa.huohua.cn/api/lesson/queryList"
|
||||
resp = id.post(url=url, json=kwargs, headers=headers, allow_redirects=False, verify=False)
|
||||
print(resp)
|
||||
405
base_framework/public_tools/huohua_dbs.py
Normal file
405
base_framework/public_tools/huohua_dbs.py
Normal file
@@ -0,0 +1,405 @@
|
||||
# encoding: utf-8
|
||||
# 维护人员:qiaoxinjiu
|
||||
import re
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
import urllib3
|
||||
from base_framework.public_tools.read_config import get_current_env, ReadConfig
|
||||
|
||||
urllib3.disable_warnings()
|
||||
|
||||
|
||||
class HuoHuaDBS:
|
||||
def __init__(self, user_name=None, pwd=None):
|
||||
self.conf = ReadConfig()
|
||||
# if not user_name:
|
||||
# user_name = self.conf.get_value(sections="lrq", options="show_username")
|
||||
# pwd = self.conf.get_value(sections="lrq", options="password")
|
||||
self.req = requests.session()
|
||||
self.user_name = user_name
|
||||
self.user_pwd = pwd
|
||||
self.headers = {}
|
||||
self.session_id = None
|
||||
self.authenticate_csrf_token = None
|
||||
self.query_url = "https://dbs.qc.huohua.cn/query/"
|
||||
self.check_url = "https://dbs.qc.huohua.cn/simplecheck/"
|
||||
self.commit_url = "https://dbs.qc.huohua.cn/autoreview/"
|
||||
self.execute_url = "https://dbs.qc.huohua.cn/execute/"
|
||||
self.get_status_url = "https://dbs.qc.huohua.cn/sqlworkflow/detail_content/?workflow_id="
|
||||
self.env = get_current_env().lower()
|
||||
if self.env == "sim": # sim环境读取一次db配置, 方便后续场景中直接使用,避免重复查询
|
||||
self.sim_db = {} # 最终格式: {"teach_teacher": "sim126", "teach_classes": "sim128"}
|
||||
instance_list = self.conf.get_options(section="DB_LIST")
|
||||
for instance in instance_list:
|
||||
db_list = self.conf.get_value(sections="DB_LIST", options=instance)
|
||||
dbs = db_list.split(',')
|
||||
for db in dbs:
|
||||
self.sim_db[db] = instance
|
||||
|
||||
def __get_instance_name(self, sql_content):
|
||||
if self.env.lower() == "qa" or "-" in self.env: # 如果是qa或独立环境,则默认查询qadb-slave
|
||||
if sql_content.split(' ')[0].lower() == "select":
|
||||
instance_name = "qadb-slave" # sql查询时,使用此名
|
||||
else:
|
||||
instance_name = "qadb-master" # sql执行时,使用此名
|
||||
else:
|
||||
# db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "")
|
||||
db_names = self.__get_db_names_from_sql(sql_statements=sql_content)
|
||||
instance_list = []
|
||||
no_config_list = []
|
||||
for db_name in db_names:
|
||||
if db_name not in self.sim_db:
|
||||
no_config_list.append(db_name)
|
||||
continue
|
||||
if self.sim_db[db_name] not in instance_list:
|
||||
instance_list.append(self.sim_db[db_name])
|
||||
if len(no_config_list) > 0:
|
||||
raise Exception("你本次查询的数据库:{} 未做sim配置,请添加....".format(no_config_list))
|
||||
if len(set(instance_list)) > 1:
|
||||
raise Exception("你本次查询的数据库:{}\n位于不同的实例:{}\n请修改sql....".format(db_names,instance_list))
|
||||
instance_name = instance_list[0]
|
||||
return instance_name
|
||||
|
||||
@staticmethod
|
||||
def __check_sql_content(sql_content):
|
||||
"""检查sql语句是否符合规范"""
|
||||
sql_list = sql_content.split(" ")
|
||||
if sql_list[0].lower() in ["insert", "update", "delete", "select"]:
|
||||
if "." not in sql_content:
|
||||
raise Exception("sql语句必现是db_name.table_name格式,请修正:{}".format(sql_content))
|
||||
|
||||
@staticmethod
|
||||
def __get_db_names_from_sql(sql_statements):
|
||||
"""
|
||||
提取sql语句中的数据库名称
|
||||
:param sql_statements: sql语句
|
||||
:return: 数据库名称列表,去重
|
||||
"""
|
||||
sql_statements = sql_statements.replace("`", "")
|
||||
pattern = re.compile(r'\b(?:FROM|INTO|UPDATE|JOIN)\s+([a-zA-Z0-9_]+)\.', re.IGNORECASE)
|
||||
|
||||
databases = set()
|
||||
# 移除注释和多余空格
|
||||
sql_clean = re.sub(r'--.*', '', sql_statements) # 移除行注释
|
||||
sql_clean = ' '.join(sql_clean.split()) # 合并多余空格
|
||||
# 查找所有匹配项
|
||||
matches = pattern.findall(sql_clean)
|
||||
if matches:
|
||||
databases.update(matches)
|
||||
return list(databases)
|
||||
|
||||
def dbs_login(self):
|
||||
login_index = 'http://dbs.qc.huohua.cn/login/'
|
||||
res_login = self.req.get(login_index)
|
||||
self.authenticate_csrf_token = res_login.cookies.get('csrftoken')
|
||||
authenticate_url = 'https://dbs.qc.huohua.cn/authenticate/'
|
||||
params_json_str = {"username": self.user_name, "password": self.user_pwd}
|
||||
self.headers.update({"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
"Cookie": "experimentation_subject_id=IjdmMmE5MmYxLTk4N2QtNDU1YS1hMGRjLTZhZWIwZWY3YWEyMiI%3D--fc2a7fdd42c874af494e99f3f5fdf0c372d3b4d2; __UUID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; __DEVICE_ID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; csrftoken={}".format(
|
||||
self.authenticate_csrf_token)})
|
||||
self.headers.update({"X-CSRFToken": "{}".format(self.authenticate_csrf_token), "X-Requested-With": "XMLHttpRequest"})
|
||||
resp = self.req.post(authenticate_url, params_json_str, headers=self.headers)
|
||||
# print(json.loads(resp.text).get("msg"))
|
||||
self.session_id = resp.cookies.get('sessionid')
|
||||
self.headers.update({
|
||||
"Cookie": "experimentation_subject_id=IjdmMmE5MmYxLTk4N2QtNDU1YS1hMGRjLTZhZWIwZWY3YWEyMiI%3D--fc2a7fdd42c874af494e99f3f5fdf0c372d3b4d2; __UUID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; __DEVICE_ID=93b21b91-675f-4019-fe4f-4442ec9f6e65-x; csrftoken={};sessionid={}".format(
|
||||
self.authenticate_csrf_token, self.session_id)})
|
||||
# print(self.session_id)
|
||||
|
||||
def dbs_query(self, instance_name, sql_content, limit_num="100"):
|
||||
"""
|
||||
dbs查询
|
||||
| 请求参数名 | 说明 | 类型 | 是否必填 |
|
||||
| instance_name | 数据库实例: 例如sim-my-allschool | string | True |
|
||||
| db_name | 数据库名称: hulk_class_help | string | True |
|
||||
| schema_name | 数据库模式: 默认空不填 | string | True |
|
||||
| tb_name | 表名 | string | True |
|
||||
| sql_content | 查询sql | string | True |
|
||||
| limit_num | 查询行数 | string | True |
|
||||
"""
|
||||
if not self.session_id:
|
||||
self.dbs_login()
|
||||
db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "")
|
||||
data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "",
|
||||
"sql_content": sql_content, "limit_num": limit_num}
|
||||
self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"})
|
||||
resp = self.req.post(self.query_url, data, headers=self.headers)
|
||||
result = json.loads(resp.text)
|
||||
return result.get('data').get('rows')
|
||||
|
||||
def dbs_query_as_json(self, instance_name, sql_content, limit_num="1000"):
|
||||
"""
|
||||
通过DBS查询数据,并返回json格式
|
||||
Args:
|
||||
instance_name: 数据库实例: 例如sim-my-allschool
|
||||
sql_content: 查询语句
|
||||
limit_num: 查询行数
|
||||
Returns:
|
||||
[{key:value}]
|
||||
"""
|
||||
if not self.session_id:
|
||||
self.dbs_login()
|
||||
self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"})
|
||||
# db_name = sql_content.lower().split("from ")[1].split(".")[0].replace("`", "").replace(" ", "")
|
||||
db_name = self.__get_db_names_from_sql(sql_statements=sql_content)[0]
|
||||
data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "",
|
||||
"sql_content": sql_content, "limit_num": limit_num, "workflow_name": "ODS线上数据监控"}
|
||||
resp = self.req.post(self.query_url, data, headers=self.headers)
|
||||
result = json.loads(resp.text)
|
||||
if result.get('status') != 0:
|
||||
raise Exception(result)
|
||||
else:
|
||||
r_data = self.__make_format_data(key_list=result.get('data').get('column_list'),
|
||||
column_list=result.get('data').get('rows'))
|
||||
return r_data
|
||||
|
||||
def dbs_query_as_json_auto_identify_env(self, sql_content, instance_name=None, limit_num="1000"):
|
||||
"""
|
||||
通过DBS查询数据,并返回json格式【建议别链表查询,因为sim环境不支持,除非你的函数不在sim使用】
|
||||
Args:
|
||||
sql_content: 查询语句
|
||||
instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126
|
||||
limit_num: 查询行数
|
||||
Returns:
|
||||
Note:可以根据你的查询表,直接指定sim环境的instance_name,因为qa环境时不使用此参数
|
||||
"""
|
||||
self.__check_sql_content(sql_content) # sql格式检查
|
||||
if not instance_name:
|
||||
instance_name = self.__get_instance_name(sql_content=sql_content) # 自动识别环境
|
||||
|
||||
if not self.session_id:
|
||||
self.dbs_login()
|
||||
self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"})
|
||||
# db_name = sql_content.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "")
|
||||
db_name = sql_content.lower().split("from ")[1].split(".")[0].replace("`", "").replace(" ", "")
|
||||
data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "",
|
||||
"sql_content": sql_content, "limit_num": limit_num, "workflow_name": "Test_online_data_check"}
|
||||
resp = self.req.post(self.query_url, data, headers=self.headers)
|
||||
result = json.loads(resp.text)
|
||||
if result.get('status') != 0:
|
||||
raise Exception(result)
|
||||
else:
|
||||
r_data = self.__make_format_data(key_list=result.get('data').get('column_list'),
|
||||
column_list=result.get('data').get('rows'))
|
||||
return r_data
|
||||
|
||||
def dbs_query_all_data_as_json(self, sql_content, instance_name=None, sort_key='id', sort_type='asc'):
|
||||
"""
|
||||
通过DBS查询db中的所有数据(超过1000条也行),并返回json格式,别带limit关键字,带了就走普通查询
|
||||
Args:
|
||||
sql_content: 查询语句
|
||||
instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126
|
||||
sort_key: 排序字段,默认为id
|
||||
sort_type: 排序类型,默认为asc-顺排,desc-倒排
|
||||
"""
|
||||
# 如果查询语句带有limit限制,则直接走普通查询
|
||||
if " limit " in sql_content.lower():
|
||||
return self.dbs_query_as_json_auto_identify_env(sql_content=sql_content, instance_name=instance_name)
|
||||
# 否则走全量查询
|
||||
if " join " in sql_content.lower() or " union " in sql_content.lower() or sql_content.lower().count("from") > 1:
|
||||
raise Exception("链表查询不支持全量查询,请修改查询语句")
|
||||
if "where" not in sql_content.lower():
|
||||
raise Exception("全量查询必须带有where条件,防止数量过多,请修改查询语句")
|
||||
# 全量查询
|
||||
all_data = []
|
||||
off_set = 0
|
||||
str_list = sql_content.lower().split("where")
|
||||
while True:
|
||||
if sort_type.lower() == 'asc':
|
||||
sql_str = ("{} where {}>{} and {}"
|
||||
.format(str_list[0], sort_key, off_set, str_list[1]))
|
||||
elif sort_type.lower() == 'desc':
|
||||
sql_str = ("{} where {}<{} and {}"
|
||||
.format(str_list[0], sort_key, off_set, str_list[1]))
|
||||
db_data = self.dbs_query_as_json_auto_identify_env(instance_name='prod-my-teach_classes-slave01',
|
||||
sql_content=sql_str)
|
||||
all_data += db_data
|
||||
if len(db_data) < 1000:
|
||||
break
|
||||
else:
|
||||
off_set = db_data[-1][sort_key]
|
||||
return all_data
|
||||
|
||||
def dbs_query_as_list(self, instance_name, sql_content, limit_num="100"):
|
||||
"""
|
||||
通过DBS查询数据,并返回list格式
|
||||
Args:
|
||||
instance_name: 数据库实例: 例如sim-my-allschool
|
||||
sql_content: 查询语句
|
||||
limit_num: 查询行数
|
||||
Returns:
|
||||
[{key:value}]
|
||||
"""
|
||||
res = self.dbs_query_as_json(instance_name=instance_name, sql_content=sql_content, limit_num=limit_num)
|
||||
out_data = []
|
||||
if len(res) > 0:
|
||||
for item in res:
|
||||
tmp_data = []
|
||||
for key in item:
|
||||
if len(item) > 1: # 查询结课是多列,则返回二维数组
|
||||
tmp_data.append(item[key])
|
||||
else: # 查询结课是单列,则返回一维数组
|
||||
tmp_data = item[key]
|
||||
out_data.append(tmp_data)
|
||||
|
||||
return out_data
|
||||
|
||||
@staticmethod
|
||||
def __make_format_data(key_list, column_list):
|
||||
"""
|
||||
组装json格式的数据类型
|
||||
Args:
|
||||
key_list: 列名,如['a','b']
|
||||
column_list: 数据,如[[1,2],[3,4]]
|
||||
Returns:
|
||||
[{key:value}],如[['a':1,'b':2],['a':3,'b':4]]
|
||||
"""
|
||||
obj_data = []
|
||||
for column in column_list:
|
||||
col_data = {}
|
||||
for index in range(len(key_list)):
|
||||
col_data[key_list[index]] = column[index]
|
||||
obj_data.append(col_data)
|
||||
return obj_data
|
||||
|
||||
def dbs_execute(self, instance_name, sql_content, time_out=60):
|
||||
"""
|
||||
SIM环境执行insert、update、del数据库操作
|
||||
Args:
|
||||
instance_name: 选择实例,例如sim126
|
||||
sql_content: sql语句
|
||||
time_out: 超时时间,默认60秒
|
||||
"""
|
||||
self.__check_sql_content(sql_content) # sql格式检查
|
||||
sql_info = sql_content.split(".")[0].split(" ")
|
||||
db_name = sql_info[-1].replace("`", "").replace(" ", "")
|
||||
if not self.session_id:
|
||||
self.dbs_login()
|
||||
try:
|
||||
# 检查SQL
|
||||
req_params = {"sql_content": sql_content, "instance_name": instance_name, "db_name": db_name,
|
||||
"sql_type": "online"}
|
||||
resp_check = self.req.post(self.check_url, req_params, headers=self.headers)
|
||||
if resp_check.status_code == 200:
|
||||
result_check = json.loads(resp_check.text)
|
||||
if result_check.get("data").get("CheckErrorCount") != 0:
|
||||
raise Exception("sql检查未通过,请检查调整后,再提交,msg: {}".format(result_check))
|
||||
else:
|
||||
raise Exception("dbs执行检查sql失败,错误状态码: {}, res:{}".format(resp_check.status_code, resp_check.text))
|
||||
|
||||
# 提交SQL
|
||||
req_params = {"csrfmiddlewaretoken": self.authenticate_csrf_token, "sql_content": sql_content,
|
||||
"instance_name": instance_name, "db_name": db_name,
|
||||
"sql_type": "online", "sql-upload": "", "workflow_id": "", "workflow_name": "test",
|
||||
"demand_url": "",
|
||||
"group_name": "测试组", "run_date_start": "", "run_date_end": "", "workflow_auditors": 1}
|
||||
resp_commit = self.req.post(self.commit_url, req_params, headers=self.headers)
|
||||
if resp_commit.status_code == 200:
|
||||
workflow_id = resp_commit.history[0].headers.get("Location").split("/")[2]
|
||||
else:
|
||||
raise Exception("dbs执行提交sql失败,错误状态码: {}, res:{}".format(resp_commit.status_code, resp_commit.text))
|
||||
|
||||
# 执行sql
|
||||
req_params = {"csrfmiddlewaretoken": self.authenticate_csrf_token, "workflow_id": workflow_id,
|
||||
"mode": "auto"}
|
||||
resp_execute = self.req.post(self.execute_url, req_params, headers=self.headers)
|
||||
if resp_execute.status_code == 200:
|
||||
if not resp_execute.ok:
|
||||
raise Exception("sql执行未通过,请检查调整后,再提交,msg: {}".format(resp_execute.reason))
|
||||
else:
|
||||
raise Exception("dbs执行sql失败,错误状态码: {}, res:{}".format(resp_execute.status_code, resp_execute.text))
|
||||
|
||||
# 查询执行状态
|
||||
time_out = int(time_out)
|
||||
while time_out:
|
||||
time_out -= 1
|
||||
resp_get_status = self.req.get(self.get_status_url + str(workflow_id), headers=self.headers)
|
||||
if resp_get_status.status_code == 200:
|
||||
result_status = json.loads(resp_get_status.text)
|
||||
if "Execute Successfully" in result_status.get("rows")[1].get("stagestatus"):
|
||||
return True
|
||||
else:
|
||||
time.sleep(1)
|
||||
else:
|
||||
time.sleep(1)
|
||||
raise Exception("超时未查询到结果,请确认sql是不是需要执行很久,若有必要请调整超时参数")
|
||||
except Exception as e:
|
||||
raise Exception(e)
|
||||
|
||||
def dbs_query_by_db_name(self, instance_name, db_name, sql_content, limit_num="100"):
|
||||
"""
|
||||
dbs查询
|
||||
| 请求参数名 | 说明 | 类型 | 是否必填 |
|
||||
| instance_name | 数据库实例: 例如sim-my-allschool | string | True |
|
||||
| db_name | 数据库名称: hulk_class_help | string | True |
|
||||
| schema_name | 数据库模式: 默认空不填 | string | True |
|
||||
| tb_name | 表名 | string | True |
|
||||
| sql_content | 查询sql | string | True |
|
||||
| limit_num | 查询行数 | string | True |
|
||||
"""
|
||||
if not self.session_id:
|
||||
self.dbs_login()
|
||||
data = {"instance_name": instance_name, "db_name": db_name, "schema_name": "", "tb_name": "",
|
||||
"sql_content": sql_content, "limit_num": limit_num}
|
||||
self.headers.update({"Referer": "https://dbs.qc.huohua.cn/sqlquery/"})
|
||||
resp = self.req.post(self.query_url, data, headers=self.headers)
|
||||
result = json.loads(resp.text)
|
||||
return result.get('data').get('rows')
|
||||
|
||||
def dbs_select(self, sql_content, r_type='json', instance_name=None):
|
||||
"""
|
||||
功能:通过dbs查询并返回全部数据
|
||||
Arg:
|
||||
sql: 查询语句
|
||||
r_type: 返回类型,json或list
|
||||
instance_name: 数据库实例名,不传则根据env_choose文件中的环境配置来获取
|
||||
Note:
|
||||
1. 若想要返回一条数据,自行在sql语句中添加limit 1限制
|
||||
2. 用于线上环境查询时,指定instance_name参数即可
|
||||
"""
|
||||
self.__check_sql_content(sql_content) # sql格式检查
|
||||
if not instance_name:
|
||||
instance_name = self.__get_instance_name(sql_content=sql_content) # 获取数据库实例名
|
||||
if r_type == 'json':
|
||||
db_data = self.dbs_query_as_json(sql_content=sql_content, instance_name=instance_name)
|
||||
elif r_type == 'list':
|
||||
db_data = self.dbs_query_as_list(instance_name=instance_name, sql_content=sql_content)
|
||||
else:
|
||||
raise Exception("返回类型只能是json或list")
|
||||
# if 'limit 1;' in sql_content:
|
||||
# if len(db_data) == 0:
|
||||
# return []
|
||||
# return db_data[0]
|
||||
# else:
|
||||
# return db_data
|
||||
return db_data
|
||||
|
||||
def dbs_execute_sql(self, sql_content, instance_name=None, time_out=60):
|
||||
"""
|
||||
通过DBS执行insert、update、del操作,支持concat拼接sql
|
||||
Args:
|
||||
sql_content: 待执行语句
|
||||
instance_name: 数据库实例: env_choose中是qa或独立环境时,此参数失效,否则已输入为准,不传则默认为sim126
|
||||
time_out: 超时时间,默认60秒
|
||||
Returns:
|
||||
Note:可以根据你的查询表,直接指定sim环境的instance_name,因为qa环境时不使用此参数
|
||||
"""
|
||||
if not instance_name:
|
||||
instance_name = self.__get_instance_name(sql_content=sql_content) # 自动识别环境
|
||||
if "concat" in sql_content.lower(): # 如果是拼接sql,则拆分后执行
|
||||
sql_list = self.dbs_query_as_json_auto_identify_env(sql_content=sql_content)
|
||||
for sql in sql_list:
|
||||
for key in sql:
|
||||
self.dbs_execute_sql(sql_content=sql[key])
|
||||
else:
|
||||
self.dbs_execute(instance_name=instance_name, sql_content=sql_content, time_out=time_out)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
hh_dbs = HuoHuaDBS()
|
||||
# hh_dbs.dbs_query_as_json("")
|
||||
sql = "SELECT ui.user_id,sp.id AS student_id,ui.user_name,ui.logo,ui.phone,ui.phone_code,ui.nick_name,ui.english_nickname,ui.share_code,ui.invite_code,ui.customer_id,ui.channel_id,ui.from_user_id,ui.email,ss.business_line FROM (SELECT up.id as user_id,up.user_name,up.logo,up.phone,up.phone_code,up.nick_name,up.english_nickname,up.share_code,up.invite_code,up.customer_id,up.channel_id,up.from_user_id,uc.contact_info as email FROM ucenter.user_profile up LEFT JOIN ucenter.user_contact uc ON up.id=uc.user_id WHERE up.phone_code='653666709542760459') ui LEFT JOIN peppa_channel.statistics_setting ss ON ui.channel_id = ss.channel_id LEFT JOIN ucenter.student_profile sp ON sp.user_id=ui.user_id;"
|
||||
|
||||
print(hh_dbs.get_instance_name(sql_content=sql))
|
||||
91
base_framework/public_tools/log.py
Normal file
91
base_framework/public_tools/log.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import logging
|
||||
import time
|
||||
from os import path
|
||||
from base_framework.base_config.current_pth import *
|
||||
from concurrent_log_handler import ConcurrentRotatingFileHandler
|
||||
import platform
|
||||
|
||||
|
||||
time_flag = time.time()
|
||||
d = path.dirname(__file__)
|
||||
parent_path = os.path.dirname(d)
|
||||
parent_path = os.path.dirname(parent_path)
|
||||
# obj_tool = Tools()
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
LOG_FILENAME = log_path + '/' + 'run.log'
|
||||
else:
|
||||
# Windows路径
|
||||
LOG_FILENAME = log_path + '\\' + 'run.log'
|
||||
if not os.path.exists(log_path):
|
||||
try:
|
||||
os.mkdir(log_path)
|
||||
except Exception as e:
|
||||
print('日志文件夹创建失败:{}'.format(e))
|
||||
|
||||
# MAC 路径
|
||||
# LOG_FILENAME = log_path + '/' + 'run.log'
|
||||
# LOG_FILENAME = (os.path.split(os.path.realpath(__file__)))[0] + '\\' + 'log\\run.log'
|
||||
|
||||
|
||||
# Color escape string
|
||||
COLOR_RED = '\033[1;31m'
|
||||
COLOR_GREEN = '\033[1;32m'
|
||||
COLOR_YELLOW = '\033[1;33m'
|
||||
COLOR_BLUE = '\033[1;34m'
|
||||
COLOR_PURPLE = '\033[1;35m'
|
||||
COLOR_CYAN = '\033[1;36m'
|
||||
COLOR_GRAY = '\033[1;37m'
|
||||
COLOR_WHITE = '\033[1;38m'
|
||||
COLOR_RESET = '\033[1;0m'
|
||||
|
||||
# Define log color
|
||||
LOG_COLORS = {
|
||||
'DEBUG': COLOR_BLUE + '%s' + COLOR_RESET,
|
||||
'INFO': COLOR_GREEN + '%s' + COLOR_RESET,
|
||||
'WARNING': COLOR_YELLOW + '%s' + COLOR_RESET,
|
||||
'ERROR': COLOR_RED + '%s' + COLOR_RESET,
|
||||
'CRITICAL': COLOR_RED + '%s' + COLOR_RESET,
|
||||
'EXCEPTION': COLOR_RED + '%s' + COLOR_RESET,
|
||||
}
|
||||
|
||||
|
||||
class log_colour(logging.Formatter):
|
||||
|
||||
def __init__(self, fmt=None, datefmt=None):
|
||||
logging.Formatter.__init__(self, fmt, datefmt)
|
||||
|
||||
def format(self, record):
|
||||
level_name = record.levelname
|
||||
msg = logging.Formatter.format(self, record)
|
||||
return LOG_COLORS.get(level_name, '%s') % msg
|
||||
|
||||
|
||||
def get_logger():
|
||||
logger = logging.Logger('Automation')
|
||||
logger.setLevel(0)
|
||||
terminal = logging.StreamHandler()
|
||||
terminal.setLevel(logging.DEBUG)
|
||||
# log_file = logging.handlers.TimedRotatingFileHandler(filename=LOG_FILENAME, when="S", backupCount=3,
|
||||
# encoding='utf-8', interval=5)
|
||||
log_file = ConcurrentRotatingFileHandler(
|
||||
LOG_FILENAME, maxBytes=5 * 1024 * 1024, backupCount=4, encoding="utf-8")
|
||||
log_file.setLevel(logging.INFO)
|
||||
|
||||
formatter_ter = log_colour(
|
||||
'%(asctime)s [tid:%(thread)d] %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
|
||||
formatter_log = logging.Formatter(
|
||||
'%(asctime)s [tid:%(thread)d pid:%(process)d] %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
|
||||
|
||||
terminal.setFormatter(formatter_ter)
|
||||
log_file.setFormatter(formatter_log)
|
||||
|
||||
logger.addHandler(terminal)
|
||||
logger.addHandler(log_file)
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
def set_log_file(logfilename):
|
||||
global LOG_FILENAME
|
||||
LOG_FILENAME = logfilename
|
||||
98
base_framework/public_tools/mail_help.py
Normal file
98
base_framework/public_tools/mail_help.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2021/11/22 10:41
|
||||
"""
|
||||
import time
|
||||
from base_framework.public_tools.my_faker import MyFaker
|
||||
from base_framework.public_tools.pymailtm.pymailtm import MailTm
|
||||
from retrying import retry
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.runner import Runner
|
||||
|
||||
magic_faker = MyFaker()
|
||||
|
||||
|
||||
class MailHelper:
|
||||
"""
|
||||
邮件相关操作
|
||||
"""
|
||||
new_mt_mail_obj = None
|
||||
|
||||
def __init__(self):
|
||||
self.mail_account = MailTm()
|
||||
self.obj_runner = Runner()
|
||||
self.obj_log = log.get_logger()
|
||||
|
||||
@retry(stop_max_attempt_number=5, wait_fixed=5000)
|
||||
def get_ci_new_mail(self):
|
||||
"""
|
||||
| 功能说明: | 生成随机邮件地址 |
|
||||
| 返回参数: | dic | 邮件地址 |
|
||||
| 作者信息: | 作者 林于棚 | 修改时间 |
|
||||
举例说明:
|
||||
| get_ci_new_mail() |
|
||||
"""
|
||||
faker_mail = magic_faker.gen_str(min_chars=8)
|
||||
faker_num = magic_faker.create_num(start=1, end=10000)
|
||||
return faker_mail.lower()+str(faker_num)+'@citest.com'
|
||||
|
||||
@retry(stop_max_attempt_number=5, wait_fixed=5000)
|
||||
def get_new_mail_mt(self):
|
||||
"""
|
||||
| 功能说明: | https://mail.tm/zh/ 生成随机邮件地址 |
|
||||
| 返回参数: | dic | 邮件地址 |
|
||||
| 作者信息: | 作者 林于棚 | 修改时间 |
|
||||
举例说明:
|
||||
| get_new_email() |
|
||||
"""
|
||||
self.new_mt_mail_obj = self.mail_account.get_account(password='666666')
|
||||
return self.new_mt_mail_obj.address
|
||||
# obj_log.info('生成的邮件地址:{}'.format(self.new_mt_mail_obj.address))
|
||||
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=5000)
|
||||
def get_mt_mail_message(self, address=None, password='666666'):
|
||||
"""
|
||||
| 功能说明: | 获取 https://mail.tm/zh/ 生成邮件地址的信息 |
|
||||
| 输入参数: | mail_obj | self.new_mt_mail_obj |
|
||||
| 返回参数: | dic | 邮件地址 |
|
||||
| 作者信息: | 作者 林于棚 | 修改时间 |
|
||||
举例说明:
|
||||
| get_mt_mail_message() |
|
||||
"""
|
||||
mail_message = []
|
||||
first_flag = 0
|
||||
self.get_new_mail_mt()
|
||||
account = self.new_mt_mail_obj
|
||||
if account.address != address and address:
|
||||
account.address = address
|
||||
account.password = password
|
||||
account.jwt = MailTm._make_account_request("token", address, password)
|
||||
account.auth_headers = {
|
||||
"accept": "application/ld+json",
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {}".format(account.jwt["token"])
|
||||
}
|
||||
self.obj_log.info('生成的邮件地址:{}'.format(account.address))
|
||||
self.obj_log.info('收取邮件中.............')
|
||||
for i in range(2):
|
||||
first_len = len(mail_message)
|
||||
mail_message = account.get_messages()
|
||||
last_len = len(mail_message)
|
||||
if first_len <= last_len != 0:
|
||||
continue
|
||||
elif last_len == 0 and first_flag < 1:
|
||||
first_flag += 1
|
||||
self.obj_log.info('没有收到邮件,重试中..............')
|
||||
time.sleep(4)
|
||||
else:
|
||||
self.obj_log.info('没有收任何到邮件!')
|
||||
break
|
||||
return mail_message
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
obj_mail = MailHelper()
|
||||
print(obj_mail.get_ci_new_mail())
|
||||
# print(obj_mail.get_mt_mail_message())
|
||||
231
base_framework/public_tools/mg_keyword.py
Normal file
231
base_framework/public_tools/mg_keyword.py
Normal file
@@ -0,0 +1,231 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/10/15 17:35
|
||||
"""
|
||||
import time
|
||||
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.my_faker import MyFaker
|
||||
from base_framework.public_tools.runner import Runner
|
||||
import re
|
||||
obj_log = get_logger()
|
||||
|
||||
obj_faker = MyFaker()
|
||||
obj_runner = Runner()
|
||||
|
||||
|
||||
class ManageKeyWord:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def kw_get_my_permissions(app_type, employee_id, **kwargs):
|
||||
"""
|
||||
| 功能说明: | 获取权限 |
|
||||
| 输入参数: | phone | 新增线索的手机号 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
| 存放位置: | | |
|
||||
举例说明:
|
||||
| kw_get_my_permissions | |
|
||||
"""
|
||||
options_dict = dict(**kwargs)
|
||||
change_user = options_dict.get('user', None)
|
||||
api_url = obj_runner.manage_host + '/permission/getMyPermissions'
|
||||
req_type = 'POST'
|
||||
post_data = {
|
||||
"appType": app_type,
|
||||
"employeeId": employee_id}
|
||||
resp = obj_runner.call_rest_api(
|
||||
user=change_user,
|
||||
API_URL=api_url,
|
||||
req_type=req_type,
|
||||
params=post_data)
|
||||
return resp
|
||||
|
||||
@staticmethod
|
||||
def kw_get_sms_code(phone=None, **kwargs):
|
||||
"""
|
||||
| 功能说明: | 获取短信验证码 |
|
||||
| 输入参数: | phone | 手机号码 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
| 存放位置: | | |
|
||||
举例说明:
|
||||
| kw_get_sms_code | |
|
||||
"""
|
||||
options_dict = dict(**kwargs)
|
||||
change_user = options_dict.get('user', None)
|
||||
current_evn = options_dict.get('current_evn', None)
|
||||
url = "https://smm.qa.huohua.cn/sms/log/page/all?WHERE.%5BEQ%5Dphone={}&WHERE.%5BEQ%5DtypeName=&WHERE.%5BEQ%5Dchannel=&WHERE.%5BEQ%5DgatewayType=&WHERE.%5BGT%5DcreateTime=&WHERE.%5BLT%5DcreateTime=&WHERE.%5BGT%5DsubmitTime=&WHERE.%5BLT%5DsubmitTime=&pageNum=1&pageSize=20&orderBy=id%20desc".format(
|
||||
phone)
|
||||
req_type = 'POST'
|
||||
resp = obj_runner.call_rest_api(
|
||||
user=change_user,
|
||||
API_URL=url,
|
||||
req_type=req_type,
|
||||
current_evn=current_evn)
|
||||
smm_code = resp['list'][0]['msg']
|
||||
reg = re.compile(r"(?<=验证码:)\d+")
|
||||
match = reg.search(smm_code)
|
||||
smm_code = match.group(0)
|
||||
return smm_code
|
||||
|
||||
@staticmethod
|
||||
def kw_get_authcode_without_message(phone,type=2,**kwargs):
|
||||
"""
|
||||
| 功能说明: | 直接获取短信验证码,不会发送短信 |
|
||||
| 输入参数: | phone | 手机号码 |
|
||||
| | type | 获取类型(2、磐石) |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
| 存放位置: | | |
|
||||
举例说明:
|
||||
| kw_get_authcode_without_message | phone=XXX type=XXX |
|
||||
"""
|
||||
if "country_code" in kwargs.keys():
|
||||
if "-" not in phone and not str(phone).startswith("0") and int(kwargs.get("country_code")) != 86:
|
||||
phone="{}-{}".format(kwargs.get("country_code"),phone)
|
||||
url = "{}/user_profile/sendLoginAuthCodeWithoutMessage?phone={}&type={}".format(obj_runner.manage_host, phone, type)
|
||||
obj_log.info("your input:{0}".format(phone))
|
||||
resp = obj_runner.call_rest_api(API_URL=url, req_type="POST")
|
||||
try:
|
||||
auth_code = resp['data'][-4::1]
|
||||
obj_log.info(auth_code)
|
||||
except:
|
||||
raise Exception("未获取到验证码,获取接口返回:%s"%resp)
|
||||
return auth_code
|
||||
|
||||
@staticmethod
|
||||
def kw_execute_xxl_job(job_id, para="", **kwargs):
|
||||
"""
|
||||
| 功能说明: | 执行xxljob定时任务 |
|
||||
| 输入参数: | job_id | job的任务id |
|
||||
| | para=none | job运行时的参数 |
|
||||
| 返回参数: | 无 |
|
||||
| 作者信息: | 林于棚 | 2020.12.14 |
|
||||
| 函数位置: | Public/Common/mg_keyword.py | |
|
||||
举例说明:
|
||||
| execute_xxl_job | 2109 |
|
||||
"""
|
||||
startTime_stamp = time.localtime()
|
||||
# startTime = time.strftime("%Y-%m-%d %H:%M:%S", startTime_stamp)
|
||||
endTime = time.strftime("%Y-%m-%d 23:59:59", startTime_stamp)
|
||||
jobGroup = None
|
||||
if "jobGroup" in kwargs:
|
||||
jobGroup = kwargs.pop("jobGroup")
|
||||
post_data = {
|
||||
"id": job_id,
|
||||
'executorParam': para}
|
||||
api_url = obj_runner.xxl_job_host + "/jobinfo/trigger"
|
||||
# obj_runner.call_rest_api(API_URL=api_url, req_type="POST", data=post_data, user="ci009")
|
||||
startTime = time.strftime("%Y-%m-%d %H:%M:%S", startTime_stamp)
|
||||
time.sleep(1)
|
||||
tragger_resp = obj_runner.call_rest_api(API_URL=api_url, req_type="POST", data=post_data)
|
||||
if tragger_resp.get("code") != 200:
|
||||
raise EnvironmentError("%s job 触发失败!" % job_id)
|
||||
time.sleep(3)
|
||||
if jobGroup:
|
||||
data_query = {"jobGroup": jobGroup, "jobId": job_id, "logStatus": -1,
|
||||
"filterTime": "%s - %s" % (startTime, endTime), "start": 0, "length": 10}
|
||||
url = obj_runner.xxl_job_host + "/joblog/pageList"
|
||||
i = 0
|
||||
while i < 300:
|
||||
resp = obj_runner.call_rest_api(API_URL=url, params=data_query, req_type="POST")
|
||||
if 'data' in resp and len(resp['data']) > 0:
|
||||
if resp['data'][0].get("handleCode") == 200:
|
||||
obj_log.info(resp['data'][0])
|
||||
obj_log.info("job:%s执行成功" % job_id)
|
||||
break
|
||||
time.sleep(5)
|
||||
i += 1
|
||||
else:
|
||||
raise EnvironmentError("%s job 执行失败!" % job_id)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def kw_modify_apollo_configuration():
|
||||
"""
|
||||
| 功能说明: | 修改apollo配置 |
|
||||
| 输入参数: | phone | 手机号码 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
| 存放位置: | | |
|
||||
举例说明:
|
||||
| execute_xxl_job | |
|
||||
"""
|
||||
post_data = {
|
||||
"id": 28959,
|
||||
"key": "sms.start.time",
|
||||
"value": "9:50",
|
||||
"comment": "",
|
||||
"dataChangeCreatedBy": "apollo",
|
||||
"tableViewOperType": "update"}
|
||||
api_url = "http://apollo.qa.huohua.cn/apps/peppa-cc-manage/envs/QA/clusters/default/namespaces/application/item"
|
||||
obj_runner.call_rest_api(API_URL=api_url, req_type="PUT", json=post_data)
|
||||
|
||||
@staticmethod
|
||||
def course_upload(**kwargs):
|
||||
"""
|
||||
| 功能说明: | 上传图片 |
|
||||
| 输入参数: | phone | 手机号码 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
| 存放位置: | | |
|
||||
举例说明:
|
||||
| execute_xxl_job | |
|
||||
"""
|
||||
options_dict = dict(**kwargs)
|
||||
change_user = options_dict.get('user', None)
|
||||
file = r"C:\Users\HuoHua\Pictures\桌面图片\download.jpg"
|
||||
api_url = obj_runner.teach_host + "/peppa-teach-api/common/upload"
|
||||
files = {
|
||||
"type": (None, "lesson"),
|
||||
"file": ("file", open(file, "rb"), "image/jpeg"),
|
||||
}
|
||||
# headers = {
|
||||
# "Content-Type": "multipart/form-data" # 上传文件不要设置type,会报错
|
||||
# }
|
||||
response = obj_runner.call_rest_api(
|
||||
user=change_user,
|
||||
req_type="POST",
|
||||
API_URL=api_url,
|
||||
files=files
|
||||
)
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def sku_classfiy_get(**kwargs):
|
||||
"""
|
||||
| 功能说明: | 获取SKU分类枚举值 |
|
||||
| 输入参数: | 无 | 无 |
|
||||
| 返回参数: | XXX |
|
||||
| 作者信息: | 作者 | 修改时间 |
|
||||
举例说明:
|
||||
| sku_classfiy_get | |
|
||||
"""
|
||||
options_dict = dict(**kwargs)
|
||||
change_user = options_dict.get('user', None)
|
||||
api_url = obj_runner.scm_host + '/smart/expressSku/skuClassifyEnum'
|
||||
req_type = 'GET'
|
||||
resp = obj_runner.call_rest_api(
|
||||
user=change_user,
|
||||
API_URL=api_url,
|
||||
req_type=req_type)
|
||||
return resp
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
url = "https://teach-opt-api.qa.huohua.cn/api/quality-manage/template/items/385/1/"
|
||||
res = obj_runner.call_rest_api(url, "get")
|
||||
|
||||
a = ManageKeyWord()
|
||||
# [a.kw_add_case(phone) for phone in ['1878201' +
|
||||
# str(random.randrange(1111, 9999, 2)) for _ in range(2)]]
|
||||
# a.kw_get_my_permissions('crmnew', 586470)
|
||||
# a.kw_get_sms_code('18782019436')
|
||||
# a.course_upload(user='xl')
|
||||
a.execute_xxl_job(2865, "[262]")
|
||||
# a.sku_classfiy_get()
|
||||
175
base_framework/public_tools/mongohelper.py
Normal file
175
base_framework/public_tools/mongohelper.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
import pymongo
|
||||
from base_framework.public_tools.db_dbutils_init import get_my_mongo_connection
|
||||
from base_framework.public_tools import log
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
class MongoDbHelper:
|
||||
def __init__(self):
|
||||
# """
|
||||
# 初始化mongodb、并且链接指定数据库
|
||||
# :param collection:
|
||||
# :param collect_db_name:
|
||||
# """
|
||||
# init = InitConfig()
|
||||
# self.host = init.MONGO_HOST
|
||||
# self.port = init.MONGO_PORT
|
||||
# self.user = init.MONGO_USER
|
||||
# self.pwd = init.MONGO_PASSWORD
|
||||
#
|
||||
# try:
|
||||
# self.connect_ = pymongo.MongoClient(host=self.host,
|
||||
# port=int(self.port),
|
||||
# username=self.user,
|
||||
# password=self.pwd,
|
||||
# authSource="hulk_teach_marketing"
|
||||
# )
|
||||
# except Exception as e:
|
||||
# obj_log.error("mongdb连接失败:{}".format(e))
|
||||
self.conn = get_my_mongo_connection().mongo_connect()
|
||||
|
||||
def mongo_find(self, db_name, collection_name, query={}, select_value=None):
|
||||
db = self.conn[db_name]
|
||||
data = db.get_collection(collection_name).find(query, select_value)
|
||||
obj_log.info("mongo查询语句:db.getCollection({}).find({},{}).limit(501)".format(collection_name, query, select_value))
|
||||
return data
|
||||
|
||||
def mongo_select_all(self, db_name, collection_name, query={}, select_value=None):
|
||||
"""
|
||||
| 功能说明: | 查询mongo数据 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | query={} | 查询条件 |
|
||||
| | column={} | 展示列:默认会展示 "_id" |
|
||||
| 返回参数: | 查询结果(tuple) |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
data = self.mongo_find(db_name, collection_name, query, select_value)
|
||||
d = []
|
||||
for i in data:
|
||||
d.append(i)
|
||||
return tuple(d)
|
||||
|
||||
def mongo_insert_many(self, db_name, collection_name, insert_value):
|
||||
"""
|
||||
| 功能说明: | 插入mongo数据 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | insert_value | 插入值 |
|
||||
| 返回参数: | TRUE/FALSE |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
try:
|
||||
db = self.conn[db_name]
|
||||
db.get_collection(collection_name).insert_many(insert_value)
|
||||
obj_log.info("mongo插入语句:db.getCollection({}).insertMany({})".format(collection_name, insert_value))
|
||||
except Exception as e:
|
||||
obj_log.error("插入数据失败:{}".format(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def mongo_update_many(self, db_name, collection_name, query, update_value):
|
||||
"""
|
||||
| 功能说明: | 修改mongo数据 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | query | 修改条件 |
|
||||
| | update_value | 修改值 |
|
||||
| 返回参数: | TRUE/FALSE |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
try:
|
||||
db = self.conn[db_name]
|
||||
result = db.get_collection(collection_name).update_many(query, update_value)
|
||||
obj_log.info("mongo修改语句:db.getCollection({}).updateMany({})".format(collection_name, update_value))
|
||||
obj_log.info("修改数量:{}".format(result.modified_count))
|
||||
except Exception as e:
|
||||
obj_log.error("修改失败:{}".format(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def mongo_delete_many(self, db_name, collection_name, delete_value):
|
||||
"""
|
||||
| 功能说明: | 删除mongo数据 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | delete_value | 删除值 |
|
||||
| 返回参数: | TRUE/FALSE |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
try:
|
||||
db = self.conn[db_name]
|
||||
result = db.get_collection(collection_name).delete_many(delete_value)
|
||||
obj_log.info("mongo删除语句:db.getCollection({}).deleteMany({})".format(collection_name, delete_value))
|
||||
obj_log.info("删除数量:{}".format(result.deleted_count))
|
||||
except Exception as e:
|
||||
obj_log.error("删除失败:{}".format(e))
|
||||
return False
|
||||
return True
|
||||
|
||||
def mongo_count(self, db_name, collection_name, filter, session=None, **kwargs):
|
||||
"""
|
||||
| 功能说明: | 查询mongo数据 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | filter | 查询条件 |
|
||||
| 返回参数: | 统计结果 |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
db = self.conn[db_name]
|
||||
db_collection = db[collection_name]
|
||||
db_count = db_collection.count_documents(filter, session, **kwargs)
|
||||
return db_count
|
||||
|
||||
def mongo_aggregate(self, db_name, collection_name, pipline, session=None, **kwargs):
|
||||
"""
|
||||
| 功能说明: | (aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果 |
|
||||
| 输入参数: | db_name | 数据库 |
|
||||
| | collection_name | 表(集合) |
|
||||
| | pipline | a list of aggregation pipeline stages |
|
||||
| 返回参数: | 返回聚合结果 |
|
||||
| 作者信息: | xl | 2020/11/26 21:11 |
|
||||
| 函数位置: | public_tool/mongohelper.py ||
|
||||
"""
|
||||
db = self.conn[db_name]
|
||||
db_collection = db[collection_name]
|
||||
data = db_collection.aggregate(pipline, session, **kwargs)
|
||||
s = []
|
||||
for i in data:
|
||||
s.append(i)
|
||||
return s
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
s = MongoDbHelper()
|
||||
|
||||
# 查询
|
||||
select_value = {"name": {"$regex": "^测试"}, "status":0}
|
||||
ss = s.mongo_count(db_name='hulk_teach_marketing', collection_name='activity', filter=select_value)
|
||||
print(ss)
|
||||
# print("{}\n".format(ss))
|
||||
# for i in ss:
|
||||
# print(i)
|
||||
|
||||
# 插入数据
|
||||
# dd = [{"code": "ACT2079479569078477", "created_time":1637897233410, "creator_id":586470, "creator_name":"xiaoliang02", "creator_type":1, "delete_flag":0, "deleted_time":0,"language":0,"name":"自动插入活动1-page-1001","region":"1","remark":"自动添加活动备注信息","status":1,"type":0,"user_type":0},
|
||||
# {"code": "ACT2079479569078476", "created_time":1637897233410, "creator_id":586470, "creator_name":"xiaoliang02", "creator_type":1, "delete_flag":0, "deleted_time":0,"language":0,"name":"自动插入活动2-page-1001","region":"1","remark":"自动添加活动备注信息","status":2,"type":0,"user_type":0}]
|
||||
# s.mongo_insert_many(db_name='hulk_teach_marketing', collection_name='activity', insert_value=dd)
|
||||
|
||||
# # 修改数据
|
||||
# q = {"code": "ACT210477219578032135"}
|
||||
# y = {"$set": {"name": "切图信息-右下角B区"}}
|
||||
# dd = s.mongo_update_many(db_name='hulk_teach_marketing', collection_name='activity', query=q, update_value=y)
|
||||
|
||||
# # 删除数据
|
||||
# ddd = {"name": {"$regex": "^自动插入"}}
|
||||
# s.mongo_delete_many(db_name='hulk_teach_marketing', collection_name='activity', delete_value=ddd)
|
||||
# pipline = [{"$match": {"region": "5", "delete_flag": 0}}, {"$group": {"_id": "$region", "max_order": {"$max": "$order"}}}]
|
||||
# p = s.mongo_aggregate('hulk_teach_marketing', 'activity', pipline)
|
||||
105
base_framework/public_tools/my_faker.py
Normal file
105
base_framework/public_tools/my_faker.py
Normal file
@@ -0,0 +1,105 @@
|
||||
from faker import Faker
|
||||
import random
|
||||
|
||||
'''
|
||||
简体中文:zh_CN
|
||||
繁体中文:zh_TW
|
||||
美国英文:en_US
|
||||
英国英文:en_GB
|
||||
德文:de_DE
|
||||
日文:ja_JP
|
||||
韩文:ko_KR
|
||||
法文:fr_FR'''
|
||||
|
||||
|
||||
class MyFaker:
|
||||
def __init__(self):
|
||||
# 选择中文语言
|
||||
self.fake = Faker("zh_CN")
|
||||
|
||||
def gen_name(self):
|
||||
return self.fake.name()
|
||||
|
||||
def gen_address(self):
|
||||
return self.fake.address()
|
||||
|
||||
def gen_phone_number(self, num=1):
|
||||
return [self.fake.phone_number() for _ in range(num)]
|
||||
|
||||
# 随机身份证
|
||||
def gen_identity_number(self):
|
||||
return self.fake.ssn()
|
||||
|
||||
# 信用卡号
|
||||
def credit_card_number(self):
|
||||
return self.fake.credit_card_number()
|
||||
|
||||
# MD5
|
||||
def gen_md5(self):
|
||||
return self.fake.md5()
|
||||
|
||||
# sku_name
|
||||
def gen_word(self):
|
||||
return self.fake.word()
|
||||
|
||||
# password
|
||||
def gen_password(self):
|
||||
return self.fake.password()
|
||||
|
||||
# 随机文本
|
||||
|
||||
def gen_txt(self, max_nb_chars=15):
|
||||
return self.fake.text(max_nb_chars=max_nb_chars)
|
||||
|
||||
def gen_str(self, min_chars=1, max_chars=10):
|
||||
"""
|
||||
范围长度内随机字符串
|
||||
:param min_chars:
|
||||
:param max_chars:
|
||||
:return:
|
||||
"""
|
||||
return self.fake.pystr(min_chars=min_chars, max_chars=max_chars)
|
||||
|
||||
def gen_letter(self, length=1):
|
||||
"""
|
||||
生成范围长度内随机字母a-z
|
||||
:param length: 生成的长度
|
||||
:return: str
|
||||
"""
|
||||
return_str = ''
|
||||
for _ in range(length):
|
||||
return_str = return_str + self.fake.random_letter()
|
||||
return return_str.lower()
|
||||
|
||||
def create_num(self, start, end):
|
||||
"""
|
||||
随机生成一个数字
|
||||
:param:起始范围
|
||||
:return:int
|
||||
"""
|
||||
return random.randint(start, end)
|
||||
|
||||
def gen_email(self):
|
||||
"""
|
||||
生成虚拟测试邮箱
|
||||
"""
|
||||
host_name_list=['roptaoti.com','prcf.site','curcuplas.me','hotmail.red','wpdork.com','gmailni.com']
|
||||
self.create_num(0,len(host_name_list)-1)
|
||||
host_name=host_name_list[self.create_num(0,len(host_name_list)-1)]
|
||||
# return self.fake.email() #这种方法可能存在真实邮箱
|
||||
return self.gen_str().lower() + '@' + host_name
|
||||
def gen_word(self):
|
||||
"""
|
||||
随机生成一个汉字
|
||||
:return:
|
||||
"""
|
||||
head = random.randint(0xb0, 0xf7)
|
||||
body = random.randint(0xa1, 0xfe)
|
||||
val = f'{head:x} {body:x}'
|
||||
word = bytes.fromhex(val).decode('gbk')
|
||||
return word
|
||||
|
||||
if __name__ == '__main__':
|
||||
my_faker = MyFaker()
|
||||
# print(my_faker.gen_name())
|
||||
print(my_faker.gen_email())
|
||||
555
base_framework/public_tools/pgsqlhelper.py
Normal file
555
base_framework/public_tools/pgsqlhelper.py
Normal file
@@ -0,0 +1,555 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/11/6 17:30
|
||||
"""
|
||||
import time
|
||||
import re
|
||||
from retrying import retry
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.db_dbutils_init import get_pg_connection # 假设有PostgreSQL连接池
|
||||
from base_framework.public_tools.read_config import get_current_config, get_current_env
|
||||
from base_framework.public_tools.huohua_dbs import HuoHuaDBS
|
||||
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
class PgSqlHelper:
|
||||
"""
|
||||
PostgreSQL数据库操作
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.db = get_pg_connection() # PostgreSQL连接池
|
||||
self.current_business = get_current_config(section='run_evn_name', key='current_business')
|
||||
self.current_evn = get_current_env()
|
||||
self.qa_db_to_sim_instance_name = {}
|
||||
self.sim_dbs = HuoHuaDBS()
|
||||
|
||||
# 封装执行命令
|
||||
def execute(self, sql, param=None, auto_close=False, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 执行具体的sql语句 |
|
||||
| 输入参数: | sql | 待执行的sql语句 |
|
||||
| | param=None | sql语句中where后跟的参数,也可直接写在sql语句中 |
|
||||
| | auto_close=True | 是否自动关闭数据库连接,默认:自动关闭 |
|
||||
| 返回参数: | conn,cursor,count | 连接,游标,行数 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
raise Exception("SIM环境请直接使用huohua_dbs.py中的函数")
|
||||
|
||||
try:
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db) # 从连接池获取连接
|
||||
except Exception as e:
|
||||
# 打印详细的连接信息
|
||||
try:
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.base_config.current_pth import config_file_path
|
||||
rc = ReadConfig(config_file_path)
|
||||
db_host = rc.get_value(sections='PostgreSQL', options='db_test_host')
|
||||
db_port = rc.get_value(sections='PostgreSQL', options='db_test_port')
|
||||
db_name = rc.get_value(sections='PostgreSQL', options='db_test_dbname')
|
||||
db_user = rc.get_value(sections='PostgreSQL', options='db_test_user')
|
||||
db_password = rc.get_value(sections='PostgreSQL', options='db_test_password')
|
||||
|
||||
error_info = """
|
||||
PostgreSQL连接失败!
|
||||
连接配置信息:
|
||||
主机(Host): {}
|
||||
端口(Port): {}
|
||||
数据库名(Database): {}
|
||||
用户名(User): {}
|
||||
密码(Password): {} (已隐藏)
|
||||
选择数据库(ChooseDB): {}
|
||||
错误详情: {}
|
||||
""".format(
|
||||
db_host, db_port, db_name, db_user,
|
||||
'*' * len(db_password) if db_password else 'None',
|
||||
choose_db or 'default',
|
||||
str(e)
|
||||
)
|
||||
print(error_info)
|
||||
obj_log.error(error_info)
|
||||
except Exception as config_error:
|
||||
error_info = "PostgreSQL连接失败: {} (无法读取配置信息: {})".format(str(e), str(config_error))
|
||||
print(error_info)
|
||||
obj_log.error(error_info)
|
||||
raise ValueError("PostgreSQL连接失败: {}".format(str(e)))
|
||||
|
||||
try:
|
||||
# count : 为改变的数据条数
|
||||
if param:
|
||||
# PostgreSQL使用 %s 作为占位符,与MySQL相同
|
||||
count = cursor.execute(sql, param)
|
||||
else:
|
||||
count = cursor.execute(sql)
|
||||
conn.commit()
|
||||
if auto_close:
|
||||
self.close(cursor, conn)
|
||||
except Exception as e:
|
||||
obj_log.error("PostgreSQL执行SQL失败: {}, SQL: {}".format(str(e), sql))
|
||||
raise ValueError("数据库操作失败,SQL语句:{}, 错误: {}".format(sql, str(e)))
|
||||
return cursor, conn, count
|
||||
|
||||
# 释放连接
|
||||
@staticmethod
|
||||
def close(cursor, conn):
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
# 查询所有
|
||||
def select_all(self, sql, param=None, choose_db=None, show_log=True):
|
||||
"""
|
||||
| 功能说明: | 查询数据库 | 并返回所有结果 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_select(sql_content=sql)
|
||||
|
||||
if show_log:
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
res = cursor.fetchall()
|
||||
if show_log:
|
||||
obj_log.info('数据库查询结果:{}'.format(res))
|
||||
return res
|
||||
except Exception as e:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn and not show_log:
|
||||
self.close(cursor, conn)
|
||||
|
||||
def select_all_as_list(self, sql, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 查询数据库 | 功能同select_all,仅返回结果为list |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_select(sql_content=sql, r_type='list')
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, choose_db=choose_db)
|
||||
res = cursor.fetchall()
|
||||
has_data = False
|
||||
for item in res:
|
||||
for key in item:
|
||||
if item[key]:
|
||||
has_data = True
|
||||
break
|
||||
if not has_data:
|
||||
return []
|
||||
res_list = []
|
||||
for index in range(len(res)):
|
||||
if len(res[index]) > 1:
|
||||
res_row = []
|
||||
for key in res[index]:
|
||||
res_row.append(res[index][key])
|
||||
res_list.append(res_row)
|
||||
else:
|
||||
for key in res[index]:
|
||||
res_list.append(res[index][key])
|
||||
return res_list
|
||||
except Exception as e:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 查询单条
|
||||
def select_one(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 查询数据库,并返第一行 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
if 'limit' not in sql.lower():
|
||||
sql = sql.split(';')[0] + ' limit 1;'
|
||||
sim_data = self.sim_dbs.dbs_select(sql_content=sql)
|
||||
if sim_data:
|
||||
return sim_data[0]
|
||||
else:
|
||||
return {}
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
res = cursor.fetchone()
|
||||
return res
|
||||
except Exception as e:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 增加单条数据
|
||||
def insert_one(self, sql, param=None, choose_db=None, log_level='info'):
|
||||
"""
|
||||
| 功能说明: | insert一行数据 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if log_level.lower() == 'info':
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
conn.commit()
|
||||
if log_level.lower() == 'info':
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 插入后返回插入ID(PostgreSQL方式)
|
||||
def insert_one_extension(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert一行数据,并返回插入行的ID |
|
||||
"""
|
||||
return_dict = dict()
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
# PostgreSQL需要在INSERT语句后添加RETURNING子句来获取ID
|
||||
# 如果SQL中已有RETURNING,则直接使用
|
||||
if 'RETURNING' not in sql.upper():
|
||||
# 尝试提取表名和主键列名(简化处理,实际需根据业务调整)
|
||||
table_match = re.search(r'INSERT INTO\s+(\w+\.)?(\w+)', sql, re.IGNORECASE)
|
||||
if table_match:
|
||||
sql = sql.rstrip(';') + ' RETURNING id;'
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db)
|
||||
if param:
|
||||
cursor.execute(sql, param)
|
||||
else:
|
||||
cursor.execute(sql)
|
||||
|
||||
# 获取返回的ID
|
||||
inserted_id = cursor.fetchone()[0] if 'RETURNING' in sql.upper() else None
|
||||
count = cursor.rowcount
|
||||
|
||||
conn.commit()
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return_dict['insert_count'] = count
|
||||
return_dict['insert_id'] = inserted_id
|
||||
return return_dict
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 插入多条数据
|
||||
def insert_many(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert多行数据 |
|
||||
"""
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db)
|
||||
count = cursor.executemany(sql, eval(str(param)))
|
||||
conn.commit()
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 插入多条并返回ID
|
||||
def insert_many_extension(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert多行数据,并返回ID列表 |
|
||||
"""
|
||||
return_dict = dict()
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
# PostgreSQL需要在INSERT语句后添加RETURNING子句
|
||||
if 'RETURNING' not in sql.upper():
|
||||
sql = sql.rstrip(';') + ' RETURNING id;'
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db)
|
||||
if param:
|
||||
cursor.executemany(sql, eval(str(param)))
|
||||
else:
|
||||
cursor.execute(sql)
|
||||
|
||||
# 获取所有返回的ID
|
||||
inserted_ids = [row[0] for row in cursor.fetchall()] if 'RETURNING' in sql.upper() else []
|
||||
count = cursor.rowcount
|
||||
|
||||
conn.commit()
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return_dict['insert_count'] = count
|
||||
return_dict['insert_ids'] = inserted_ids
|
||||
if inserted_ids:
|
||||
return_dict['insert_id'] = inserted_ids[0] # 第一条数据的ID
|
||||
return return_dict
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 删除
|
||||
def delete(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 删除数据库记录 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
obj_log.info('删除数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 更新
|
||||
def update(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 更新数据库记录 |
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
conn.commit()
|
||||
obj_log.info('更新数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 以下方法保持原样,仅需确保内部调用的方法正确
|
||||
def check_result_exist(self, *select_statement, retry_count=3):
|
||||
start = 0
|
||||
flag = 0
|
||||
while start <= retry_count:
|
||||
for sql in select_statement:
|
||||
res = self.select_all(sql=sql)
|
||||
if not res:
|
||||
if start == retry_count:
|
||||
obj_log.info('the result is not exist,but expect at least one')
|
||||
raise RuntimeError(u'No results, when exec sql: %s' % sql)
|
||||
else:
|
||||
obj_log.info('the result is not exist,retry {}'.format(start + 1))
|
||||
start += 1
|
||||
time.sleep(1)
|
||||
else:
|
||||
obj_log.info('find [{0}] results when exec :{1}'.format(len(res), sql))
|
||||
flag += 1
|
||||
break
|
||||
if flag > 0:
|
||||
break
|
||||
|
||||
def check_result_not_exist(self, *select_statement):
|
||||
for sql in select_statement:
|
||||
res = self.select_all(sql=sql)
|
||||
if res:
|
||||
obj_log.info('find [{0}] results, but expect 0'.format(len(res)))
|
||||
raise RuntimeError(u'the result exist, when exec sql: %s' % sql)
|
||||
else:
|
||||
obj_log.info('the result is not exist, this step pass...')
|
||||
|
||||
def row_count(self, selectStatement, param=None):
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, count = self.execute(selectStatement, param)
|
||||
return count
|
||||
except Exception as e:
|
||||
print("error_msg:", e.args)
|
||||
return 0
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 数据库断言方法(适配PostgreSQL)
|
||||
def kw_check_if_exists_in_database(self, selectStatement, choose_db=None):
|
||||
obj_log.info('Executing : Check If Exists In Database | %s ' % selectStatement)
|
||||
if not self.select_one(selectStatement, choose_db=choose_db):
|
||||
raise AssertionError("Expected to have have at least one row from '%s' "
|
||||
"but got 0 rows." % selectStatement)
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_check_if_not_exists_in_database(self, selectStatement, choose_db=None):
|
||||
obj_log.info('Executing : Check If Not Exists In Database | %s ' % selectStatement)
|
||||
queryResults = self.select_one(selectStatement, choose_db=choose_db)
|
||||
if queryResults:
|
||||
raise AssertionError("Expected to have have no rows from '%s' "
|
||||
"but got some rows : %s." % (selectStatement, queryResults))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_0(self, selectStatement):
|
||||
obj_log.info('Executing : Row Count Is 0 | %s ' % selectStatement)
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows > 0:
|
||||
raise AssertionError("Expected zero rows to be returned from '%s' "
|
||||
"but got rows back. Number of rows returned was %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_equal_to_x(self, selectStatement, numRows):
|
||||
obj_log.info('Executing : Row Count Is Equal To X | %s | %s ' % (selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows != int(str(numRows)):
|
||||
raise AssertionError("Expected same number of rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_greater_than_x(self, selectStatement, numRows):
|
||||
obj_log.info('Executing : Row Count Is Greater Than X | %s | %s ' % (selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows <= int(numRows):
|
||||
raise AssertionError("Expected more rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_less_than_x(self, selectStatement, numRows):
|
||||
obj_log.info('Executing : Row Count Is Less Than X | %s | %s ' % (selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows >= int(numRows):
|
||||
raise AssertionError("Expected less rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_table_must_exist(self, tableName):
|
||||
"""
|
||||
PostgreSQL检查表是否存在
|
||||
"""
|
||||
obj_log.info('Executing : Table Must Exist | %s ' % tableName)
|
||||
# PostgreSQL检查表是否存在的查询
|
||||
selectStatement = """
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = %s
|
||||
);
|
||||
"""
|
||||
try:
|
||||
result = self.select_one(selectStatement, (tableName,))
|
||||
if not result or not result[0]: # 结果为False或None
|
||||
raise AssertionError("Table '%s' does not exist in the database" % tableName)
|
||||
return True
|
||||
except Exception as e:
|
||||
raise AssertionError("Error checking table existence: %s" % str(e))
|
||||
|
||||
# 生成器方式查询
|
||||
def select_all_as_generator(self, sql, param=None, choose_db=None):
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
cursor, conn = None, None
|
||||
try:
|
||||
cursor, conn, counts = self.execute(sql, param, choose_db=choose_db)
|
||||
for count in range(counts):
|
||||
item = cursor.fetchone()
|
||||
obj_log.info(item)
|
||||
yield item
|
||||
except Exception as e:
|
||||
raise RuntimeError(e.args)
|
||||
finally:
|
||||
if cursor and conn:
|
||||
self.close(cursor, conn)
|
||||
|
||||
def get_qa_to_sim_dbs(self):
|
||||
get_sql = "select qa_db,sim_instance from sparkatp.qa_db_mapping_sim_dbs where status=1 and is_delete=0"
|
||||
res_info = self.sim_dbs.dbs_query_by_db_name("qadb-slave", "sparkatp", get_sql, limit_num=1000)
|
||||
for item in res_info:
|
||||
self.qa_db_to_sim_instance_name.update({item[0]: item[1]})
|
||||
return self.qa_db_to_sim_instance_name
|
||||
|
||||
def get_instance_name(self, sql_str):
|
||||
db_name = sql_str.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "")
|
||||
return self.qa_db_to_sim_instance_name.get(db_name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 测试示例
|
||||
db = PgSqlHelper()
|
||||
|
||||
# 测试查询
|
||||
sql = "SELECT * FROM account.account LIMIT 5;"
|
||||
res = db.select_one(sql=sql)
|
||||
print(res)
|
||||
|
||||
# 测试插入(带RETURNING)
|
||||
insert_sql = """
|
||||
INSERT INTO account.account (username, email, created_at)
|
||||
VALUES (%s, %s, NOW())
|
||||
RETURNING id;
|
||||
"""
|
||||
insert_params = ('test_user', 'test@example.com')
|
||||
insert_result = db.insert_one_extension(insert_sql, insert_params)
|
||||
print(f"插入结果: {insert_result}")
|
||||
3
base_framework/public_tools/pymailtm/__init__.py
Normal file
3
base_framework/public_tools/pymailtm/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from base_framework.public_tools.pymailtm.pymailtm import MailTm, Account, Message
|
||||
|
||||
__version__ = '1.0'
|
||||
33
base_framework/public_tools/pymailtm/cli.py
Normal file
33
base_framework/public_tools/pymailtm/cli.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
from argparse import ArgumentParser
|
||||
import signal
|
||||
import sys
|
||||
from pymailtm import MailTm
|
||||
|
||||
|
||||
def init():
|
||||
def signal_handler(sig, frame) -> None:
|
||||
print('\n\nClosing! Bye!')
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
parser = ArgumentParser(
|
||||
description="A python interface to mail.tm web api. The temp mail address "
|
||||
"will be copied to the clipboard and the utility will then "
|
||||
"wait for a message to arrive. When it does, it will be "
|
||||
"opened in a browser. Exit the loop with ctrl+c.")
|
||||
parser.add_argument('-n', '--new-account', action='store_true',
|
||||
help="whether to force the creation of a new account")
|
||||
parser.add_argument('-l', '--login', action='store_true',
|
||||
help="print the credentials and open the login page, then exit")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.login:
|
||||
MailTm().browser_login(new=args.new_account)
|
||||
else:
|
||||
MailTm().monitor_new_account(force_new=args.new_account)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
init()
|
||||
226
base_framework/public_tools/pymailtm/pymailtm.py
Normal file
226
base_framework/public_tools/pymailtm/pymailtm.py
Normal file
@@ -0,0 +1,226 @@
|
||||
import json
|
||||
import os
|
||||
import pyperclip
|
||||
import random
|
||||
import requests
|
||||
import string
|
||||
import webbrowser
|
||||
|
||||
from random_username.generate import generate_username
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
from time import sleep
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class Account:
|
||||
"""Representing a temprary mailbox."""
|
||||
|
||||
def __init__(self, id, address, password):
|
||||
self.id_ = id
|
||||
self.address = address
|
||||
self.password = password
|
||||
# Set the JWT
|
||||
jwt = MailTm._make_account_request("token",
|
||||
self.address, self.password)
|
||||
self.auth_headers = {
|
||||
"accept": "application/ld+json",
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer {}".format(jwt["token"])
|
||||
}
|
||||
self.api_address = MailTm.api_address
|
||||
|
||||
def get_messages(self, page=1):
|
||||
"""Download a list of messages currently in the account."""
|
||||
r = requests.get("{}/messages?page={}".format(self.api_address, page),
|
||||
headers=self.auth_headers, verify=False)
|
||||
messages = []
|
||||
for message_data in r.json()["hydra:member"]:
|
||||
# recover full message
|
||||
r = requests.get(
|
||||
f"{self.api_address}/messages/{message_data['id']}", headers=self.auth_headers, verify=False)
|
||||
text = r.json()["text"]
|
||||
html = r.json()["html"]
|
||||
# prepare the mssage object
|
||||
messages.append(Message(
|
||||
message_data["id"],
|
||||
message_data["from"],
|
||||
message_data["to"],
|
||||
message_data["subject"],
|
||||
message_data["intro"],
|
||||
text,
|
||||
html,
|
||||
message_data))
|
||||
return messages
|
||||
|
||||
def delete_account(self):
|
||||
"""Try to delete the account. Returns True if it succeeds."""
|
||||
r = requests.delete("{}/accounts/{}".format(self.api_address,
|
||||
self.id_), headers=self.auth_headers, verify=False)
|
||||
return r.status_code == 204
|
||||
|
||||
def monitor_account(self):
|
||||
"""Keep waiting for new messages and open them in the browser."""
|
||||
while True:
|
||||
print("\nWaiting for new messages...")
|
||||
start = len(self.get_messages())
|
||||
while len(self.get_messages()) == start:
|
||||
sleep(1)
|
||||
print("New message arrived!")
|
||||
self.get_messages()[0].open_web()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Message:
|
||||
"""Simple data class that holds a message information."""
|
||||
id_: str
|
||||
from_: Dict
|
||||
to: Dict
|
||||
subject: str
|
||||
intro: str
|
||||
text: str
|
||||
html: str
|
||||
data: Dict
|
||||
|
||||
def open_web(self):
|
||||
"""Open a temporary html file with the mail inside in the browser."""
|
||||
with NamedTemporaryFile(mode="w", delete=False, suffix=".html") as f:
|
||||
|
||||
html = self.html[0].replace("\n", "<br>").replace("\r", "")
|
||||
message = """<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<b>from:</b> {}<br>
|
||||
<b>to:</b> {}<br>
|
||||
<b>subject:</b> {}<br><br>
|
||||
{}</body>
|
||||
</html>""".format(self.from_, self.to, self.subject, html)
|
||||
|
||||
f.write(message)
|
||||
f.flush()
|
||||
file_name = f.name
|
||||
|
||||
open_webbrowser("file://{}".format(file_name))
|
||||
# Wait a second before deleting the tempfile, so that the
|
||||
# browser can load it safely
|
||||
sleep(1)
|
||||
# os.remove(file_name)
|
||||
|
||||
|
||||
def open_webbrowser(link: str) -> None:
|
||||
"""Open a url in the browser ignoring error messages."""
|
||||
saverr = os.dup(2)
|
||||
os.close(2)
|
||||
os.open(os.devnull, os.O_RDWR)
|
||||
try:
|
||||
webbrowser.open(link)
|
||||
finally:
|
||||
os.dup2(saverr, 2)
|
||||
|
||||
|
||||
class CouldNotGetAccountException(Exception):
|
||||
"""Raised if a POST on /accounts or /authorization_token return a failed status code."""
|
||||
|
||||
|
||||
class InvalidDbAccountException(Exception):
|
||||
"""Raised if an account could not be recovered from the db file."""
|
||||
|
||||
|
||||
class MailTm:
|
||||
"""A python wrapper for mail.tm web api, which is documented here:
|
||||
https://api.mail.tm/"""
|
||||
|
||||
api_address = "https://api.mail.tm"
|
||||
db_file = os.path.join(Path.home(), ".pymailtm")
|
||||
|
||||
def _get_domains_list(self):
|
||||
r = requests.get("{}/domains".format(self.api_address), verify=False)
|
||||
response = r.json()
|
||||
domains = list(map(lambda x: x["domain"], response["hydra:member"]))
|
||||
return domains
|
||||
|
||||
def get_account(self, password=None):
|
||||
"""Create and return a new account."""
|
||||
username = (generate_username(1)[0]).lower()
|
||||
domain = random.choice(self._get_domains_list())
|
||||
address = "{}@{}".format(username, domain)
|
||||
if not password:
|
||||
password = self._generate_password(6)
|
||||
response = self._make_account_request("accounts", address, password)
|
||||
account = Account(response["id"], response["address"], password)
|
||||
self._save_account(account)
|
||||
return account
|
||||
|
||||
def _generate_password(self, length):
|
||||
letters = string.ascii_letters + string.digits
|
||||
return ''.join(random.choice(letters) for i in range(length))
|
||||
|
||||
@staticmethod
|
||||
def _make_account_request(endpoint, address, password):
|
||||
account = {"address": address, "password": password}
|
||||
headers = {
|
||||
"accept": "application/ld+json",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
r = requests.post("{}/{}".format(MailTm.api_address, endpoint),
|
||||
data=json.dumps(account), headers=headers, verify=False)
|
||||
if r.status_code not in [200, 201]:
|
||||
raise CouldNotGetAccountException()
|
||||
return r.json()
|
||||
|
||||
def monitor_new_account(self, force_new=False):
|
||||
"""Create a new account and monitor it for new messages."""
|
||||
account = self._open_account(new=force_new)
|
||||
account.monitor_account()
|
||||
|
||||
def _save_account(self, account: Account):
|
||||
"""Save the account data for later use."""
|
||||
data = {
|
||||
"id": account.id_,
|
||||
"address": account.address,
|
||||
"password": account.password
|
||||
}
|
||||
with open(self.db_file, "w+") as db:
|
||||
json.dump(data, db)
|
||||
|
||||
def _load_account(self):
|
||||
"""Return the last used account."""
|
||||
with open(self.db_file, "r") as db:
|
||||
data = json.load(db)
|
||||
# send a /me request to ensure the account is there
|
||||
if "address" not in data or "password" not in data or "id" not in data:
|
||||
# No valid db file was found, raise
|
||||
raise InvalidDbAccountException()
|
||||
else:
|
||||
return Account(data["id"], data["address"], data["password"])
|
||||
|
||||
def _open_account(self, new=False):
|
||||
"""Recover a saved account data, check if it's still there and return that one; otherwise create a new one and
|
||||
return it.
|
||||
|
||||
:param new: bool - force the creation of a new account"""
|
||||
def _new():
|
||||
account = self.get_account()
|
||||
print("New account created and copied to clipboard: {}".format(account.address), flush=True)
|
||||
return account
|
||||
if new:
|
||||
account = _new()
|
||||
else:
|
||||
try:
|
||||
account = self._load_account()
|
||||
print("Account recovered and copied to clipboard: {}".format(account.address), flush=True)
|
||||
except Exception:
|
||||
account = _new()
|
||||
pyperclip.copy(account.address)
|
||||
print("")
|
||||
return account
|
||||
|
||||
def browser_login(self, new=False):
|
||||
"""Print login credentials and open the login page in the browser."""
|
||||
account = self._open_account(new=new)
|
||||
print("\nAccount credentials:")
|
||||
print("\nEmail: {}".format(account.address))
|
||||
print("Password: {}\n".format(account.password))
|
||||
open_webbrowser("https://mail.tm/")
|
||||
sleep(1) # Allow for the output of webbrowser to arrive
|
||||
196
base_framework/public_tools/read_config.py
Normal file
196
base_framework/public_tools/read_config.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# coding=utf-8
|
||||
|
||||
import configparser
|
||||
import os
|
||||
import codecs
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools import log
|
||||
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
class ReadConfig:
|
||||
"""
|
||||
专门读取配置文件的,.ini文件格式
|
||||
"""
|
||||
|
||||
def __init__(self, filename=config_file_path):
|
||||
self.config_path = filename
|
||||
fd = open(self.config_path, encoding='utf-8')
|
||||
data = fd.read()
|
||||
if data[:3] == codecs.BOM_UTF8:
|
||||
data = data[3:]
|
||||
files = codecs.open(self.config_path, "w")
|
||||
files.write(data)
|
||||
files.close()
|
||||
fd.close()
|
||||
|
||||
self.cf = configparser.SafeConfigParser(allow_no_value=True)
|
||||
self.cf.read(self.config_path, encoding='utf-8')
|
||||
|
||||
def get_value(self, sections, options):
|
||||
"""
|
||||
获取config文件数据
|
||||
"""
|
||||
return self.cf.get(sections, options)
|
||||
|
||||
def read_cfg(self):
|
||||
"""
|
||||
读取配置文件路径
|
||||
"""
|
||||
return self.cf.read(filenames=self.config_path, encoding='utf-8')
|
||||
|
||||
def get_sections(self):
|
||||
"""
|
||||
读取配置文件中所有的section(可以理解为组名)
|
||||
"""
|
||||
return self.cf.sections()
|
||||
|
||||
def get_options(self, section):
|
||||
"""
|
||||
读取该section下所有的option(可以理解成读取该组下的所有key)
|
||||
"""
|
||||
return self.cf.options(section)
|
||||
|
||||
def get_items(self, section):
|
||||
"""
|
||||
读取该section下的所有值,并以键值对形式输出
|
||||
"""
|
||||
return self.cf.items(section)
|
||||
|
||||
def add_section(self, section):
|
||||
"""
|
||||
添加一个section,参数为section的名称
|
||||
"""
|
||||
self.cf.add_section(section)
|
||||
with open(self.config_path, 'w') as fw: # 循环写入
|
||||
self.cf.write(fw)
|
||||
|
||||
def set_section(self, section, option, value):
|
||||
"""
|
||||
在section下面添加一条数据(key=value),需要调用write()将内容写入文件
|
||||
"""
|
||||
self.cf.set(section, option, value)
|
||||
with open(self.config_path, 'w') as fw: # 循环写入
|
||||
self.cf.write(fw)
|
||||
|
||||
def get_all_cfg(self, section=None):
|
||||
all_config = {}
|
||||
for key in self.get_sections():
|
||||
all_values = {}
|
||||
for value_key in self.get_options(key):
|
||||
all_values[value_key] = self.get_value(key, value_key)
|
||||
all_config[key] = all_values
|
||||
if section:
|
||||
if section in all_config:
|
||||
return all_config[section]
|
||||
else:
|
||||
raise Exception("配置节点:{}在文件中不存在,请检查....".format(section))
|
||||
else:
|
||||
return all_config
|
||||
|
||||
|
||||
def get_current_config(file_path=env_choose_path, section=None, key=None):
|
||||
"""
|
||||
功能:读取env_choose.ini中的配置并返回
|
||||
"""
|
||||
rd = ReadConfig(file_path)
|
||||
if key in rd.get_options(section=section):
|
||||
return rd.get_value(sections=section, options=key)
|
||||
else:
|
||||
return "server not exist"
|
||||
|
||||
|
||||
def get_zhyy_config(file_path=config_choose_path, section=None, key=None):
|
||||
"""
|
||||
功能:读取env_choose.ini中的配置并返回
|
||||
"""
|
||||
rd = ReadConfig(file_path)
|
||||
if key in rd.get_options(section=section):
|
||||
return rd.get_value(sections=section, options=key)
|
||||
else:
|
||||
return "server not exist"
|
||||
|
||||
|
||||
def get_current_env():
|
||||
"""
|
||||
功能:读取env_choose.ini中的当前环境配置并返回
|
||||
"""
|
||||
rd = ReadConfig(env_choose_path)
|
||||
env = rd.get_value(sections="run_jira_id", options="huohua-podenv")
|
||||
if not env:
|
||||
env = rd.get_value(sections="run_evn_name", options="current_evn")
|
||||
return env
|
||||
|
||||
|
||||
def get_uat_config(website, page, key=None, section='page_element'):
|
||||
"""
|
||||
| 功能说明: | 从uat_config文件夹下的对应文件中读取key |
|
||||
| 输入参数: | website | 站点名,public_business/uat/uat_config目录下的文件名 |
|
||||
| | page | web页面名称,website文件夹下的文件名,不用.ini的文件后缀名 |
|
||||
| | section | 配置文件中的节点名,默认读取页面元素 |
|
||||
| | key | 配置文件中的key,如果不输入,将以当前构建环境为key,如qa,sim |
|
||||
| 作者信息: | 吴勇刚 | 2022.06.22 |
|
||||
"""
|
||||
config_path = os.path.join(uat_config_path, website, "{}.ini".format(page))
|
||||
if not key:
|
||||
key = get_current_config(section="run_evn_name", key="current_evn").lower()
|
||||
rd = ReadConfig(config_path)
|
||||
return rd.get_value(sections=section, options=key)
|
||||
|
||||
|
||||
class InitConfig:
|
||||
|
||||
def __init__(self, run_user_name=None, current_evn=None):
|
||||
self.cfg_rtn = ReadConfig()
|
||||
self.evn_rtn = ReadConfig(env_choose_path)
|
||||
self.db_rtn = ReadConfig(db_config_path)
|
||||
self.config_rtn = ReadConfig(config_choose_path)
|
||||
self.all_cfg = self.cfg_rtn.get_all_cfg()
|
||||
self.config_cfg = self.config_rtn.get_all_cfg()
|
||||
self.evn_cfg = self.evn_rtn.get_all_cfg()
|
||||
self.db_cfg = self.db_rtn.get_all_cfg()
|
||||
try:
|
||||
self.astwb_cfg = ReadConfig(astwb_config)
|
||||
self.astwb_all_cfg = self.astwb_cfg.get_all_cfg()
|
||||
# 合并配置文件
|
||||
self.all_cfg.update(self.astwb_all_cfg)
|
||||
except Exception as e:
|
||||
pass
|
||||
if current_evn is None:
|
||||
self.current_evn = self.evn_cfg['run_evn_name']['current_evn'] if self.evn_cfg['run_evn_name'][
|
||||
'current_evn'] != '' else 'QA'
|
||||
else:
|
||||
self.current_evn = current_evn
|
||||
if run_user_name:
|
||||
self.current_user = run_user_name
|
||||
else:
|
||||
self.current_user = self.evn_cfg['run_user_name']['default_user']
|
||||
self.zhyy_host = self.get_current_env_info(self.config_cfg, self.current_evn, 'zhyy_login')
|
||||
|
||||
def get_current_env_info(self, cfg_obj, current_env, key):
|
||||
"""
|
||||
config.ini中环境处理专用
|
||||
:param cfg_obj: self.cfg_rtn = ReadConfig() self.all_cfg = self.cfg_rtn.get_all_cfg() cfg_obj = self.all_cfg
|
||||
:param current_env: QA SIM PRODUCT
|
||||
:param key: options
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
value = cfg_obj[current_env][key]
|
||||
except KeyError:
|
||||
value = cfg_obj['QA'][key]
|
||||
if current_env == 'SIM':
|
||||
value = value.replace('.qa.', '.sim.')
|
||||
return value
|
||||
elif current_env == 'PRODUCT':
|
||||
value = value.replace('.qa.', '.')
|
||||
return value
|
||||
return value
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
# init_cfg = InitConfig(curt_evn='QA', curt_user='lrq')
|
||||
print(ReadConfig().add_section(section='temp_teacher'))
|
||||
print(ReadConfig().set_section(section='temp_teacher', option='show_username', value=11))
|
||||
247
base_framework/public_tools/redis_api.py
Normal file
247
base_framework/public_tools/redis_api.py
Normal file
@@ -0,0 +1,247 @@
|
||||
# coding: utf-8
|
||||
from redis import ConnectionPool, StrictRedis
|
||||
import time
|
||||
from base_framework.public_tools.read_config import ReadConfig
|
||||
from base_framework.base_config.current_pth import env_choose_path
|
||||
|
||||
|
||||
class RedisApi:
|
||||
"""
|
||||
| 功能说明: | 操作redis |
|
||||
| 作者信息: | 作者 qiaoxinjiu |
|
||||
| 修改时间: | 2021-09-03 |
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.redis_con = None
|
||||
self.pool = None
|
||||
self.host = None
|
||||
self.pwd = ""
|
||||
self.port = 6379
|
||||
self.evn_cfg = ReadConfig(env_choose_path)
|
||||
self.current_business = self.evn_cfg.get_value(sections="run_evn_name", options="current_business")
|
||||
|
||||
def kw_conn_redis_service(self, redis_dbname, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 与redis server 建立连接 |
|
||||
| 输入参数: |
|
||||
| | redis_dbname | redis数据库名,默认为5 |
|
||||
| | redis_ip_addr | redis服务器地址 |
|
||||
| | password | redis密码 |
|
||||
| | redis_port | redis端口号,默认为6379 |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | 无 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
说明:初始化 pool和redis_con
|
||||
"""
|
||||
if not self.host: # 如果没有指定redis域名
|
||||
if is_as:
|
||||
self.host = 'redis-qa2.redis.rds.aliyuncs.com'
|
||||
self.pwd = 'AcUVeRb8lN'
|
||||
else:
|
||||
if self.current_business == "hh":
|
||||
self.host = 'redis.qa.huohua.cn'
|
||||
self.pwd = 'AcUVeRb8lN'
|
||||
elif self.current_business == "hhi":
|
||||
self.host = 'redis.qa.visparklearning.com'
|
||||
self.pwd = 'hxTjlWBYdK6UpAGF'
|
||||
try:
|
||||
self.pool = ConnectionPool(host=self.host, port=self.port,
|
||||
db=redis_dbname, password=self.pwd, decode_responses=True)
|
||||
self.redis_con = StrictRedis(connection_pool=self.pool)
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError('Failed to connect redis service. error: %s' % e)
|
||||
|
||||
def kw_get_redis_message(self, redis_dbname, redis_key, timeout=5, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 获取redis_key对应的value |
|
||||
| 输入参数: | redis_key |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | redis_key对应的value |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
"""
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
try:
|
||||
while timeout > 0:
|
||||
if not self.redis_con.exists(redis_key):
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
continue
|
||||
else:
|
||||
resp = self.redis_con.get(redis_key)
|
||||
return resp
|
||||
raise RuntimeError('not have redis_key, please check')
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError('get redis value. error: %s' % e)
|
||||
finally:
|
||||
self.kw_disconnect_redis()
|
||||
|
||||
def kw_verify_redis_have_key(self, redis_dbname, key, timeout=5, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 验证缓存中存在KEY |
|
||||
| 输入参数: | key | key值 |
|
||||
| | timeout | 超时时间,默认为5s |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | True/False:存在返回true,否则失败 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
"""
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
try:
|
||||
while timeout > 0:
|
||||
if self.redis_con.exists(key):
|
||||
return True
|
||||
else:
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
continue
|
||||
raise RuntimeError('not have this key: %s' % key)
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError('verify_redis_have_key. error: %s' % e)
|
||||
finally:
|
||||
self.kw_disconnect_redis()
|
||||
|
||||
def kw_verify_redis_not_have_key(self, redis_dbname, key, timeout=5, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 验证缓存中不存在KEY |
|
||||
| 输入参数: | key | key值 |
|
||||
| | timeout | 超时时间,默认为5s |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | True/False:不存在返回true,否则失败 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
"""
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
try:
|
||||
while timeout > 0:
|
||||
if not self.redis_con.exists(key):
|
||||
return True
|
||||
else:
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
continue
|
||||
raise RuntimeError('have this key: %s' % key)
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError('verify_redis_not_have_key. error: %s' % e)
|
||||
finally:
|
||||
self.kw_disconnect_redis()
|
||||
|
||||
def kw_disconnect_redis(self):
|
||||
"""
|
||||
| 功能说明: | 断开rebids服务 |
|
||||
| 输入参数: | 无 |
|
||||
| 返回参数: | 无 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
备注: 断开连接池
|
||||
"""
|
||||
try:
|
||||
self.redis_con.close()
|
||||
self.pool.disconnect()
|
||||
except RuntimeError as e:
|
||||
raise RuntimeError('disconnect redis service failed. error: %s' % e)
|
||||
|
||||
def kw_del_key_by_key(self, redis_dbname, redis_key, is_as=True, is_check=True, host=None, pwd=None):
|
||||
"""
|
||||
| 功能说明: | 删掉redis中的 redis_key |
|
||||
| 输入参数: | host | redis域名,可以不传,按环境走默认配置 |
|
||||
| | pwd | redis密码,可以不传,按环境走默认配置 |
|
||||
| | redis_dbname | redis数据库编号,必传 |
|
||||
| | redis_key | 关键key,必传 |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | 无 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
"""
|
||||
if host and pwd: # 当指定了域名,则按入参查询
|
||||
self.host = host
|
||||
self.pwd = pwd
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
try:
|
||||
resp = self.redis_con.delete(redis_key)
|
||||
if not resp and is_check:
|
||||
raise RuntimeError('del key %s failed. error: %s' % (redis_key, resp))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.kw_disconnect_redis()
|
||||
|
||||
# def kw_del_key_by_key_pre(self, redis_dbname, key_pre, is_as=True):
|
||||
# """
|
||||
# | 功能说明: | 根据前缀删除|
|
||||
# | 输入参数: | key_pre |
|
||||
# | | is_as | 是否为ALLSchool业务 |
|
||||
# | 返回参数: | 无 |
|
||||
# | 作者信息: | 作者 huaxuemin | 修改时间 2021-09-03 |
|
||||
# """
|
||||
# self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
# try:
|
||||
# key_pre = key_pre + "*"
|
||||
# res = self.redis_con.scan(match=key_pre, count=9999999999)
|
||||
# if len(res[1]) > 0:
|
||||
# for key in res[1]:
|
||||
# resp = self.redis_con.delete(key)
|
||||
# if not resp:
|
||||
# raise RuntimeError('del key %s failed. error: %s' % (key, resp))
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
# self.kw_disconnect_redis()
|
||||
|
||||
def kw_get_key_by_key_pre(self, redis_dbname, key_pre, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 根据前缀删除|
|
||||
| 输入参数: | key_pre |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | 无 |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2021-10-07 |
|
||||
"""
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
key_pre = key_pre + "*"
|
||||
# for key in self.redis_con.scan_iter(match=key_pre):
|
||||
# self.redis_con.delete(key)
|
||||
for key in self.redis_con.keys(key_pre):
|
||||
return key
|
||||
self.kw_disconnect_redis()
|
||||
return ""
|
||||
|
||||
def kw_set_redis_by_db_key_value(self, db_num, key, value, is_as=False):
|
||||
self.kw_conn_redis_service(db_num, is_as=is_as)
|
||||
self.redis_con.set(key, value)
|
||||
if not self.redis_con.exists(key):
|
||||
raise ValueError("设置redis失败。")
|
||||
|
||||
def kw_get_zset_message(self, redis_dbname, redis_key, timeout=5, is_as=True):
|
||||
"""
|
||||
| 功能说明: | 获取redis_key 有序集合对应的value |
|
||||
| 输入参数: | redis_key |
|
||||
| | is_as | 是否为ALLSchool业务 |
|
||||
| 返回参数: | redis_key 有序集合对应的value |
|
||||
| 作者信息: | 作者 huaxuemin | 修改时间 2022-07-27 |
|
||||
"""
|
||||
self.kw_conn_redis_service(redis_dbname, is_as=is_as)
|
||||
try:
|
||||
while timeout > 0:
|
||||
if not self.redis_con.exists(redis_key):
|
||||
time.sleep(1)
|
||||
timeout -= 1
|
||||
continue
|
||||
else:
|
||||
resp = self.redis_con.zrange(redis_key, 0, -1)
|
||||
return resp
|
||||
print('not have redis_key: {}, please check'.format(redis_key))
|
||||
return []
|
||||
except RuntimeError as e:
|
||||
print('get redis value. error: %s' % e)
|
||||
return []
|
||||
finally:
|
||||
self.kw_disconnect_redis()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test = RedisApi()
|
||||
# res = test.kw_get_redis_message(9, "peppa:ticket:call:pcsm:13708231975", is_as=False)
|
||||
res = test.kw_del_key_by_key(host='rediscourse.qa.huohua.cn',
|
||||
pwd='Bkl6LvqfzFCzYPAh',
|
||||
redis_dbname='9',
|
||||
redis_key="public-holiday-teacher-v1:1_82908",
|
||||
is_as=False)
|
||||
print(res)
|
||||
# test.kw_conn_redis_service(0)
|
||||
# test.kw_del_key_by_key(9, "REVISIT_TASK:REVISITING_IDS", is_as=False)
|
||||
# print(test.kw_get_key_by_key_pre(0, "HULK-ORG-API:RESET-TOKEN:huaxuemin@huohua.cn#"))
|
||||
|
||||
138
base_framework/public_tools/rocket_mq.py
Normal file
138
base_framework/public_tools/rocket_mq.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# coding=utf-8
|
||||
# 用于MQ的各类操作
|
||||
import time
|
||||
import os
|
||||
|
||||
from base_framework.public_business.common.UBRD.kw.promotion_keyword import obj_log
|
||||
from base_framework.public_tools.read_config import ReadConfig, get_current_env, get_current_config
|
||||
from base_framework.public_tools.runner import Runner
|
||||
from base_framework.public_tools.custom_error import BusinessError
|
||||
obj_runner = Runner()
|
||||
|
||||
|
||||
class RocketMQ:
|
||||
|
||||
def __init__(self, env=''):
|
||||
self.need_login = True # 是否需要登录
|
||||
self.team = get_current_config(section="run_evn_name", key="current_team")
|
||||
if not env:
|
||||
env = get_current_env().lower()
|
||||
if env.lower() == 'sim':
|
||||
if self.team in ['XUEDAU']:
|
||||
self.mq_host = ""
|
||||
self.need_login = False
|
||||
else:
|
||||
self.mq_host = "https://mq-console.sim.huohua.cn"
|
||||
else:
|
||||
if self.team in ['XUEDAU']:
|
||||
self.mq_host = "http://10.250.200.3:19876"
|
||||
self.need_login = False
|
||||
else:
|
||||
self.mq_host = "https://mq-console.qa.huohua.cn"
|
||||
|
||||
def mq_query_msg_info(self, topic, begin_time, end_time):
|
||||
"""查询mq消息"""
|
||||
msg_id_list = self.__query_msg_id_list(topic=topic, begin_time=begin_time, end_time=end_time)
|
||||
msg_info = []
|
||||
for msg_id in msg_id_list:
|
||||
m_info = self.__query_msg_detail_by_id(topic=topic, msg_id=msg_id)
|
||||
msg_info.append(m_info)
|
||||
return msg_info
|
||||
|
||||
def mq_query_msg_info_by_sub_str(self, topic, begin_time, end_time, sub_str=None):
|
||||
"""查询mq消息,并返回匹配的消息内容"""
|
||||
msg_info = self.mq_query_msg_info(topic=topic, begin_time=begin_time, end_time=end_time)
|
||||
if sub_str:
|
||||
sub_msg = []
|
||||
for msg in msg_info:
|
||||
if str(sub_str) in str(msg["msg_body"]):
|
||||
sub_msg.append(msg)
|
||||
return sub_msg
|
||||
else:
|
||||
return msg_info
|
||||
|
||||
def __query_msg_id_list(self, topic, begin_time, end_time):
|
||||
"""查询mq的id列表"""
|
||||
mq_url = "{}/message/queryMessageByTopic.query".format(self.mq_host)
|
||||
post_data = {"topic": topic,
|
||||
"begin": str(time.mktime(time.strptime(str(begin_time), '%Y-%m-%d %H:%M'))).split('.')[0] + '000',
|
||||
"end": str(time.mktime(time.strptime(str(end_time), '%Y-%m-%d %H:%M'))).split('.')[0] + '000'}
|
||||
if self.need_login:
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", params=post_data)
|
||||
else:
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", token=False, params=post_data)
|
||||
if resp['status'] == 0:
|
||||
msg_id_list = []
|
||||
for item in resp['data']:
|
||||
msg_id_list.append(item['msgId'])
|
||||
return msg_id_list
|
||||
else:
|
||||
raise Exception("查询MQ消息列表失败:{}".format(resp))
|
||||
|
||||
def __query_msg_detail_by_id(self, topic, msg_id):
|
||||
"""查询mq消息明细"""
|
||||
mq_url = "{}/message/viewMessage.query".format(self.mq_host)
|
||||
post_data = {"topic": topic, "msgId": msg_id}
|
||||
if self.need_login:
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", params=post_data)
|
||||
else:
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="GET", token=False, params=post_data)
|
||||
if resp['status'] == 0:
|
||||
msg_tag = resp['data']['messageView']['properties'].get('TAGS', None)
|
||||
msg_key = resp['data']['messageView']['properties'].get('KEYS', None)
|
||||
msg_env = resp['data']['messageView']['properties'].get('podenv', None)
|
||||
msg_body = resp['data']['messageView'].get('messageBody', None)
|
||||
return {"msg_id": msg_id, "msg_tag": msg_tag, "msg_key": msg_key, "msg_env": msg_env, "msg_body": msg_body}
|
||||
else:
|
||||
raise Exception("查询MQ消息明细失败:{}".format(resp))
|
||||
|
||||
def mq_send_msg(self, topic, message_body, tag=None, key=None, pro=None, **kwargs):
|
||||
"""mq消息发送"""
|
||||
mq_url = "{}/topic/sendTopicMessage.do".format(self.mq_host)
|
||||
post_data = {'topic': topic,
|
||||
'tag': tag,
|
||||
'key': key,
|
||||
'messageBody': str(message_body).replace("'", "\"")
|
||||
}
|
||||
if not pro:
|
||||
# 添加启动文件中的独立环境编号
|
||||
cfg_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
ini_path = cfg_path + '/base_framework/base_config/env_choose.ini'
|
||||
jira_id = ReadConfig(ini_path).get_value(sections='run_jira_id', options='huohua-podenv')
|
||||
if jira_id:
|
||||
post_data['pro'] = jira_id
|
||||
else:
|
||||
post_data['pro'] = 'qa'
|
||||
else:
|
||||
post_data['pro'] = pro
|
||||
|
||||
if self.team.lower() == "xuedau":
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data, **kwargs)
|
||||
else:
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data)
|
||||
|
||||
try:
|
||||
obj_log.info("发送mq消息返回:{}".format(resp))
|
||||
if resp and 'errMsg' in resp and 'timeout' in str(resp['errMsg']):
|
||||
time.sleep(1) # 如果接口返回超时,就再发送一次
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data)
|
||||
elif resp and 'message' in resp and '无效的token' in str(resp['message']):
|
||||
time.sleep(1) # 如果token过期,就再发送一次
|
||||
resp = obj_runner.call_rest_api(user=None, API_URL=mq_url, req_type="POST", json=post_data)
|
||||
elif resp and resp['status'] == 0 and resp['data']['sendStatus'] == 'SEND_OK':
|
||||
return True
|
||||
else:
|
||||
raise Exception("发送mq消息失败:{}".format(resp))
|
||||
except Exception as e:
|
||||
obj_log.info("发送mq消息体:{}".format(post_data))
|
||||
raise Exception("发送mq消息失败:{}|{}".format(e, resp))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
mq = RocketMQ()
|
||||
mb = {"platformId": 2, "eventType": "user.join", "roleType": 200, "roleId": 48711, "appId": "", "source": "SparkEnglish", "classroomCode": "", "classroomType": 1, "scheduleCode": "ECR666358307917090884", "joinTime": 1747189786903, "classSizeType": 1, "isFirst": "", "subChannelCodeList": "", "deviceId": "20200830-9CFC-E8BE-8D68-9CFCE8BE8D6C", "classroomVersion": "25.5.3-qa.2212381", "appVersion": "25.4.7-stable.2172220"}
|
||||
rsp = mq.mq_send_msg(topic='CP_CLASSROOM_STATUS_SYNC', message_body=mb, tag='TAG_ROLE_JOIN_TIME', pro='QA', xuedau="")
|
||||
print(rsp)
|
||||
|
||||
|
||||
365
base_framework/public_tools/runner.py
Normal file
365
base_framework/public_tools/runner.py
Normal file
@@ -0,0 +1,365 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from urllib import parse
|
||||
from base_framework.public_tools.get_token import LoginSys, SparkleLogin, AgentApiLogin, MarketApiLogin, token_dict, \
|
||||
AllSchool, \
|
||||
ParentLogin, student_token_cache, StudentLogin, SparkEduLogin, SparkSaasLogin, SparkSaasTeacherLogin
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.read_config import ReadConfig, get_current_env
|
||||
from base_framework.base_config.current_pth import *
|
||||
from base_framework.public_tools.read_config import get_current_config
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
|
||||
obj_log = log.get_logger()
|
||||
evn_cfg = ReadConfig(env_choose_path)
|
||||
base_cfg = ReadConfig(config_file_path)
|
||||
all_school_api_host = ['api.qa.allschool.com', 'api.sim.allschool.com', 'api.allschool.com']
|
||||
|
||||
|
||||
class Runner(LoginSys):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# self.env = get_current_config(section="run_evn_name", key="current_evn")
|
||||
self.env = get_current_env()
|
||||
self.is_need_sso = ["teach-api"]
|
||||
self.team = get_current_config(section="run_evn_name", key="current_team")
|
||||
self.db_con = MySqLHelper()
|
||||
self.sql_data = [] # 存放接口响应时间
|
||||
self.week = time.strftime("%w", time.localtime())
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
|
||||
if not hasattr(cls, 'instance'):
|
||||
cls.instance = super().__new__(cls)
|
||||
|
||||
return cls.instance
|
||||
|
||||
def __del__(self):
|
||||
if self.sql_data: # 如果有数据未插入
|
||||
# sql_str = ("insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) "
|
||||
# "values (%s,%s,%s,%s,%s);")
|
||||
# self.db_con.insert_many(sql=sql_str, param=self.sql_data)
|
||||
# self.sql_data.clear()
|
||||
pass
|
||||
|
||||
def __call_api(self, session, api_url, req_type, **kwargs):
|
||||
try:
|
||||
if req_type == "GET":
|
||||
req = session.get(api_url, verify=False, **kwargs)
|
||||
elif req_type == "POST":
|
||||
req = session.post(api_url, verify=False, **kwargs)
|
||||
elif req_type == "PUT":
|
||||
req = session.put(api_url, verify=False, **kwargs)
|
||||
elif req_type == "DELETE":
|
||||
req = session.delete(api_url, verify=False, **kwargs)
|
||||
elif req_type == "PATCH":
|
||||
req = session.patch(api_url, verify=False, **kwargs)
|
||||
else:
|
||||
obj_log.info(f'req_type,输入错误!不支持请求方法{req_type}')
|
||||
raise Exception(f'req_type,输入错误!不支持请求方法{req_type}')
|
||||
except Exception as e:
|
||||
obj_log.info('返回数据:{}'.format(e))
|
||||
return e
|
||||
|
||||
if self.env.lower() != "sim": # 非sim环境才记录接口响应时间
|
||||
# 每周六,记录一次所有接口的响应时间
|
||||
if self.week in ['6']:
|
||||
current_time = int(time.strftime("%H", time.localtime()))
|
||||
if current_time >= 5: # 5点之前的404校验脚本不统计,因为都是非正常参数,详见:http://10.250.200.1:8080/jenkins/view/2-%E8%BE%85%E5%8A%A9%E7%A8%8B%E5%BA%8F/job/%E6%8E%A5%E5%8F%A3404%E6%A3%80%E6%9F%A5/
|
||||
run_time = int(req.elapsed.total_seconds() * 1000)
|
||||
if run_time > 200: # 超过200ms的接口记录到数据库
|
||||
in_data = (self.team, req_type, run_time, api_url, 0)
|
||||
self.sql_data.append(in_data)
|
||||
if len(self.sql_data) >= 10: # 每10条数据插入一次, 最后一次不足100条时在__del__中插入
|
||||
sql_str = (
|
||||
"insert into sparkatp.interface_response_time(team, in_type, rp_time, in_url,in_id) "
|
||||
"values (%s,%s,%s,%s,%s);")
|
||||
self.db_con.insert_many(sql=sql_str, param=self.sql_data)
|
||||
self.sql_data.clear()
|
||||
return req
|
||||
|
||||
def call_rest_api(self, API_URL, req_type, user=None,
|
||||
token=None, current_evn=None, is_file=False, **kwargs):
|
||||
'''
|
||||
功能:所有接口访问入口
|
||||
'''
|
||||
if self.env.lower() == "sim":
|
||||
API_URL = API_URL.replace(".qa.", ".sim.")
|
||||
return self._call_zhyy_api(API_URL, req_type, user,
|
||||
token, current_evn, is_file, **kwargs)
|
||||
|
||||
def _call_zhyy_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs):
|
||||
"""
|
||||
功能:模拟走ruoyi登录模式,带token访问接口
|
||||
:param API_URL:
|
||||
:param req_type:
|
||||
:param user: 使用的自定义用户登录
|
||||
:param token: 从SSO获取的token,此字段使用默认值None,不是的情况使用False
|
||||
:param current_evn: QA or SIM
|
||||
:param is_file:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
req_type = req_type.upper()
|
||||
retry_num = 4
|
||||
cnt = 0
|
||||
if 'as_login_type' in kwargs.keys():
|
||||
kwargs.pop('as_login_type')
|
||||
options_dict = dict(**kwargs)
|
||||
result = parse.urlparse(url=api_url, allow_fragments=True)
|
||||
host = result.hostname
|
||||
if not user:
|
||||
user = self.curt_user
|
||||
obj_log.info('登录系统为{},用户名为手动输入:{}'.format(host, user))
|
||||
obj_log.info('请求地址:{}'.format(api_url))
|
||||
obj_log.info('请求数据:{}'.format(options_dict))
|
||||
header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"}
|
||||
session = requests.session()
|
||||
session.headers.update(header)
|
||||
while cnt < retry_num:
|
||||
if token is None:
|
||||
if not user:
|
||||
user = evn_cfg.get_value(sections='run_user_name', options='default_user')
|
||||
tenant_name = base_cfg.get_value(sections=user, options='tenant')
|
||||
usr_name = base_cfg.get_value(sections=user, options='username')
|
||||
usr_pwd = base_cfg.get_value(sections=user, options='password')
|
||||
# 检查 kwargs 中是否包含登录信息(tenantName/username 和 password)
|
||||
# 如果包含,使用传入的参数;否则使用配置文件中的登录信息
|
||||
json_data = kwargs.get('json', {})
|
||||
is_login_params = isinstance(json_data, dict) and 'password' in json_data and (
|
||||
'tenantName' in json_data or 'username' in json_data)
|
||||
|
||||
if is_login_params:
|
||||
# 使用传入的登录参数
|
||||
obj_log.info('检测到 kwargs 中包含登录信息,使用传入的参数进行登录')
|
||||
login_req = self.__call_api(session, api_url, req_type='POST', **kwargs)
|
||||
else:
|
||||
# 使用配置文件中的登录信息
|
||||
obj_log.info('使用配置文件中的登录信息进行登录')
|
||||
request_body = {'json': {'tenantName': tenant_name, 'username': usr_name, 'password': usr_pwd,
|
||||
'rememberMe': 'true'}}
|
||||
api_login_url = self.zhyy_host
|
||||
login_req = self.__call_api(session, api_login_url, req_type='POST', **request_body)
|
||||
try:
|
||||
req_json = login_req.json()
|
||||
except Exception:
|
||||
# 如果 json() 失败,尝试将 text 转成 JSON
|
||||
try:
|
||||
if isinstance(login_req, Exception):
|
||||
obj_log.warning("登录请求失败: {}".format(login_req))
|
||||
req_json = {}
|
||||
else:
|
||||
req_json = json.loads(login_req.text)
|
||||
except Exception:
|
||||
if isinstance(login_req, Exception):
|
||||
obj_log.warning("登录请求失败: {}".format(login_req))
|
||||
else:
|
||||
obj_log.warning("登录响应无法解析为JSON: {}".format(login_req.text))
|
||||
req_json = {}
|
||||
# 判断登录是否成功:code为0或200表示成功,或者data中包含accessToken
|
||||
login_code = req_json.get("code")
|
||||
if login_code not in [0, 200] and not req_json.get("data", {}).get('accessToken'):
|
||||
raise Exception("SSO登录失败: {}".format(req_json))
|
||||
user_token = req_json.get("data", {}).get('accessToken')
|
||||
# 如果 api_url 本身就是登录接口,登录成功后直接返回登录响应
|
||||
if is_login_params:
|
||||
obj_log.info('api_url 是登录接口,直接返回登录响应')
|
||||
if not is_file:
|
||||
return req_json
|
||||
else:
|
||||
return login_req.content
|
||||
else:
|
||||
user_token = token
|
||||
if user_token:
|
||||
session.headers.update({'ssotoken': user_token})
|
||||
session.headers.update({'sso-token': user_token})
|
||||
session.headers.update({'accesstoken': user_token})
|
||||
session.headers.update({'Accesstoken': user_token})
|
||||
session.headers.update({'access-token': user_token})
|
||||
session.headers.update({'Authorization': user_token})
|
||||
session.headers.update({'token': user_token})
|
||||
obj_log.info(f'请求头headers:{session.headers}')
|
||||
req = self.__call_api(session, api_url, req_type, **kwargs)
|
||||
if isinstance(req, Exception):
|
||||
obj_log.error('请求失败:{}'.format(req))
|
||||
return req
|
||||
if not is_file:
|
||||
try:
|
||||
rtn_temp = req.json()
|
||||
except Exception as e:
|
||||
# 如果 json() 失败,尝试将 text 转成 JSON
|
||||
try:
|
||||
rtn_temp = json.loads(req.text)
|
||||
except Exception:
|
||||
# 如果都失败,保持为 text 字符串
|
||||
rtn_temp = req.text
|
||||
else:
|
||||
rtn_temp = req.content
|
||||
# SSO登录过期处理开始
|
||||
status_code = str(req.status_code)
|
||||
obj_log.info('------状态码:{}, 返回信息:{}'.format(status_code, rtn_temp))
|
||||
try:
|
||||
# 如果 rtn_temp 是字典,才能使用 .get() 方法
|
||||
if isinstance(rtn_temp, dict):
|
||||
resp_code = str(rtn_temp.get('code', '200'))
|
||||
else:
|
||||
resp_code = '200'
|
||||
except:
|
||||
resp_code = '200'
|
||||
if not is_file:
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("--缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
if status_code == "401" or resp_code == '401':
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
if status_code == "200" and "<!DOCTYPE html>" in str(rtn_temp):
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("--缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
# SSO登录过期处理完成
|
||||
req.close()
|
||||
if not is_file:
|
||||
obj_log.info('返回数据:{}'.format(rtn_temp))
|
||||
else:
|
||||
obj_log.info('返回数据:文件字节流')
|
||||
return rtn_temp
|
||||
return False
|
||||
|
||||
def _call_sso_api(self, api_url, req_type, user=None, token=None, current_evn=None, is_file=False, **kwargs):
|
||||
"""
|
||||
功能:模拟走sso登录模式,带token访问接口
|
||||
:param API_URL:
|
||||
:param req_type:
|
||||
:param user: 使用的自定义用户登录
|
||||
:param token: 从SSO获取的token,此字段使用默认值None,不是的情况使用False
|
||||
:param current_evn: QA or SIM
|
||||
:param is_file:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
req_type = req_type.upper()
|
||||
retry_num = 4
|
||||
cnt = 0
|
||||
if 'as_login_type' in kwargs.keys():
|
||||
kwargs.pop('as_login_type')
|
||||
options_dict = dict(**kwargs)
|
||||
result = parse.urlparse(url=api_url, allow_fragments=True)
|
||||
host = result.hostname
|
||||
if not user:
|
||||
user = self.curt_user
|
||||
obj_log.info('登录系统为{},用户名为手动输入:{}'.format(host, user))
|
||||
obj_log.info('请求地址:{}'.format(api_url))
|
||||
obj_log.info('请求数据:{}'.format(options_dict))
|
||||
|
||||
header = {"Authorization": "Basic c3BhcmtsZS13ZWI6c3BhcmtsZS13ZWI=", "tenant-id": "1"}
|
||||
session = requests.session()
|
||||
session.headers.update(header)
|
||||
while cnt < retry_num:
|
||||
if token is None:
|
||||
if not user:
|
||||
user = evn_cfg.get_value(sections='run_user_name', options='default_user')
|
||||
usr_name = base_cfg.get_value(sections=user, options='show_username')
|
||||
usr_pwd = base_cfg.get_value(sections=user, options='password')
|
||||
sso_url = "{}?username={}&password={}".format(self.sparkle_pc_token_url, usr_name, usr_pwd)
|
||||
sso_rsp = session.post(sso_url).json()
|
||||
if sso_rsp.get("code") != 200:
|
||||
raise Exception("SSO登录失败: {}".format(sso_rsp))
|
||||
user_token = sso_rsp.get("data").get('accessToken')
|
||||
else:
|
||||
user_token = token
|
||||
if user_token:
|
||||
session.headers.update({'ssotoken': user_token})
|
||||
session.headers.update({'sso-token': user_token})
|
||||
session.headers.update({'accesstoken': user_token})
|
||||
session.headers.update({'Accesstoken': user_token})
|
||||
session.headers.update({'access-token': user_token})
|
||||
session.headers.update({'token': user_token})
|
||||
obj_log.info(f'请求头headers:{session.headers}')
|
||||
|
||||
req = self.__call_api(session, api_url, req_type, **kwargs)
|
||||
|
||||
if not is_file:
|
||||
try:
|
||||
rtn_temp = req.json()
|
||||
except Exception as e:
|
||||
# 如果 json() 失败,尝试将 text 转成 JSON
|
||||
try:
|
||||
rtn_temp = json.loads(req.text)
|
||||
except Exception:
|
||||
# 如果都失败,保持为 text 字符串
|
||||
rtn_temp = req.text
|
||||
else:
|
||||
rtn_temp = req.content
|
||||
# SSO登录过期处理开始
|
||||
status_code = str(req.status_code)
|
||||
obj_log.info('------状态码:{}, 返回信息:{}'.format(status_code, rtn_temp))
|
||||
try:
|
||||
# 如果 rtn_temp 是字典,才能使用 .get() 方法
|
||||
if isinstance(rtn_temp, dict):
|
||||
resp_code = str(rtn_temp.get('code', '200'))
|
||||
else:
|
||||
resp_code = '200'
|
||||
except:
|
||||
resp_code = '200'
|
||||
if not is_file:
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("--缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
|
||||
if status_code == "401" or resp_code == '401':
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
if status_code == "200" and "<!DOCTYPE html>" in str(rtn_temp) and "https://sso.qa.huohua.cn" in str(
|
||||
rtn_temp):
|
||||
token_dict.clear() # 缓存session过期清理
|
||||
student_token_cache.clear() # 缓存session过期清理
|
||||
obj_log.warning("--缓存session过期,清理缓存!")
|
||||
cnt += 1
|
||||
if cnt <= 1:
|
||||
continue
|
||||
# SSO登录过期处理完成
|
||||
req.close()
|
||||
if not is_file:
|
||||
obj_log.info('返回数据:{}'.format(rtn_temp))
|
||||
else:
|
||||
obj_log.info('返回数据:文件字节流')
|
||||
return rtn_temp
|
||||
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
obj_runner = Runner()
|
||||
# url = "https://api.qa.sparkedu.com/user_language/"
|
||||
# url = "https://api.qa.sparkedu.com/third/student"
|
||||
url = "https://api.qa.sparkedu.com/user/appoint/add"
|
||||
phone = '13716640630'
|
||||
# post_data = {"avatar":"https://stalegacy.huohua.cn/image/huohua/avatar/default/default_avatar1.png",
|
||||
# "birthday":"2021-01-03 00:00:00",
|
||||
# "id":21434807,
|
||||
# "nickname":"hute222",
|
||||
# "sex":0}
|
||||
post_data = {"subjectType": 1}
|
||||
# resp = obj_runner.call_rest_api(API_URL=url, phone=phone, req_type="POST", json=post_data)
|
||||
resp = obj_runner.week
|
||||
print(resp)
|
||||
504
base_framework/public_tools/selenium_api.py
Normal file
504
base_framework/public_tools/selenium_api.py
Normal file
@@ -0,0 +1,504 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
import os
|
||||
import time
|
||||
import platform
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as ec
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver import ActionChains
|
||||
from selenium.webdriver.support.select import Select
|
||||
from base_framework.public_tools.read_config import get_current_config
|
||||
|
||||
cull_path = os.path.dirname(os.path.abspath(__file__))
|
||||
plugs = os.path.abspath(os.path.join(cull_path, '../{}'.format('/platform_tools/plugins/headerenv.crx')))
|
||||
DEFAULT_TIMEOUT = 5
|
||||
|
||||
|
||||
class SeleniumWebUI:
|
||||
def __init__(self):
|
||||
self.driver = None
|
||||
self.action = None # 用于鼠标类事件
|
||||
self.business = get_current_config(section="run_evn_name", key="current_business")
|
||||
self.jira_id = get_current_config(section="run_jira_id", key="huohua-podenv")
|
||||
self.feature = get_current_config(section="run_jira_id", key="huohua-feature")
|
||||
# print(self.jira_id)
|
||||
|
||||
def web_open_browser(self, url="", browser_type="chrome", enable_image=True, station='web'):
|
||||
"""
|
||||
| 功能说明: | 打开浏览器,默认chrome |
|
||||
| 输入参数: | url | 网址,默认为空:仅打开浏览器 |
|
||||
| | browser_type | 浏览器类型:chrome,firefox,edge |
|
||||
| | enable_image | 浏览器是否加载图片 |
|
||||
| | station | 浏览器模式:web:web模式,m:手机模式 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
if browser_type == "chrome":
|
||||
options = webdriver.ChromeOptions()
|
||||
if platform.system() == 'Linux':
|
||||
default_driver_path = r'/home/chrome_home/chromedriver'
|
||||
options.add_argument("--headless")
|
||||
options.add_argument("--no-sandbox")
|
||||
options.add_argument('--disable-gpu')
|
||||
options.add_argument('--disable-dev-shm-usage')
|
||||
self.driver = webdriver.Chrome(executable_path=default_driver_path, options=options)
|
||||
else:
|
||||
# 将正确的chrome driver版本放在python根目录下
|
||||
options.add_extension(plugs)
|
||||
if not enable_image:
|
||||
options.add_argument('blink-settings=imagesEnabled=false')
|
||||
print('当前为无图片模式')
|
||||
if station == 'm':
|
||||
options.add_experimental_option('mobileEmulation', {'deviceName': 'iPhone 12 Pro'})
|
||||
self.driver = webdriver.Chrome(options=options)
|
||||
elif browser_type == "firefox":
|
||||
self.driver = webdriver.Firefox()
|
||||
elif browser_type == "edge":
|
||||
self.driver = webdriver.Edge()
|
||||
else:
|
||||
raise Exception("传入的浏览器类型不正确,正确的浏览器类型(chrome, firefox, edge)")
|
||||
if len(url) > 0:
|
||||
self.web_open_url(url=url)
|
||||
self.action = ActionChains(self.driver)
|
||||
|
||||
def web_open_url(self, url):
|
||||
"""
|
||||
| 功能说明: | 打开链接并最大化浏览器 |
|
||||
| 输入参数: | url | 访问地址 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
if not self.driver:
|
||||
self.web_open_browser()
|
||||
if self.jira_id.lower() not in ('', 'qa'):
|
||||
# 判断并添加独立环境
|
||||
self.web_set_independent_environment()
|
||||
self.driver.maximize_window()
|
||||
self.driver.get(url)
|
||||
|
||||
def web_close_browser(self):
|
||||
"""
|
||||
| 功能说明: | 关闭浏览器,但注意并没有销毁driver对象 |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
举例说明:
|
||||
"""
|
||||
self.driver.quit()
|
||||
|
||||
def web_refresh_page(self):
|
||||
"""
|
||||
| 功能说明: | 刷新浏览器当前页面 |
|
||||
| 输入参数: | |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
self.driver.refresh()
|
||||
|
||||
def web_switch_handle(self, index):
|
||||
"""
|
||||
| 功能说明: | 切换浏览器页面标签,适用于新开页面,或多页面操作 |
|
||||
| 输入参数: | index | 标签索引 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
handles = self.driver.window_handles
|
||||
self.driver.switch_to_window(handles[index])
|
||||
|
||||
def web_get_cookie(self):
|
||||
"""
|
||||
| 功能说明: | 返回浏览器当前application的cookie |
|
||||
| 输入参数: | |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
return self.driver.get_cookies()
|
||||
|
||||
def web_find_element(self, locator, timeout=DEFAULT_TIMEOUT, raise_exception=True):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回第一个元素 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
if not isinstance(locator, tuple):
|
||||
locator = (By.XPATH, locator)
|
||||
element = WebDriverWait(self.driver, timeout).until(ec.presence_of_element_located(locator),
|
||||
message=['The element is not exist,please check....\n'
|
||||
'element xpath:{}'.format(locator)
|
||||
if raise_exception else None])
|
||||
return element
|
||||
|
||||
def web_find_elements(self, locator, timeout=DEFAULT_TIMEOUT, raise_exception=True):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回所有元素 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| | raise_exception | 当找不到元素时:True-阻断报错(默认),False-返回空list |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
if not isinstance(locator, tuple):
|
||||
locator = (By.XPATH, locator)
|
||||
element = []
|
||||
try:
|
||||
element = WebDriverWait(self.driver, timeout).until(ec.presence_of_all_elements_located(locator))
|
||||
except:
|
||||
if raise_exception:
|
||||
raise Exception("The element is not exist,please check....\n element xpath:{}".format(locator))
|
||||
return element
|
||||
|
||||
def web_click_element(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,普通方法点击 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
element.click()
|
||||
|
||||
def web_click_elements(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 点击所有元素 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
elements = self.web_find_elements(locator, timeout)
|
||||
for element in elements:
|
||||
element.click()
|
||||
|
||||
def web_click_element_by_js(self, locator=None, element=None, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,以JS的方式点击 |
|
||||
| 输入参数: | locator | 元素路径,当没有element时输入 |
|
||||
| | element | 元素对象,当没有locator时输入 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
if not element and locator:
|
||||
obj = self.web_find_element(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].click();", obj)
|
||||
elif element and not locator:
|
||||
self.driver.execute_script("arguments[0].click();", element)
|
||||
else:
|
||||
raise Exception('不支持的传参方式,locator和element必须且只能传一个')
|
||||
|
||||
def web_move_element_to_top(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 将指定元素滑动到当前页面顶部 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.08.12 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].scrollIntoView();", element)
|
||||
|
||||
def web_move_element_to_bottom(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 将指定元素滑动到当前页面底部 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.08.12 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].scrollIntoView(false);", element)
|
||||
|
||||
def web_move_to_top(self):
|
||||
"""
|
||||
| 功能说明: | 滑动到当前页面顶部 |
|
||||
| 作者信息: | 谢祥益 | 2022.08.12 |
|
||||
"""
|
||||
self.driver.execute_script("window.scrollTo(document.body.scrollHeight,0)")
|
||||
|
||||
def web_move_to_bottom(self):
|
||||
"""
|
||||
| 功能说明: | 滑动到当前页面底部 |
|
||||
| 作者信息: | 谢祥益 | 2022.08.12 |
|
||||
"""
|
||||
self.driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")
|
||||
|
||||
def web_send_keys(self, locator, text, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,清空输入框,写入text |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | text | 输入内容 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
element.send_keys(Keys.CONTROL, 'a')
|
||||
element.send_keys(text)
|
||||
|
||||
def web_get_element_text(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回该元素的文本值 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
return element.text
|
||||
|
||||
def web_click_single_box(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入单选框元素定位器,定位到该元素,依次点击单选框 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
elements = self.web_find_elements(locator, timeout)
|
||||
for element in elements:
|
||||
self.driver.execute_script("arguments[0].click();", element)
|
||||
time.sleep(1.5)
|
||||
|
||||
def web_select_drop_down_box(self, locator, index=0, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入下拉框定位器,定位到该元素,选择下拉框的第index个选项 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | index | 第几个元素的下标 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
select = Select(self.web_find_element(locator, timeout))
|
||||
select.select_by_index(index)
|
||||
|
||||
def web_click_checkbox(self, locator, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入多选框元素定位器,定位到该元素,全选 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
checkbox = self.web_find_elements(locator, timeout)
|
||||
for i in checkbox:
|
||||
if not i.is_selected():
|
||||
i.click()
|
||||
|
||||
def web_upload_file(self, locator, path, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,传入文件路径,只适用于input标签 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | path | 文件完整路径 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
element = self.web_find_element(locator, timeout)
|
||||
element.send_keys(path)
|
||||
|
||||
def web_move_windows(self, x=0, y=0):
|
||||
"""
|
||||
| 功能说明: | 传滑动窗口 |
|
||||
| 输入参数: | x | x轴距离 |
|
||||
| | y | y轴距离 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
self.driver.execute_script("window.scrollBy({},{})".format(x, y))
|
||||
|
||||
def web_go_to_previous_page(self):
|
||||
"""
|
||||
| 功能说明: | 返回上一页(浏览器工具栏向左箭头) |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
self.driver.back()
|
||||
|
||||
def web_go_to_next_page(self):
|
||||
"""
|
||||
| 功能说明: | 前进一页(浏览器工具栏向右箭头) |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
self.driver.forward()
|
||||
|
||||
def web_close_current_page(self):
|
||||
"""
|
||||
| 功能说明: | 关闭当前窗口页面 |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
self.driver.close()
|
||||
|
||||
def web_get_window_title(self):
|
||||
"""
|
||||
| 功能说明: | 获取当前浏览器标题 |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
return self.driver.title
|
||||
|
||||
def web_get_element_attribute_value(self, locator, attr: str, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 获取元素属性值 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | attr | 属性名 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
obj = self.web_find_element(locator, timeout=timeout)
|
||||
attr_value = obj.get_attribute(attr)
|
||||
return attr_value
|
||||
|
||||
def web_modify_tag_attribution(self, locator, attr, value, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 修改页签属性 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | attr | 修改属性名 |
|
||||
| | value | 修改后的值 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
obj = self.web_find_element(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].{attr}={value};".format(attr=attr, value=value), obj)
|
||||
|
||||
def web_input_to_readonly_tag(self, locator, text, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 带只读属性的标签输入 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | text | 输入内容 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
obj = self.web_find_element(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].removeAttribute('readonly');", obj)
|
||||
self.web_send_keys(locator=locator, text=text)
|
||||
|
||||
def web_get_css_property_value(self, locator, css_property, timeout=DEFAULT_TIMEOUT):
|
||||
"""
|
||||
| 功能说明: | 获取css属性值 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | text | 输入内容 |
|
||||
| | timeout | 超时时间,默认10秒 |
|
||||
| 作者信息: | 谢祥益 | 2022.07.08 |
|
||||
"""
|
||||
obj = self.web_find_element(locator, timeout)
|
||||
value = obj.value_of_css_property(css_property)
|
||||
return value
|
||||
|
||||
def web_take_screenshot(self, save_path, picture_name):
|
||||
"""
|
||||
| 功能说明: | 截屏浏览器当前页面 |
|
||||
| 输入参数: | save_path | 保存地址 |
|
||||
| | picture_name | 图片名称 |
|
||||
| 作者信息: | 谢祥益 | 2022.06.01 |
|
||||
"""
|
||||
picture_path = os.path.join(save_path, picture_name + '.png')
|
||||
self.driver.get_screenshot_as_file(picture_path)
|
||||
|
||||
def web_set_independent_environment(self):
|
||||
"""
|
||||
| 功能说明: | 设置独立环境 |
|
||||
| 输入参数: | 无 |
|
||||
| 作者信息: | 谢祥益 at 2022.06.01 |
|
||||
"""
|
||||
# 新增独立环境
|
||||
new_loc = ('xpath', '/html/body/div/div[3]/button/div/div/i')
|
||||
# 独立环境名称
|
||||
rule_name_loc = ('id', 'rule-name')
|
||||
# 规则类型
|
||||
rule_type_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[1]/div/div[2]/div[2]/div[2]/div[3]/div')
|
||||
# 头名称
|
||||
rule_header_name_loc = ('id', 'rule-headerName')
|
||||
# 头内容
|
||||
rule_header_value_loc = ('id', 'rule-headerValue')
|
||||
# 保存
|
||||
create_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[2]/div[2]/div[2]/button/div/div')
|
||||
|
||||
self.driver.get("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html")
|
||||
|
||||
if self.jira_id and self.jira_id.lower()!='qa':
|
||||
rule_header_name = 'huohua-podenv'
|
||||
rule_header_value = self.jira_id
|
||||
self.web_click_element(new_loc)
|
||||
self.web_send_keys(rule_name_loc, self.jira_id)
|
||||
self.web_click_element(rule_type_loc)
|
||||
self.web_send_keys(rule_header_name_loc, rule_header_name)
|
||||
self.web_send_keys(rule_header_value_loc, rule_header_value)
|
||||
self.web_click_element(create_loc)
|
||||
rule_header_name = 'huohua-feature'
|
||||
rule_header_value = self.feature
|
||||
self.web_click_element(new_loc)
|
||||
self.web_send_keys(rule_name_loc, "front")
|
||||
self.web_click_element(rule_type_loc)
|
||||
self.web_send_keys(rule_header_name_loc, rule_header_name)
|
||||
self.web_send_keys(rule_header_value_loc, rule_header_value)
|
||||
self.web_click_element(create_loc)
|
||||
else:
|
||||
print("无独立环境!")
|
||||
|
||||
def web_mouse_move_to_element(self, locator):
|
||||
"""
|
||||
| 功能 | 移动鼠标到元素位置 |
|
||||
| 入参 | locator | 元素xpath路径 |
|
||||
| 说明 | 多次刷新页面 | 容易导致此方法失效,慎用... |
|
||||
| | 必要时 | 可以尝试直接点击对应控件:web_click_element |
|
||||
"""
|
||||
self.action.reset_actions()
|
||||
obj = self.web_find_element(locator=locator)
|
||||
self.action.move_to_element(obj)
|
||||
self.action.perform()
|
||||
|
||||
def web_mouse_click(self):
|
||||
"""功能:模拟鼠标点击"""
|
||||
self.action.click().perform()
|
||||
# self.action.perform()
|
||||
|
||||
def web_check_element_exist(self, locator, quantity=1):
|
||||
"""
|
||||
| 功能说明: | 检查页面元素是否存在:0-不存在,1-存在,>1-存在多少个,-1-仅判断存在不判断数量 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | quantity | 对应locator的元素有多少个, 传入-1则仅检查存在,不判断数量 |
|
||||
| 作者信息: | 吴勇刚 | 2022.08.01 |
|
||||
"""
|
||||
if int(quantity) == 0: # 当检查元素不存在时,设计设置为2秒
|
||||
time_out = 2
|
||||
else:
|
||||
time_out = DEFAULT_TIMEOUT
|
||||
elements = self.web_find_elements(locator=locator, raise_exception=False, timeout=time_out)
|
||||
if quantity != -1:
|
||||
if len(elements) != int(quantity):
|
||||
raise Exception("the element [{}] expect exist {},but current is {}..."
|
||||
.format(locator, quantity, len(elements)))
|
||||
else:
|
||||
if len(elements) == 0:
|
||||
raise Exception("the element is not exist:{}...".format(locator))
|
||||
|
||||
def web_check_element_text(self, locator, exp_text):
|
||||
"""
|
||||
| 功能说明: | 检查源文本是否正确 | 转为字符串比较 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | exp_text | 元素预期文本 |
|
||||
| 作者信息: | 吴勇刚 | 2022.08.01 |
|
||||
"""
|
||||
act_text = self.web_get_element_text(locator=locator)
|
||||
if act_text != str(exp_text):
|
||||
raise Exception("The element [{}]'s text expect [{}],bug now it's [{}] "
|
||||
.format(locator, exp_text, act_text))
|
||||
|
||||
def web_check_element_attribute(self, locator, attr_name, exp_value):
|
||||
"""
|
||||
| 功能说明: | 检查元素属性值是否正确 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | attr_name | 元素属性名称 |
|
||||
| | exp_value | 元素预期属性值 |
|
||||
| 作者信息: | 吴勇刚 | 2022.08.01 |
|
||||
"""
|
||||
act_value = self.web_get_element_attribute_value(locator=locator, attr=attr_name)
|
||||
if act_value != exp_value:
|
||||
raise Exception("The element [{}] attribute [{}]'s value expect [{}],bug now it's [{}] "
|
||||
.format(locator, attr_name, exp_value, act_value))
|
||||
|
||||
def web_get_current_url(self):
|
||||
"""
|
||||
| 功能说明: | 检查源文本是否正确 | 转为字符串比较 |
|
||||
| 输入参数: | locator | 元素路径 |
|
||||
| | exp_text | 元素预期文本 |
|
||||
| 作者信息: | 吴勇刚 | 2022.08.01 |
|
||||
"""
|
||||
current_url = self.driver.current_url
|
||||
return current_url
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
web_driver = SeleniumWebUI()
|
||||
vispark_url = "https://sparkle.qa.huohua.cn/intl/schedule"
|
||||
web_driver.web_open_url(url=vispark_url)
|
||||
url = web_driver.web_get_current_url()
|
||||
print(url)
|
||||
web_driver.web_close_browser()
|
||||
727
base_framework/public_tools/sqlhelper.py
Normal file
727
base_framework/public_tools/sqlhelper.py
Normal file
@@ -0,0 +1,727 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
|
||||
"""
|
||||
Author: qiaoxinjiu
|
||||
Create Data: 2020/11/6 17:30
|
||||
"""
|
||||
import time
|
||||
import re
|
||||
from retrying import retry
|
||||
from base_framework.public_tools import log
|
||||
from base_framework.public_tools.db_dbutils_init import get_my_connection,get_pg_connection
|
||||
from base_framework.public_tools.read_config import get_current_config, get_current_env
|
||||
from base_framework.public_tools.huohua_dbs import HuoHuaDBS
|
||||
obj_log = log.get_logger()
|
||||
|
||||
|
||||
"""执行语句查询有结果返回结果没有返回0;增/删/改返回变更数据条数,没有返回0"""
|
||||
# retry 参数说明
|
||||
"""
|
||||
stop_max_attempt_number 指定重试的次数,默认是5
|
||||
stop_max_delay 指定重试超时时间,默认是100,单位是ms
|
||||
wait_fixed 指定每次重试的时间间隔,默认是1000,单位是ms
|
||||
wait_random_min=None 指定每次重试最小时间,默认为0
|
||||
wait_random_max=None, 指定每次重试最大时间,默认为1000
|
||||
wait_incrementing_start 指定每次重试递增的开始时间
|
||||
wait_incrementing_increment 指定每次重试时间的递增幅度
|
||||
wait_exponential_multiplier 指定每次重试时间的递增幅度,跟上面的参数算法差异
|
||||
wait_exponential_max 指定每次重试时间的递增幅度的最大值
|
||||
retry_on_exception 指定每次出现异常执行的函数
|
||||
retry_on_result 指定程序运行正常执行的函数
|
||||
wrap_exception 出现异常后返回的异常类型,True是返回原异常,False返回RetryError
|
||||
stop_func 自定义停止重试的条件,传入参数是一个函数
|
||||
wait_func 自定义每次重试的时间间隔,传入参数是一个函数
|
||||
wait_jitter_max 指定每次重试时间抖动值
|
||||
stop 指定重试停止后,执行Retrying对象的成员函数
|
||||
wait 指定重试间隔,执行Retrying对象的成员函数
|
||||
"""
|
||||
|
||||
|
||||
class MySqLHelper:
|
||||
"""
|
||||
mysql数据库操作
|
||||
"""
|
||||
def __init__(self):
|
||||
self.db = get_my_connection() # 从数据池中获取连接
|
||||
self.current_business = get_current_config(section='run_evn_name', key='current_business')
|
||||
self.current_evn = get_current_env()
|
||||
self.qa_db_to_sim_instance_name = {}
|
||||
self.sim_dbs = HuoHuaDBS()
|
||||
|
||||
# def __new__(cls, *args, **kwargs):
|
||||
# if not hasattr(cls, 'inst'): # 单例
|
||||
# cls.inst = super(MySqLHelper, cls).__new__(cls, *args, **kwargs)
|
||||
# return cls.inst
|
||||
|
||||
|
||||
# 封装执行命令
|
||||
def execute(self, sql, param=None, auto_close=False, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 执行具体的sql语句 |
|
||||
| 输入参数: | sql | 待执行的sql语句 |
|
||||
| | param=None | sql语句中where后跟的参数,也可直接写在sql语句中 |
|
||||
| | auto_close=True | 是否自动关闭数据库连接,默认:自动关闭 |
|
||||
| 返回参数: | conn,cursor,count | 连接,游标,行数 |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
raise Exception("SIM环境请直接使用huohua_dbs.py中的函数")
|
||||
|
||||
# if 'sparkatp' in sql or 'push_service' in sql : # 自动化和信息化的数据都走huohua
|
||||
# choose_db = 'hh.qa'
|
||||
# if "account." in sql or "emp." in sql or "sso." in sql:
|
||||
# choose_db = 'hh.qa'
|
||||
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db) # 从连接池获取连接
|
||||
try:
|
||||
# count : 为改变的数据条数
|
||||
if param:
|
||||
count = cursor.execute(sql, param)
|
||||
else:
|
||||
count = cursor.execute(sql)
|
||||
conn.commit()
|
||||
if auto_close:
|
||||
self.close(cursor, conn)
|
||||
except Exception as e:
|
||||
obj_log.error(e)
|
||||
raise ValueError("数据库操作失败,SQL语句:{}".format(sql))
|
||||
return cursor, conn, count
|
||||
|
||||
# 释放连接
|
||||
@staticmethod
|
||||
def close(cursor, conn):
|
||||
"""
|
||||
| 功能说明: | 关闭数据库连接 |
|
||||
| 输入参数: | cursor | 游标 |
|
||||
| | conn | 连接 |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
"""
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
# 查询所有
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def select_all(self, sql, param=None, choose_db=None, show_log=True):
|
||||
"""
|
||||
| 功能说明: | 查询数据库 | 并返回所有结果 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | 查询语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | 所有查询结果 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_select(sql_content=sql)
|
||||
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
if show_log:
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
res = cursor.fetchall()
|
||||
if show_log:
|
||||
obj_log.info('数据库查询结果:{}'.format(res))
|
||||
return res
|
||||
except Exception as e:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
def select_all_as_list(self, sql, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 查询数据库 | 功能同select_all,仅返回结果为list |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| 返回参数: | 所有查询结果,类型:list | |
|
||||
| 作者信息: | 吴勇刚 | 2021/11/22 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_select(sql_content=sql, r_type='list')
|
||||
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, choose_db=choose_db)
|
||||
res = cursor.fetchall()
|
||||
has_data = False # 以下代码是防止返回空值列表添加的
|
||||
for item in res:
|
||||
for key in item:
|
||||
if item[key]:
|
||||
has_data = True # 有一个值非空即可
|
||||
break
|
||||
if not has_data:
|
||||
return [] # 以上代码是防止返回空值列表添加的
|
||||
res_list = []
|
||||
for index in range(len(res)):
|
||||
if len(res[index]) > 1:
|
||||
res_row = []
|
||||
for key in res[index]:
|
||||
res_row.append(res[index][key])
|
||||
res_list.append(res_row)
|
||||
else:
|
||||
for key in res[index]:
|
||||
res_list.append(res[index][key])
|
||||
return res_list
|
||||
except Exception as e:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 查询单条
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def select_one(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 查询数据库,并返第一行 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | 查询语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | 返回第一条查询结果 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
if 'limit' not in sql.lower():
|
||||
sql = sql.split(';')[0] + ' limit 1;'
|
||||
sim_data = self.sim_dbs.dbs_select(sql_content=sql)
|
||||
if sim_data:
|
||||
return sim_data[0]
|
||||
else:
|
||||
return {}
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
res = cursor.fetchone()
|
||||
return res
|
||||
except Exception as e:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 增加
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def insert_one(self, sql, param=None, choose_db=None, log_level='info'):
|
||||
"""
|
||||
| 功能说明: | insert一行数据 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| | log_level='info' | 日志级别:info-全量日志,否则,仅输入报错日志 |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if log_level.lower() == 'info':
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
# _id = cursor.lastrowid() # 获取当前插入数据的主键id,该id应该为自动生成为好
|
||||
conn.commit()
|
||||
self.close(cursor, conn)
|
||||
if log_level.lower() == 'info':
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return count
|
||||
# 防止表中没有id返回0
|
||||
# if _id == 0:
|
||||
# return True
|
||||
# return _id
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 插入后返回插入ID
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def insert_many_extension(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert一行数据,并返回插入行的主键ID |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | insert_count | 插入的条数 |
|
||||
| last_id | 插入最近一条的ID |
|
||||
| insert_id | 插入的第一条数据的主键ID(参数param的的第一条) |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
return_dict = dict()
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db)
|
||||
try:
|
||||
count = cursor.executemany(sql, eval(str(param)))
|
||||
# last_id = cursor.lastrowid # 获取最新插入数据的主键id
|
||||
insert_id = cursor._result.insert_id # 获取本次插入数据的主键id
|
||||
# insert_id_list = list(range(insert_id, insert_id+count)) # 获取最新插入数据的主键id_list
|
||||
conn.commit()
|
||||
self.close(cursor, conn)
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return_dict['insert_count'] = count
|
||||
# return_dict['last_id'] = last_id
|
||||
return_dict['insert_id'] = insert_id
|
||||
return return_dict
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 插入后返回插入ID
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def insert_one_extension(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert一行数据,并返回插入行的ID |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | insert_count | 插入的条数 |
|
||||
| last_id | 插入最近一条的ID |
|
||||
| insert_id | 插入数据的ID |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
return_dict = dict()
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
# last_id = cursor.lastrowid # 获取最新插入数据的主键id
|
||||
insert_id = cursor._result.insert_id # 获取本次插入数据的主键id
|
||||
# insert_id_list = list(range(insert_id, insert_id+count)) # 获取最新插入数据的主键id_list
|
||||
conn.commit()
|
||||
self.close(cursor, conn)
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return_dict['insert_count'] = count
|
||||
# return_dict['last_id'] = last_id
|
||||
return_dict['insert_id'] = insert_id
|
||||
return return_dict
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 增加多行
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def insert_many(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | insert多行数据 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,必须是元组或列表[(),()]或((),()) |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
cursor, conn = self.db.getconn(choose_db=choose_db)
|
||||
try:
|
||||
count = cursor.executemany(sql, eval(str(param)))
|
||||
conn.commit()
|
||||
self.close(cursor, conn)
|
||||
obj_log.info('插入数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 删除
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def delete(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 删除数据库记录 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
self.close(cursor, conn)
|
||||
obj_log.info('删除数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
# 更新
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def update(self, sql, param=None, choose_db=None):
|
||||
"""
|
||||
| 功能说明: | 更新数据库记录 |
|
||||
| 输入参数: | sql | 待执行的查询语句 |
|
||||
| | param=None | sql语句中where条件后的参数,也可直接写入sql语句中 |
|
||||
| 返回参数: | 无 | |
|
||||
| 作者信息: | 林于棚 | 2020/11/26 21:11 |
|
||||
| 函数位置: | Public/Common/mysql_api.py ||
|
||||
"""
|
||||
if self.current_evn.lower() == "sim":
|
||||
return self.sim_dbs.dbs_execute_sql(sql_content=sql)
|
||||
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
try:
|
||||
cursor, conn, count = self.execute(sql, param, choose_db=choose_db)
|
||||
conn.commit()
|
||||
self.close(cursor, conn)
|
||||
obj_log.info('更新数据库条数:{}'.format(count))
|
||||
return count
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
def check_result_exist(self, *select_statement, retry_count=3):
|
||||
"""
|
||||
| 功能说明: | 验证select语句查询的结果存在 |
|
||||
| 输入参数: | select_statement | select查询语句,可以多个 |
|
||||
| | retry_count | 重试次数,默认3次 |
|
||||
| 返回参数: | 无 | 结果存在则pass,否则failed |
|
||||
| 作者信息: | 吴勇刚 | 2020.12.16 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
举例说明:
|
||||
| check_result_exist | [sql1,sql2] |
|
||||
"""
|
||||
# retry_count = 3
|
||||
start = 0
|
||||
flag = 0
|
||||
while start <= retry_count:
|
||||
for sql in select_statement:
|
||||
res = self.select_all(sql=sql)
|
||||
if not res:
|
||||
if start == retry_count:
|
||||
obj_log.info('the result is not exist,but expect at least one')
|
||||
raise RuntimeError(u'No results, when exec sql: %s' % sql)
|
||||
else:
|
||||
obj_log.info('the result is not exist,retry {}'.format(start+1))
|
||||
start += 1
|
||||
time.sleep(1)
|
||||
else:
|
||||
obj_log.info('find [{0}] results when exec :{1}'.format(len(res), sql))
|
||||
flag += 1
|
||||
break
|
||||
if flag > 0:
|
||||
break
|
||||
|
||||
def check_result_not_exist(self, *select_statement):
|
||||
"""
|
||||
| 功能说明: | 验证select语句查询的结果不存在 |
|
||||
| 输入参数: | select_statement | select查询语句列表 |
|
||||
| 返回参数: | 无 | 结果不存在则pass,否则failed |
|
||||
| 作者信息: | 吴勇刚 | 2020.12.16 |
|
||||
函数位置:Public/Common/mysql_api.py
|
||||
举例说明:
|
||||
| check_result_not_exist | [sql1,sql2] |
|
||||
"""
|
||||
for sql in select_statement:
|
||||
res = self.select_all(sql=sql)
|
||||
if res:
|
||||
obj_log.info('find [{0}] results, but expect 0'.format(len(res)))
|
||||
raise RuntimeError(u'the result exist, when exec sql: %s' % sql)
|
||||
else:
|
||||
obj_log.info('the result is not exist, this step pass...')
|
||||
|
||||
def row_count(self, selectStatement, param=None):
|
||||
"""
|
||||
Uses the input `selectStatement` to query the database and returns the number of rows from the query.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
| 2 | Jerry | Schneider |
|
||||
|
||||
When you do the following:
|
||||
| ${rowCount} | Row Count | SELECT * FROM person |
|
||||
| Log | ${rowCount} |
|
||||
|
||||
You will get the following:
|
||||
2
|
||||
|
||||
Also, you can do something like this:
|
||||
| ${rowCount} | Row Count | SELECT * FROM person WHERE id = 2 |
|
||||
| Log | ${rowCount} |
|
||||
|
||||
And get the following
|
||||
1
|
||||
"""
|
||||
try:
|
||||
cursor, conn, count = self.execute(selectStatement, param)
|
||||
self.close(cursor, conn)
|
||||
return count
|
||||
except Exception as e:
|
||||
print("error_msg:", e.args)
|
||||
self.close(cursor, conn)
|
||||
|
||||
# 数据库断言
|
||||
def kw_check_if_exists_in_database(self, selectStatement, choose_db=None):
|
||||
"""
|
||||
Check if any row would be returned by given the input `selectStatement`. If there are no results, then this will
|
||||
throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit transaction
|
||||
commit or rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' |
|
||||
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' |
|
||||
|
||||
Then you will get the following:
|
||||
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # PASS |
|
||||
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # FAIL |
|
||||
|
||||
"""
|
||||
obj_log.info(
|
||||
'Executing : Check If Exists In Database | %s ' %
|
||||
selectStatement)
|
||||
if not self.select_one(selectStatement, choose_db=choose_db):
|
||||
raise AssertionError("Expected to have have at least one row from '%s' "
|
||||
"but got 0 rows." % selectStatement)
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_check_if_not_exists_in_database(self, selectStatement, choose_db=None):
|
||||
"""
|
||||
This is the negation of `check_if_exists_in_database`.
|
||||
|
||||
Check if no rows would be returned by given the input `selectStatement`. If there are any results, then this
|
||||
will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit
|
||||
transaction commit or rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' |
|
||||
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' |
|
||||
|
||||
Then you will get the following:
|
||||
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | # PASS |
|
||||
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL |
|
||||
|
||||
"""
|
||||
obj_log.info(
|
||||
'Executing : Check If Not Exists In Database | %s ' %
|
||||
selectStatement)
|
||||
queryResults = self.select_one(selectStatement, choose_db=choose_db)
|
||||
if queryResults:
|
||||
raise AssertionError("Expected to have have no rows from '%s' "
|
||||
"but got some rows : %s." % (selectStatement, queryResults))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_0(self, selectStatement):
|
||||
"""
|
||||
Check if any rows are returned from the submitted `selectStatement`. If there are, then this will throw an
|
||||
AssertionError. Set optional input `sansTran` to True to run command without an explicit transaction commit or
|
||||
rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' |
|
||||
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' |
|
||||
|
||||
Then you will get the following:
|
||||
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' | # FAIL |
|
||||
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | # PASS |
|
||||
"""
|
||||
obj_log.info('Executing : Row Count Is 0 | %s ' % selectStatement)
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows > 0:
|
||||
raise AssertionError("Expected zero rows to be returned from '%s' "
|
||||
"but got rows back. Number of rows returned was %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_equal_to_x(self, selectStatement, numRows):
|
||||
"""
|
||||
Check if the number of rows returned from `selectStatement` is equal to the value submitted. If not, then this
|
||||
will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit
|
||||
transaction commit or rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
| 2 | Jerry | Schneider |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Row Count Is Equal To X | SELECT id FROM person | 1 |
|
||||
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 |
|
||||
|
||||
Then you will get the following:
|
||||
| Row Count Is Equal To X | SELECT id FROM person | 1 | # FAIL |
|
||||
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | # PASS |
|
||||
"""
|
||||
obj_log.info(
|
||||
'Executing : Row Count Is Equal To X | %s | %s ' %
|
||||
(selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows != int(str(numRows).encode('ascii')):
|
||||
raise AssertionError("Expected same number of rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_greater_than_x(self, selectStatement, numRows):
|
||||
"""
|
||||
Check if the number of rows returned from `selectStatement` is greater than the value submitted. If not, then
|
||||
this will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit
|
||||
transaction commit or rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
| 2 | Jerry | Schneider |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Row Count Is Greater Than X | SELECT id FROM person | 1 |
|
||||
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 |
|
||||
|
||||
Then you will get the following:
|
||||
| Row Count Is Greater Than X | SELECT id FROM person | 1 | # PASS |
|
||||
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | # FAIL |
|
||||
"""
|
||||
obj_log.info(
|
||||
'Executing : Row Count Is Greater Than X | %s | %s ' %
|
||||
(selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows <= int(numRows.encode('ascii')):
|
||||
raise AssertionError("Expected more rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_row_count_is_less_than_x(self, selectStatement, numRows):
|
||||
"""
|
||||
Check if the number of rows returned from `selectStatement` is less than the value submitted. If not, then this
|
||||
will throw an AssertionError. Set optional input `sansTran` to True to run command without an explicit
|
||||
transaction commit or rollback.
|
||||
|
||||
For example, given we have a table `person` with the following data:
|
||||
| id | first_name | last_name |
|
||||
| 1 | Franz Allan | See |
|
||||
| 2 | Jerry | Schneider |
|
||||
|
||||
When you have the following assertions in your robot
|
||||
| Row Count Is Less Than X | SELECT id FROM person | 3 |
|
||||
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 |
|
||||
|
||||
Then you will get the following:
|
||||
| Row Count Is Less Than X | SELECT id FROM person | 3 | # PASS |
|
||||
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 | # FAIL |
|
||||
"""
|
||||
obj_log.info(
|
||||
'Executing : Row Count Is Less Than X | %s | %s ' %
|
||||
(selectStatement, numRows))
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows >= int(numRows.encode('ascii')):
|
||||
raise AssertionError("Expected less rows to be returned from '%s' "
|
||||
"than the returned rows of %s" % (selectStatement, num_rows))
|
||||
else:
|
||||
return True
|
||||
|
||||
def kw_table_must_exist(self, tableName):
|
||||
"""
|
||||
Check if the table given exists in the database. Set optional input `sansTran` to True to run command without an
|
||||
explicit transaction commit or rollback.
|
||||
|
||||
For example, given we have a table `person` in a database
|
||||
|
||||
When you do the following:
|
||||
| Table Must Exist | person |
|
||||
|
||||
Then you will get the following:
|
||||
| Table Must Exist | person | # PASS |
|
||||
| Table Must Exist | first_name | # FAIL |
|
||||
"""
|
||||
obj_log.info('Executing : Table Must Exist | %s ' % tableName)
|
||||
if self.db_api_module_name in ["cx_Oracle"]:
|
||||
selectStatement = (
|
||||
"SELECT * FROM all_objects WHERE object_type IN ('TABLE','VIEW') AND owner = SYS_CONTEXT('USERENV', 'SESSION_USER') AND object_name = UPPER('%s')" %
|
||||
tableName)
|
||||
elif self.db_api_module_name in ["sqlite3"]:
|
||||
selectStatement = (
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name='%s' COLLATE NOCASE" %
|
||||
tableName)
|
||||
elif self.db_api_module_name in ["ibm_db", "ibm_db_dbi"]:
|
||||
selectStatement = (
|
||||
"SELECT name FROM SYSIBM.SYSTABLES WHERE type='T' AND name=UPPER('%s')" %
|
||||
tableName)
|
||||
else:
|
||||
selectStatement = (
|
||||
"SELECT * FROM information_schema.tables WHERE table_name='%s'" %
|
||||
tableName)
|
||||
num_rows = self.row_count(selectStatement)
|
||||
if num_rows == 0:
|
||||
raise AssertionError(
|
||||
"Table '%s' does not exist in the db" %
|
||||
tableName)
|
||||
|
||||
# @retry(stop_max_attempt_number=5, wait_fixed=3000)
|
||||
def select_all_as_generator(self, sql, param=None, choose_db=None):
|
||||
if param is not None:
|
||||
obj_log.info('SQL语句:{}|{}'.format(sql, param))
|
||||
else:
|
||||
obj_log.info('SQL语句:{}'.format(sql))
|
||||
|
||||
try:
|
||||
cursor, conn, counts = self.execute(sql, param, choose_db=choose_db)
|
||||
for count in range(counts):
|
||||
item = cursor.fetchone()
|
||||
obj_log.info(item)
|
||||
yield item
|
||||
except Exception as e:
|
||||
self.close(cursor, conn)
|
||||
raise RuntimeError(e.args)
|
||||
|
||||
def get_qa_to_sim_dbs(self):
|
||||
get_sql = "select qa_db,sim_instance from sparkatp.qa_db_mapping_sim_dbs where status=1 and is_delete=0"
|
||||
res_info = self.sim_dbs.dbs_query_by_db_name("qadb-slave", "sparkatp", get_sql, limit_num=1000)
|
||||
print(res_info)
|
||||
for item in res_info:
|
||||
self.qa_db_to_sim_instance_name.update({item[0]: item[1]})
|
||||
return self.qa_db_to_sim_instance_name
|
||||
|
||||
def get_instance_name(self, sql_str):
|
||||
db_name = sql_str.split(".")[0].split(" ")[-1].replace("`", "").replace(" ", "")
|
||||
return self.qa_db_to_sim_instance_name.get(db_name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
db = MySqLHelper()
|
||||
# sql = "select id,name,code from peppa.grade_group where course_level in (0, 1, 2, 3) order by id asc;"
|
||||
# sql = " DELETE FROM classes.timetable_plan_teacher WHERE teacher_id in (SELECT id FROM `teach_teacher`.`teacher_profile` WHERE name='666000排课' AND nickname='666000排课');"
|
||||
# print("【原始sql】:{}".format(sql))
|
||||
# res = db.update_db_name(sql=sql)
|
||||
# print(res)
|
||||
sql = "SELECT * FROM account.account;"
|
||||
res = db.select_one(sql=sql)
|
||||
print(res)
|
||||
|
||||
|
||||
1545
base_framework/public_tools/utils.py
Normal file
1545
base_framework/public_tools/utils.py
Normal file
File diff suppressed because it is too large
Load Diff
283
base_framework/public_tools/webdriver.py
Normal file
283
base_framework/public_tools/webdriver.py
Normal file
@@ -0,0 +1,283 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
import os
|
||||
import time
|
||||
import platform
|
||||
from seleniumwire import webdriver
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as ec
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.select import Select
|
||||
from base_framework.public_tools.read_config import get_current_config, ReadConfig, InitConfig
|
||||
from base_framework.base_config.current_pth import env_choose_path
|
||||
from copy import copy, deepcopy
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
cull_path = os.path.dirname(os.path.abspath(__file__))
|
||||
plugs = os.path.abspath(os.path.join(cull_path, '../{}'.format('/platform_tools/plugins/headerenv.crx')))
|
||||
cc_driver = os.path.abspath(os.path.join(cull_path, '../{}'.format('/public_business/CC/webdriver/chromedriver.exe')))
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
|
||||
class DriverInit(InitConfig):
|
||||
def __init__(self, driver_path=None):
|
||||
|
||||
# super(DriverInit, self).__init__()
|
||||
InitConfig.__init__(self, run_user_name=None, current_evn=None)
|
||||
self.driver_path = cc_driver if not driver_path else driver_path
|
||||
self.config = ReadConfig(env_choose_path)
|
||||
self.jira_id = ReadConfig(env_choose_path).get_value('run_jira_id', 'huohua-podenv')
|
||||
self.option = webdriver.ChromeOptions()
|
||||
self.option.add_argument('--no-sandbox')
|
||||
self.option.add_argument('--disable-gpu')
|
||||
self.option.add_argument('--disable-dev-shm-usage')
|
||||
# self.option.headless = True
|
||||
self.option.add_extension(plugs)
|
||||
# self._init_driver()
|
||||
|
||||
def _init_driver(self):
|
||||
self.driver = webdriver.Chrome(executable_path=self.driver_path, options=self.option)
|
||||
self.driver.scopes = [
|
||||
'.*huohua.cn.*', '.*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+.*', '.*allschool.com', '.*sparkedu.com'
|
||||
]
|
||||
self.driver.implicitly_wait(30)
|
||||
self.driver.maximize_window()
|
||||
self.actions = ActionChains(self.driver)
|
||||
self.wait = WebDriverWait(self.driver, timeout=60)
|
||||
if self.jira_id:
|
||||
self._set_independent_environment()
|
||||
|
||||
def _set_independent_environment(self):
|
||||
backend = self.jira_id
|
||||
frontend = 'feature/%s' % self.jira_id
|
||||
new_loc = ('xpath', '/html/body/div/div[3]/button/div/div/i')
|
||||
# 独立环境名称
|
||||
rule_name_loc = ('id', 'rule-name')
|
||||
# 规则类型
|
||||
rule_type_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[1]/div/div[2]/div[2]/div[2]/div[3]/div')
|
||||
# 头名称
|
||||
rule_headerName_loc = ('id', 'rule-headerName')
|
||||
# 头内容
|
||||
rule_headerValue_loc = ('id', 'rule-headerValue')
|
||||
# 保存
|
||||
create_loc = ('xpath', '//*[@id="edit-page"]/div[2]/div[2]/div[2]/div[2]/button/div/div')
|
||||
|
||||
# js = 'window.open("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html");'
|
||||
# self.driver.execute_script(js)
|
||||
self.driver.get("chrome-extension://eningockdidmgiojffjmkdblpjocbhgh/options/options.html")
|
||||
rule_headerName = 'huohua-podenv'
|
||||
rule_headerValue = backend
|
||||
self.clickElement(new_loc)
|
||||
self.sendKeysElement(rule_name_loc, "back")
|
||||
self.clickElement(rule_type_loc)
|
||||
self.sendKeysElement(rule_headerName_loc, rule_headerName)
|
||||
self.sendKeysElement(rule_headerValue_loc, rule_headerValue)
|
||||
self.clickElement(create_loc)
|
||||
rule_headerName = 'huohua-feature'
|
||||
rule_headerValue = frontend
|
||||
self.clickElement(new_loc)
|
||||
self.sendKeysElement(rule_name_loc, "front")
|
||||
self.clickElement(rule_type_loc)
|
||||
self.sendKeysElement(rule_headerName_loc, rule_headerName)
|
||||
self.sendKeysElement(rule_headerValue_loc, rule_headerValue)
|
||||
self.clickElement(create_loc)
|
||||
|
||||
def waitEleDisappear(self, locator, timeout=10):
|
||||
count = 0
|
||||
while count <= timeout:
|
||||
try:
|
||||
self.wait.until(ec.invisibility_of_element_located(locator=locator))
|
||||
break
|
||||
except Exception as error:
|
||||
time.sleep(1)
|
||||
count += 1
|
||||
|
||||
def elementScroolToView(self, locator):
|
||||
ele = self.findElement(locator=locator, timeout=10)
|
||||
self.driver.execute_script("arguments[0].focus()", ele)
|
||||
self.driver.execute_script("arguments[0].scrollIntoView();", ele)
|
||||
|
||||
def findElement(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回第一个元素|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
element = WebDriverWait(self.driver, timeout).until(ec.presence_of_element_located(locator))
|
||||
return element
|
||||
|
||||
def findElements(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回所有元素|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
element = WebDriverWait(self.driver, timeout).until(ec.presence_of_all_elements_located(locator))
|
||||
return element
|
||||
|
||||
def clickElement(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,普通方法点击|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
element = self.findElement(locator, timeout)
|
||||
element.click()
|
||||
|
||||
def clickElement_by_JS(self, locator=None, element=None, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,以JS的方式点击|
|
||||
| 传入参数: | locator | 元素定位器 |
|
||||
| element | 页面对象 |
|
||||
举例说明:
|
||||
"""
|
||||
if not element and locator:
|
||||
obj = self.findElement(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].click();", obj)
|
||||
elif element and not locator:
|
||||
self.driver.execute_script("arguments[0].click();", element)
|
||||
else:
|
||||
raise Exception('不支持的传参方式,locator和element必须且只能传一个')
|
||||
|
||||
def sendKeysElement(self, locator, text, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,清空输入框,写入text|
|
||||
| 传入参数: | locator,text |
|
||||
举例说明:
|
||||
"""
|
||||
element = self.findElement(locator, timeout)
|
||||
element.send_keys(Keys.CONTROL, 'a')
|
||||
element.send_keys(text)
|
||||
|
||||
def getElementText(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,返回该元素的文本值|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
element = self.findElement(locator, timeout)
|
||||
return element.text
|
||||
|
||||
def clickSingleBox(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入单选框元素定位器,定位到该元素,依次点击单选框|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
elements = self.findElements(locator, timeout)
|
||||
for element in elements:
|
||||
self.driver.execute_script("arguments[0].click();", element)
|
||||
time.sleep(1.5)
|
||||
|
||||
def selectDropDownBox(self, locator, index=0, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入下拉框定位器,定位到该元素,选择下拉框的第index个选项|
|
||||
| 传入参数: | locator,index |
|
||||
举例说明:
|
||||
"""
|
||||
select = Select(self.findElement(locator, timeout))
|
||||
select.select_by_index(index)
|
||||
|
||||
# 选择多选框-->全选
|
||||
def clickCheckbox(self, locator, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入多选框元素定位器,定位到该元素,全选|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
checkbox = self.findElements(locator, timeout)
|
||||
for i in checkbox:
|
||||
if not i.is_selected():
|
||||
i.click()
|
||||
|
||||
def uploadFile(self, locator, path, timeout=10):
|
||||
"""
|
||||
| 功能说明: | 传入元素定位器,定位到该元素,传入文件路径,只适用于input标签|
|
||||
| 传入参数: | locator |
|
||||
举例说明:
|
||||
"""
|
||||
element = self.findElement(locator, timeout)
|
||||
element.send_keys(path)
|
||||
|
||||
def roll_windows(self, x=0, y=0):
|
||||
"""
|
||||
滑动窗口
|
||||
:param x: x轴距离
|
||||
:param y: y轴距离
|
||||
:return: 无
|
||||
"""
|
||||
self.driver.execute_script("window.scrollBy({},{})".format(x, y))
|
||||
|
||||
def back_window(self):
|
||||
"""
|
||||
返回上一页(浏览器工具栏向左箭头)
|
||||
:return: 无
|
||||
"""
|
||||
self.driver.back()
|
||||
|
||||
def forward_window(self):
|
||||
"""
|
||||
前进一页(浏览器工具栏向右箭头)
|
||||
:return: 无
|
||||
"""
|
||||
self.driver.forward()
|
||||
|
||||
def close_current_window(self):
|
||||
"""
|
||||
关闭当前窗口
|
||||
:return: 无
|
||||
"""
|
||||
self.driver.close()
|
||||
|
||||
def get_window_title(self):
|
||||
"""
|
||||
关闭当前窗口
|
||||
:return: 无
|
||||
"""
|
||||
current_title = self.driver.title
|
||||
return current_title
|
||||
|
||||
def get_element_attribute_value(self, locator, attr: str, timeout=10):
|
||||
"""
|
||||
关闭当前窗口
|
||||
:param locator 定位器
|
||||
:param attr 属性名
|
||||
:param timeout 元素查找超时时间
|
||||
:return: 无
|
||||
"""
|
||||
obj = self.findElement(locator, timeout=timeout)
|
||||
attr_value = obj.get_attribute(attr)
|
||||
return attr_value
|
||||
|
||||
def modify_tag_attribution(self, locator, attr, value, timeout=10):
|
||||
"""
|
||||
修改页签属性
|
||||
:param locator: 需要修改的元素定位器
|
||||
:param attr: 修改属性名
|
||||
:param value: 修改后的值
|
||||
:param timeout: 超时时间
|
||||
:return:
|
||||
"""
|
||||
obj = self.findElement(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].{attr}={value};".format(attr=attr, value=value), obj)
|
||||
|
||||
def input_to_readonly_tag(self, locator, text, timeout=10):
|
||||
"""
|
||||
带只读属性的标签输入
|
||||
:param locator: 元素定位器
|
||||
:param text: 输入内容
|
||||
:param timeout: 超时时间
|
||||
:return:无
|
||||
"""
|
||||
obj = self.findElement(locator, timeout)
|
||||
self.driver.execute_script("arguments[0].removeAttribute('readonly');", obj)
|
||||
self.sendKeysElement(locator=locator, text=text)
|
||||
|
||||
def takeScreenshot(self, savePath, pictureName):
|
||||
"""
|
||||
| 功能说明: | 截取浏览器当前页面|
|
||||
| 传入参数: | savePath:保存地址 |
|
||||
| | pictureName:图片保存名称
|
||||
举例说明:
|
||||
"""
|
||||
picturePath = os.path.join(savePath, pictureName + '.png')
|
||||
self.driver.get_screenshot_as_file(picturePath)
|
||||
117
base_framework/public_tools/websocket_api.py
Normal file
117
base_framework/public_tools/websocket_api.py
Normal file
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
# @Author : qiaoxinjiu
|
||||
# @Time : 2021/07/13
|
||||
# @File : websocket_api.py
|
||||
|
||||
import time
|
||||
import json
|
||||
from functools import wraps
|
||||
import requests
|
||||
from websocket import create_connection
|
||||
from base_framework.public_tools.log import get_logger
|
||||
from base_framework.public_tools.sqlhelper import MySqLHelper
|
||||
from base_framework.public_tools.read_config import InitConfig
|
||||
|
||||
|
||||
def check_conn(func):
|
||||
@wraps(func)
|
||||
def wrap_check_conn(self, *args, **kwargs):
|
||||
self.ws = self.get_ws_conn()
|
||||
self.logger.info('%s connected websocket server' % func.__name__)
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return wrap_check_conn
|
||||
|
||||
|
||||
class WebSocketAPI(InitConfig):
|
||||
def __init__(self, user_name=None, env_name=None, timeout=30):
|
||||
# super().__init__(run_user_name=user_name, current_evn=env_name)
|
||||
InitConfig.__init__(self, run_user_name=user_name, current_evn=env_name)
|
||||
self.sso_url = self.all_cfg[self.current_evn]['sso_url']
|
||||
self.code_url = self.all_cfg[self.current_evn]['code_url']
|
||||
self.token_url = self.all_cfg[self.current_evn]['token_url']
|
||||
self.redirect_url = self.all_cfg[self.current_evn]['teach_opt_url']
|
||||
self.auth_code = self.all_cfg['Authorization']['sparkle-manage']
|
||||
self.logger = get_logger()
|
||||
self.db_conn = MySqLHelper()
|
||||
|
||||
def get_session(self):
|
||||
session = requests.Session()
|
||||
session.headers.update({'Authorization': self.auth_code})
|
||||
if not hasattr(self, 'access_token'):
|
||||
self.access_token = self._get_access_token()
|
||||
session.headers.update({'accesstoken': self.access_token})
|
||||
return session
|
||||
|
||||
def _get_access_token(self):
|
||||
token_session = requests.Session()
|
||||
post_data = {'showUsername': self.show_username, 'username': self.username, 'password': self.password}
|
||||
resp1 = token_session.post(self.sso_url, data=post_data, allow_redirects=False)
|
||||
assert resp1.status_code == 302, 'incorrect response code %s' % resp1.status_code
|
||||
get_data = {'client_id': 'tm-manage', 'response_type': 'code',
|
||||
'redirect_uri': self.redirect_url}
|
||||
resp2 = token_session.get(self.code_url, params=get_data, allow_redirects=False)
|
||||
assert resp2.status_code == 302, 'incorrect response code %s' % resp2.status_code
|
||||
tmp = resp2.headers
|
||||
code = tmp.get('Location').split('=')[1]
|
||||
post_data2 = {'grant_type': 'authorization_code', 'code': code, 'redirect_uri': self.redirect_url}
|
||||
token_session.headers.update({'Authorization': self.auth_code})
|
||||
resp3 = token_session.post(self.token_url, data=post_data2)
|
||||
token = json.loads(resp3.text).get('access_token')
|
||||
return token
|
||||
|
||||
def get_ws_conn(self, timeout=60):
|
||||
self.logger.info('websocket connecting...')
|
||||
self.access_token = self._get_access_token()
|
||||
uri = self.all_cfg[self.current_evn]['websocket_server']
|
||||
if 'accesstoken' in uri:
|
||||
self.uri = uri
|
||||
else:
|
||||
self.uri = uri + '/?accesstoken=%s' % self.access_token if uri.endswith(
|
||||
'/') else uri + '/?accesstoken=%s' % self.access_token
|
||||
ws_conn = create_connection(self.uri, timeout=timeout)
|
||||
if ws_conn.getstatus() == 101:
|
||||
return ws_conn
|
||||
else:
|
||||
raise Exception('connect websocket server failed!')
|
||||
|
||||
def _close_ws(self):
|
||||
if hasattr(self, 'ws'):
|
||||
self.ws.close()
|
||||
self.logger.info('connection closed success!')
|
||||
|
||||
@check_conn
|
||||
def send_msg_and_get_response(self, send_data: [dict, str], timeout=3):
|
||||
if isinstance(send_data, dict):
|
||||
data = json.dumps(send_data)
|
||||
self.ws.send(data)
|
||||
self.logger.info(">>> heart beat!")
|
||||
self.logger.info(">>> send message:%s" % data)
|
||||
return self.get_response(ws_conn=self.ws, tm_stamp=send_data.get("timestamp"), timeout=timeout)
|
||||
elif isinstance(send_data, str):
|
||||
data = send_data
|
||||
self.ws.send(data)
|
||||
self.logger.info(">>> heart beat!")
|
||||
self.logger.info(">>> send message:%s" % data)
|
||||
return self.get_response(ws_conn=self.ws, tm_stamp=json.loads(send_data).get("timestamp"), timeout=timeout)
|
||||
else:
|
||||
raise Exception('send_data can only support dict or str type')
|
||||
|
||||
def get_response(self, ws_conn, tm_stamp=None, timeout=3):
|
||||
t0 = time.time()
|
||||
while time.time() - t0 <= int(timeout):
|
||||
time.sleep(0.2)
|
||||
out_put = ws_conn.recv()
|
||||
if tm_stamp:
|
||||
if str(tm_stamp) in out_put:
|
||||
self.logger.info(">>> received message:%s" % out_put)
|
||||
return json.loads(out_put)
|
||||
elif out_put:
|
||||
return json.loads(out_put)
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
self._close_ws()
|
||||
raise Exception('>>> received no response, please check ws connect!')
|
||||
39
base_framework/public_tools/xml_file_api.py
Normal file
39
base_framework/public_tools/xml_file_api.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 功能:对xml文件的操作函数
|
||||
|
||||
import os
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class XmlFileApi:
|
||||
def __init__(self, file_path):
|
||||
self.file_path = file_path
|
||||
self.root = ET.parse(file_path).getroot()
|
||||
|
||||
def get_contain_by_tag_names(self, tag_names):
|
||||
"""
|
||||
功能:根据多级标签名查询xml文件中的内容
|
||||
Args:
|
||||
tag_names: 多级标签名,从根目录开始,中间用/分隔,例如:statistics/total/stat
|
||||
Returns:
|
||||
对应标签名的内容,格式:[{"text": xxx, "attrib": yyy}]
|
||||
"""
|
||||
# for child in self.root:
|
||||
# print(child.tag, child.attrib)
|
||||
elements = self.root.findall('.//' + tag_names)
|
||||
if len(elements) == 0:
|
||||
print("根据标签:{},未找到对应的内容,请检查标签名是否正确".format(tag_names))
|
||||
return
|
||||
return_data = []
|
||||
for element in elements:
|
||||
return_data.append({"text": element.text, "attrib": element.attrib})
|
||||
return return_data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
xf = XmlFileApi(file_path="C:/Users/17447/Downloads/output_gue_it.xml")
|
||||
xf.get_contain_by_tag_names(tag_names='statistics/total/stat')
|
||||
|
||||
# C:/Users/17447/Downloads/output_gue_it.xml
|
||||
# C:/Users/17447/Downloads/output_gue_it.xml
|
||||
70
base_framework/startup.py
Normal file
70
base_framework/startup.py
Normal file
@@ -0,0 +1,70 @@
|
||||
# coding: utf-8
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import configparser
|
||||
# 这几项添加路径,必须放前面,否则后续import会报错
|
||||
# 获取组名
|
||||
input_team_name = sys.argv
|
||||
try:
|
||||
team_index = input_team_name.index('-t')
|
||||
except ValueError:
|
||||
raise Exception("组名是必填参数,请设置....")
|
||||
else:
|
||||
TEAM_NAME = input_team_name[team_index+1]
|
||||
BASIC_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
TEAM_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../{}/'.format(TEAM_NAME)))
|
||||
sys.path.append(TEAM_PATH)
|
||||
PROJECT_PATH = os.path.abspath(os.path.join(BASIC_PATH, '../'))
|
||||
sys.path.append(PROJECT_PATH)
|
||||
env_choose_path = os.path.join(BASIC_PATH, 'base_config', 'env_choose.ini')
|
||||
# 获取业务标示:hh or hhi
|
||||
try:
|
||||
current_business_index = input_team_name.index('-b')
|
||||
current_business = input_team_name[current_business_index + 1]
|
||||
except ValueError:
|
||||
current_business = "hh"
|
||||
# 获取环境 QA or SIM
|
||||
try:
|
||||
current_evn_index = input_team_name.index('-e')
|
||||
current_evn = input_team_name[current_evn_index + 1]
|
||||
except ValueError:
|
||||
current_evn = "QA"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Choose current environment")
|
||||
parser.add_argument("-b", dest='business', help="hh or hhi", default='hh')
|
||||
parser.add_argument("-e", dest='env', help="QA or SIM", default='QA')
|
||||
parser.add_argument("-u", dest='user', help="user", default='lrq')
|
||||
parser.add_argument("-j", dest='jira_id', help="jira_id", default='')
|
||||
parser.add_argument("-t", dest='team_name', help="team_name", default='')
|
||||
args = parser.parse_args()
|
||||
# choose_env = args.env
|
||||
choose_user = args.user
|
||||
# jira_id = args.jira_id
|
||||
business = args.business
|
||||
jira_id = args.jira_id
|
||||
if jira_id.lower() == 'sim':
|
||||
args.env = 'SIM'
|
||||
choose_env = args.env
|
||||
|
||||
# 将命令行参数写入env_choose.ini文件中
|
||||
cof = configparser.ConfigParser()
|
||||
cof.read(env_choose_path, encoding='utf-8')
|
||||
cof.set(section="run_evn_name", option="current_business", value=args.business.lower())
|
||||
cof.set(section="run_evn_name", option="current_evn", value=args.env.upper())
|
||||
cof.set(section="run_user_name", option="default_user", value=args.user.lower())
|
||||
cof.set(section="run_jira_id", option="huohua-podenv", value=args.jira_id.upper())
|
||||
cof.set(section="run_evn_name", option="current_team", value=args.team_name.upper())
|
||||
with open(env_choose_path, 'w') as fw: # 循环写入
|
||||
cof.write(fw)
|
||||
|
||||
if jira_id:
|
||||
print("running jira_id is:【{}】【{}】【{}】".format(business, TEAM_NAME, jira_id))
|
||||
else:
|
||||
print("running environment is: 【{}】【{}】【{}】".format(business, TEAM_NAME, choose_env))
|
||||
|
||||
from base_framework.main import main, kill_pid
|
||||
kill_pid()
|
||||
main()
|
||||
Reference in New Issue
Block a user