addproject
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user