第三方模块
allure-pytest
pymysql
requests
main.py
import os import pytest if __name__ == '__main__': pytest.main(["--alluredir","./alluretmp"]) os.system("allure generate ./alluretmp -o ./allure-report --clean")
config.py
import os import pytest import requests from common.encryOPS import EncryOPS from common.getBaseInfo import GetBaseInfo from common.pathSetting import PathSetting @pytest.fixture(scope="session",autouse=True) def get_token(): config_path = PathSetting().path_setting("test_config_path") url = GetBaseInfo().get_value_bySection(config_path+"\\baseEnv.ini","testenv")["baseurl"] + "user/login" username = GetBaseInfo().get_value_byoption(config_path+"\\baseEnv.ini","userinfo","username") pwd = GetBaseInfo().get_value_byoption(config_path+"\\baseEnv.ini", "userinfo", "pwd") pwd = EncryOPS().encry_sha1(pwd.encode("utf-8")) data ={ "username": username, "password": pwd } try: res = requests.post(url=url, params=data) token = res.json()["data"]["token"] os.environ["token1"] = str(token) except Exception as e: print(f"初始化登录失败conftest.get_token,用户名{username},密码{pwd},token{token}") @pytest.fixture(scope="session",autouse=True) def clean_tmpallure(): project_path = PathSetting().path_setting("project_path") tmp_allure_path = project_path+"\\alluretmp" tmpallure_files =os.listdir(tmp_allure_path) for fl in tmpallure_files: fl_path = tmp_allure_path+"\\"+fl if fl.endswith(".json") or fl.endswith(".txt"): os.remove(fl_path) def get_my_sql_info(): test_config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" mysql_info = GetBaseInfo().get_value_bySection(test_config_path, "mysql") mysql_info["port"] = int(mysql_info["port"]) return mysql_info
pytest.ini
[pytest] addopts = -vs testpaths = ./testcase python_files = test*.py python_classes =Test* python_functions = test_* markers = smoke story: sdv sit: sit uat: uat
common/dbconn.py
import pymysql as pymysql from common.getBaseInfo import GetBaseInfo from common.logger import logger from common.pathSetting import PathSetting class Dbconn(object): def __init__(self,dbinfo): self.db = pymysql.connect(cursorclass=pymysql.cursors.DictCursor ,**dbinfo) self.cursor = self.db.cursor() def select(self,sql): try: self.cursor.execute(sql) result = self.cursor.fetchall() return result except Exception as e: print(e) logger.error("执行SELECT SQL失败",e) def execute(self,sql): self.cursor.execute(sql) self.db.commit() def close(self): self.cursor.close() self.db.close()
common/encryOPS.py
import hashlib class EncryOPS: @staticmethod def encry_sha1(value): return hashlib.sha256(value).hexdigest() common/getbaseinfo.py
import configparser as configparser class GetBaseInfo: def __init__(self): self.config = configparser.ConfigParser() def get_all_sections(self,path): self.config.read(path) return self.config.sections() def get_all_options(self,path,section): self.config.read(path) return self.config.options(section) def get_value_byoption(self,path,section,option): try: self.config.read(path) return self.config.get(section,option) except configparser.NoSectionError as e: print("error is",e) return 'the section"{}"->options"{}" is no value'.format(section,option) def get_value_bySection(self,path,section): try: self.config.read(path) options = self.config.options(section) sect_list ={} for opt in options: opt_value = self.config.get(section,opt) sect_list[opt] = opt_value return(sect_list) except Exception as e: print("error is",e) return(f"no section{section} in the path{path}")
common/getheaders.py
class GetHeaders: @staticmethod def get_header(token): try: headers = { "Content-Type": "application/json", "token": token } return headers except Exception as e: print("get_header is error:",e) @staticmethod def get_header_token(token): try: headers = { "token":token } return headers except Exception as e: print("get_header_token is error:", e)
common/getyml.py
import codecs import yaml class GetYmlData: @staticmethod def get_yml_data(filepath): try: with open(filepath,'r',encoding='utf-8') as f: datas = yaml.load(stream=f,Loader=yaml.FullLoader) return datas except Exception as e: print("获取yml文件内容失败:get_yml_data",e) @staticmethod def get_yml_data_byc(filepath): try: with codecs.open(filepath,'r',encoding='utf-8',errors='ignores') as f: datas = yaml.load(stream=f, Loader=yaml.FullLoader) return datas except Exception as e: print("获取yml文件内容失败:get_yml_data_byc", e)
common/pathsetting.py
import os.path class PathSetting: def path_setting(self,path_type): project_path = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) if path_type == "test_cases_path": testcases_path = project_path+"\\testcase" return testcases_path elif path_type == "test_datas_path": testdatas_path = project_path+"\\testdatas" return testdatas_path elif path_type == "test_config_path": return project_path +"\\config" elif path_type == "project_path": return project_path
common/readupdate_dcm.py
# -*- coding:utf-8 -*- import os import shutil import pydicom as pd import SimpleITK as ITK class ReadUpdateDicom: @staticmethod def read_dicom(file_path): dicom_files = os.listdir(file_path) studyInstanceUid_lst = [] seriesInstanceUid_lst = [] position_lst = [] position_dct = {} instanceNumber_lst = [] bodyPart_lst = [] sopInstanceUid_lst = [] for fl in dicom_files: if fl.endswith(".dcm"): dicom_data = pd.read_file(dicom_path + "\\" + fl) dicom_imagePositionPatient = 0 try: dicom_imagePositionPatient = dicom_data.get("ImagePositionPatient")[-1] except: pass dicom_instanceNumber = dicom_data.get("InstanceNumber") dicom_studyInstanceUid = dicom_data.get("StudyInstanceUID") dicom_SeriesInstanceUID = dicom_data.get("SeriesInstanceUID") dicom_BodyPartExamined = dicom_data.get("BodyPartExamined") dicom_SOPInstanceUID = dicom_data.get("SOPInstanceUID") if dicom_imagePositionPatient != 0: if dicom_imagePositionPatient not in position_lst: position_lst.append(dicom_imagePositionPatient) position_dct[dicom_imagePositionPatient] = 1 else: position_dct[dicom_imagePositionPatient] += 1 if dicom_studyInstanceUid not in studyInstanceUid_lst: studyInstanceUid_lst.append(dicom_studyInstanceUid) if dicom_SeriesInstanceUID not in seriesInstanceUid_lst: seriesInstanceUid_lst.append(dicom_SeriesInstanceUID) if dicom_BodyPartExamined not in bodyPart_lst: bodyPart_lst.append(dicom_BodyPartExamined) instanceNumber_lst.append(dicom_instanceNumber) sopInstanceUid_lst.append(dicom_SOPInstanceUID.split(".")[-1]) # 多个study或者series不支持 if len(studyInstanceUid_lst) > 1 or len(seriesInstanceUid_lst) > 1: print('\033[31m多个study或者series不支持\033[0m') exit(0) # 判断当前影像是否为HEAD头部影像 print("\33[32;40m规则1:BodyPartExamined 是否为HEAD头部影像:\33[0m") if len(bodyPart_lst) == 1 and bodyPart_lst[0] == "HEAD": print('当前影像的扫描部位均为HEAD') else: print('\33[31m当前影像的扫描部位不是全部为HEAD:\\33[0m') for i in bodyPart_lst: print(i, end=" ") # 统计总计有多少个扫描时相 print("\33[32;40m规则2:扫描部位统计及扫描部位是否等差数列:\33[0m") print('扫描部位总计:{}'.format(len(position_lst))) if len(position_lst) >= 1: position_lst = list(map(int, position_lst)) position_lst.sort() print('扫描部位列表:{}'.format(position_lst)) is_sequence = True for i in range(2, len(position_lst)): value = round(position_lst[1] - position_lst[0], 2) if round(position_lst[i] - position_lst[i - 1], 2) != value: is_sequence = False print("当前扫描部位与前一张影像扫描部位不成等差数列:", position_lst[i]) break if is_sequence: print("影像扫描部位成等差数列,差值为:{}".format(round(position_lst[1] - position_lst[0], 2))) # 统计每个扫描时相下的扫描次数 print("\33[32;40m规则3:统计每个扫描时相下的扫描次数:\33[0m") n = 0 for i in position_dct: print('Z轴为{}:'.format(i), end=" ") print('{}次'.format(position_dct[i]), end=" ") n += 1 if n % 10 == 0: print() print() # 统计InstanceNumber是否从1,按步长为1递增 print("\33[32;40m规则4:统计InstanceNumber是否从1,按步长为1递增:\33[0m") instanceNumber_lst = list(map(int, instanceNumber_lst)) instanceNumber_lst.sort() if instanceNumber_lst[0] != 1: print('\033[31m影像InstanceNumber最小值不是1,实际值为{},文件名为{}\033[0m'.format(instanceNumber_lst[0], dicom_files[0])) flag = True for i in range(1, len(instanceNumber_lst)): if instanceNumber_lst[i] - instanceNumber_lst[i - 1] != 1: print('\033[31m当前影像与上一张影像步长不为1,实际为{},文件名为{}\033[0m'.format( instanceNumber_lst[i] - instanceNumber_lst[i - 1], dicom_files[i])) flag = False break if flag: print("规则4验证OK:当前影像步长均为1.") print("instanceNumber_lst", instanceNumber_lst) print("\33[32;40m规则5:SOPInstanceUID是否连续且步长为1:\33[0m") flag = True sopInstanceUid_lst = list(map(int, sopInstanceUid_lst)) sopInstanceUid_lst_new = sorted(sopInstanceUid_lst) for i in range(1, len(sopInstanceUid_lst_new)): if int(sopInstanceUid_lst_new[i]) - int(sopInstanceUid_lst_new[i - 1]) != 1: flag = False print('\33[31m当前影像SOPInstanceUID不连续,实际步长为{},当前影像SOPInstanceUID为{}\33[0m'.format( int(sopInstanceUid_lst_new[i]) - int(sopInstanceUid_lst_new[i - 1]), sopInstanceUid_lst_new[i])) break if flag: print("规则5验证OK:当前影像SOPInstanceUID连续且步长为1:") print(sopInstanceUid_lst_new) @staticmethod def update_dicom(filepath, params, patientname, patientid): dicom_files = os.listdir(filepath) for fl in dicom_files: fl_path = os.path.join(dicom_path, fl) try: dicom_data = pd.read_file(dicom_path + "\\" + fl) # 每次执行需要修改 update_data = params # 修改患者属性 dicom_data.PatientName = patientname dicom_data.PatientID = patientid # dicom_data.PatientSex ="F" # dicom_data.PatientAge ="022Y" # dicom_data.PatientBirthDate = "19780120" # 修改影像属性 测试上传规则 # dicom_data.BodyPartExamined = "BODY" # dicom_data.Modality = "XA" dicom_data.SliceThickness = 0.5 # dicom_data.Rows = 555 # dicom_data.Columns = 555 # 修改 study数据,StudyInstanceUID截取倒数第8到倒数第14位 dicom_data.StudyID = update_data study_instanceuid = dicom_data.get("StudyInstanceUID") try: start_studyinstanceuid = study_instanceuid[0:-14] middle_StudyInstanceUID = update_data + str(int(study_instanceuid[-10:-6]) + 1).rjust(4, "0") en_StudyInstanceUID = study_instanceuid[-4:] dicom_data.StudyInstanceUID = start_studyinstanceuid + middle_StudyInstanceUID + en_StudyInstanceUID except: print('\033[31m影像原始StudyInstanceUID格式不对:{}\033[0m'.format(study_instanceuid)) # 修改series数据 # dicom_data.SeriesNumber =update_data series_instanceuid = dicom_data.get("SeriesInstanceUID") try: start_seriesinstanceuid = series_instanceuid[0:-17] middle_seriesinstanceuid = update_data + str(int(series_instanceuid[-11:-7]) + 1).rjust(4, "0") end_seriesinstanceuid = series_instanceuid[-7:] dicom_data.SeriesInstanceUID = start_seriesinstanceuid + middle_seriesinstanceuid + end_seriesinstanceuid # SOPInstanceUID sopinstanceuid = str(dicom_data.get("SOPInstanceUID")).split(".") before_sopinstanceuid = sopinstanceuid[0:-2] replace_sopinstanceuid = str(int(update_data[-5:]) + 1).ljust(5, "0") after_sopinstanceuid = sopinstanceuid[-1] dicom_data.SOPInstanceUID = ".".join( before_sopinstanceuid) + "." + replace_sopinstanceuid + "." + after_sopinstanceuid except: print('\033[31m影像原始SeriesInstanceUID格式不对:{}\033[0m'.format(series_instanceuid)) dicom_data.save_as(fl_path) except: continue @staticmethod def copy_dicom(src_path, dest_path, params): src_files = os.listdir(src_path) for i, fl in enumerate(src_files): if fl.endswith(".dcm"): srcfl_path = os.path.join(src_path, fl) # desc_flname = params + fl.split(".")[0] + ".dcm" desc_flname = '1' + fl.split(".")[0] + ".dcm" destfl_path = os.path.join(dest_path, desc_flname) shutil.copy(srcfl_path, destfl_path) # 修改目标路径下series数据 """ SeriesInstanceUID SOPInstanceUID seriesnumber """ update_data = params dest_dicom_files = os.listdir(dest_path) for j, fl in enumerate(dest_dicom_files): file_path = os.path.join(dest_path, fl) dicom_data = pd.read_file(dest_path + "\\" + fl) series_instanceuid = dicom_data.get("SeriesInstanceUID") sopinstanceuid = str(dicom_data.get("SOPInstanceUID")).split(".") dicom_data.SeriesNumber = dicom_data.get("SeriesNumber") + 1 try: # SeriesInstanceUID start_seriesinstanceuid = series_instanceuid[0:-17] middle_seriesinstanceuid = update_data + str(int(series_instanceuid[-11:-7]) + 1).rjust(4, "0") end_seriesinstanceuid = series_instanceuid[-7:] dicom_data.SeriesInstanceUID = start_seriesinstanceuid + middle_seriesinstanceuid + end_seriesinstanceuid # SOPInstanceUID before_sopinstanceuid = sopinstanceuid[0:-2] replace_sopinstanceuid = str(int(update_data) + 1).ljust(5, "0") after_sopinstanceuid = sopinstanceuid[-1] dicom_data.SOPInstanceUID = ".".join( before_sopinstanceuid) + "." + replace_sopinstanceuid + "." + after_sopinstanceuid except: print('\033[31m影像原始SeriesInstanceUID格式不对:{}\033[0m'.format(series_instanceuid)) dicom_data.save_as(file_path) @staticmethod def delete_image_position(path, image_position_lst2): dicom_files = os.listdir(path) image_position_lst2 = list(map(float, image_position_lst2)) for file in dicom_files: file_path = os.path.join(path, file) file_datas = pd.read_file(file_path) file_location = file_datas.get("ImagePositionPatient")[-1] if file_location in image_position_lst2: os.remove(file_path) @staticmethod def rename_image(dicom_path2): files = os.listdir(dicom_path2) for file in files: if not file.endswith(".dcm"): file_path = os.path.join(dicom_path2, file) new_file = file + ".dcm" new_file_path = os.path.join(dicom_path2, new_file) os.renames(file_path, new_file_path) @staticmethod def get_file_names(dicom_path3): files = os.listdir(dicom_path3) files = sorted(files) for file in files: print(file) if __name__ == '__main__': """ func_name =1 统计校验当前路径文件夹下的影像完整性 当前不支持一个文件夹下有多个study或者series影像的数据 1.BodyPartExamined 是否为HEAD头部影像 2.扫描部位统计及扫描部位是否等差数列 3.统计每个部位扫描次数 4.InstanceNumber是否连续,且最小值为1 5.SOPInstanceUID是否连续且步长为1 func_name = 2 直接修改当前路径下的所有dicom文件属性 每次执行必须修改: dicom_path update_data dicom_data.PatientName dicom_data.PatientID func_name = 3 直接基于源路径下的dicom文件进行copy,生成一个新的series 每次执行必须修改param_data,不能每次都基于同一份数据进行copy,否则每次copy后的seriesNumber都相同 fun_name =4 :删除指定扫描部位的影像ImagePositionPatient fun_name = 5 :将指定路径下的文件名拼上.dcm fun_name = 6 : 将指定路径下的文件拼成2000个,名读取出来 --- UI自动化构造2000个影像 """ # 入口 func_name = 2 # 修改参数值 param_data = "100053" # 修改患者属性 patient_name = "UIAUTO_023" patient_id = "100053" # dicom文件路径 dicom_path = "/".join(str(r'E:\autotest\ais_ui_testdatas\upload\upload_less_30pc_study').split("\\")) destination_path = "/".join(str(r"E:\项目\手术导航系统\影像数据\林华桂-cta-test-copy-ok").split("\\")) try: if func_name == 1: ReadUpdateDicom().read_dicom(file_path=dicom_path) print("\33[32;40m统计完成\33[0m") elif func_name == 2: ReadUpdateDicom().update_dicom(filepath=dicom_path, params=param_data, patientname=patient_name, patientid=patient_id) print("\33[32;40m更新完成\33[0m") elif func_name == 3: ReadUpdateDicom().copy_dicom(dicom_path, destination_path, param_data) print("\33[32;40m文件拷贝完成\33[0m") elif func_name == 4: image_position_lst2 = [6] ReadUpdateDicom().delete_image_position(dicom_path, image_position_lst2) print("\33[32;40m已经删除指定的ImagePositionPatient的影像\33[0m") elif func_name == 5: ReadUpdateDicom().rename_image(dicom_path) print("\33[32;40m非dcm文件已经重新更名.dcm格式\33[0m") elif func_name == 6: ReadUpdateDicom().get_file_names(dicom_path) except: print('执行异常')
config/env.ini
[testenv] baseUrl = http://172.31.1.16:8081/ [userinfo] username = ais pwd = 123456 [mysql] host = 172.31.1.16 port = 3306 user = ais password = ais database = ais
testcase/testCTP/testCTPList.py
import os import allure import pytest import requests from common.getBaseInfo import GetBaseInfo from common.getHeaders import GetHeaders from common.getYmlData import GetYmlData from common.pathSetting import PathSetting @allure.feature("CTP诊断List") class TestCTPList: test_datas_path = PathSetting().path_setting("test_datas_path")+"\\testCTP\\testCTPList.yml" test_datas = GetYmlData().get_yml_data(test_datas_path) test_config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" baseurl = GetBaseInfo().get_value_byoption(test_config_path,'testenv','baseUrl') @allure.story("CTP诊断List查询") @pytest.mark.parametrize('datas',test_datas) def test_ctp_search(self,datas): with allure.step("设置token及headers"): token = os.environ.get("token1") headers = GetHeaders().get_header_token(token) method = datas["method"] with allure.step("获取测试用例参数"): url = TestCTPList.baseurl+datas["url"] data1 = datas["data"] validate_code = datas["validate"]["code"] with allure.step(f"调用查询接口{url}"): res = requests.get(url,data=data1, headers=headers) with allure.step(f"校验实际接口返回code是否为{validate_code},"): assert res.json()["code"] == validate_code
testcase/testlist/teststudylist.py
import os import allure import pytest import requests from common.getBaseInfo import GetBaseInfo from common.getHeaders import GetHeaders from common.getYmlData import GetYmlData from common.logger import logger from common.pathSetting import PathSetting @allure.parent_suite("影像模块") @allure.feature("影像列表") class TestStudyList: test_datas_path = PathSetting().path_setting("test_datas_path")+"\\testStudyList\\testStudyList.yml" test_config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" test_datas = GetYmlData().get_yml_data(test_datas_path) baseurl = GetBaseInfo().get_value_bySection(test_config_path,"testenv")["baseurl"] @allure.story("列表查询") @allure.title("测试测试用例执行列表查询") @pytest.mark.parametrize("datas",test_datas) def test_study_search(self,datas): token = os.environ.get("token1") headers1 = GetHeaders().get_header_token(token) url = TestStudyList.baseurl+datas["url"] data1 = datas["data"] validate_code = datas["validate"]["code"] validate_total = datas["validate"]["data"]["total"] logger.debug("Begin to send request:"+url) with allure.step(f"开始发送请求:{url}"): res = requests.get(url,params=data1,headers=headers1) with allure.step(f"获取接口{url}返回值"): res_code = res.json()["code"] res_total = res.json()["data"]["total"] with allure.step("开始数据校验"): logger.debug("Begin to Check The Result:"+str(validate_total)) assert res_code == validate_code assert res_total == validate_total if __name__ == '__main__': TestStudyList().test_study_search() testcase/testlist/teststudyalign.py
import os import time import allure import pytest import requests from common.dbconn import Dbconn from common.getBaseInfo import GetBaseInfo from common.getHeaders import GetHeaders from common.getYmlData import GetYmlData from common.pathSetting import PathSetting @allure.feature("影像列表") class TestStudyRealign: test_datas_path = PathSetting().path_setting("test_datas_path")+"\\testStudyList\\testStudyRealign.yml" test_datas = GetYmlData().get_yml_data(test_datas_path) test_config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" baseurl = GetBaseInfo().get_value_bySection(test_config_path, "testenv")["baseurl"] mysql_config_path = PathSetting().path_setting("test_config_path")+"\\mysql.yml" mysql_info = GetBaseInfo().get_value_bySection(test_config_path, "mysql") mysql_info["port"] = int(mysql_info["port"]) @allure.story("影像校正") @allure.title("根据不同状态发起影像校正") @pytest.mark.parametrize("testdata",test_datas) def test_study_realign(self,testdata): headers = GetHeaders().get_header(os.environ.get("token1")) data = testdata["data"] patient_name = testdata["data"]["patient_name"] url = TestStudyRealign.baseurl + testdata["url"] study_status = testdata["data"]["study_status"] exp_status = testdata["validate"]["exp_status"] with allure.step("定义SQL"): """ select_studyid : 查询study_id update_study_realign_status : 更新study校正状态 select_study_status :获取校正后的study状态 """ select_studyid_sql = f'select id from t_study ts where exists(\ select 1 from t_patient tu where ts.patient=tu.id and\ tu.patient_name = \'{patient_name}\')' exec_study_realign_status=f'update t_study_realign tsr set\ tsr.request_status ={study_status} where exists(\ select 1 from t_study ts where tsr.study = ts.id and exists ( select 1 from t_patient tu where\ ts.patient = tu.id and tu.patient_name = \'{patient_name}\'))' # select_study_status = f'select request_status from t_study_realign tsr where exists(\ # select 1 from t_study ts where tsr.study = ts.id and exists ( select 1 from t_patient tu where\ # ts.patient = tu.id and tu.patient_name = \'{patient_name}\'))' with allure.step("执行SQL查询study_id"): db = Dbconn(TestStudyRealign.mysql_info) study_id = db.select(select_studyid_sql)[0]["id"] with allure.step(f"更新当前study影像诊断状态{study_status}"): db.execute(exec_study_realign_status) db.close() with allure.step(f"拼接完整URL接口信息{url}/{study_id}"): url = url+"/"+str(study_id) with allure.step(f"发送校正请求{url}"): res = requests.post(url,json=data,headers=headers) with allure.step("开始接口校验"): assert testdata["validate"]["code"] == res.json()["code"] assert testdata["validate"]["message"] == res.json()["message"] # with allure.step(f"校验最终的影像状态是否为{exp_status},默认等待5分钟"): # time.sleep(300) # status = db.select(select_study_status)[0]["request_status"] # db.close() # assert status == exp_status if __name__ == '__main__': TestStudyRealign().test_study_realign()
testcase/testlist/teststudyupload.py
from datetime import datetime import os import allure import pytest import requests from common.dbconn import Dbconn from common.getBaseInfo import GetBaseInfo from common.getHeaders import GetHeaders from common.getYmlData import GetYmlData from common.logger import logger from common.pathSetting import PathSetting @allure.parent_suite("影像模块") @allure.feature("影像上传") class TestStudyUpload: test_datas_base_path = PathSetting().path_setting("test_datas_path") test_datas_path = test_datas_base_path+"\\testStudyList\\testStudyUpload.yml" test_datas = GetYmlData().get_yml_data(test_datas_path) for data_ in test_datas: data_["data"]["file_path"] = test_datas_base_path+data_["data"]["file_path"] test_config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" baseurl = GetBaseInfo().get_value_bySection(test_config_path, "testenv")["baseurl"] config_test_path = PathSetting().path_setting("test_config_path") + "\\mysql.yml" mysql_info = GetBaseInfo().get_value_bySection(test_config_path, "mysql") mysql_info["port"] = int(mysql_info["port"]) @allure.story("影像上传") @allure.title("根据影像状态进行影像上传") @pytest.mark.parametrize("testdata",test_datas) def test_study_upload_001(self,testdata): file_sets = set() files_path = testdata["data"]["file_path"] filedirs = os.listdir(files_path) with allure.step(f"选取待上传的文件{files_path}"): for fl in filedirs: fl_path = files_path + fl file_sets.add((('file',(fl,open(fl_path,'rb'))))) with allure.step("获取token并设置headers"): token = os.getenv("token1") headers1 = GetHeaders().get_header_token(token) url = TestStudyUpload.baseurl+testdata["url"] files = file_sets study_status = testdata["data"]["study_status"] series_status = testdata["data"]["series_status"] patient_name = testdata["data"]["patient_name"] db = Dbconn(TestStudyUpload.mysql_info) exec_study_sql = f"update t_study_realign tsr2 set tsr2.request_status ={study_status} where EXISTS(\ select 1 from t_study tst where tsr2.study = tst.id and tst.patient in(\ select id from t_patient tp where tp.patient_name = \"{patient_name}\"))" exec_series_sql = f"update t_series_dx dsd set dsd.request_status = {series_status} where\ EXISTS(select 1 from t_study_dx tsd1 where dsd.study_dx =tsd1.id and exists( select\ 1 from t_study ts where tsd1.study = ts.id and exists( select 1 from t_patient tp\ where ts.patient =tp.id and tp.patient_name = \'{patient_name}\')))" with allure.step(f"根据测试用例中影像状态为{study_status},影像序列状态为{series_status}:更新t_study_realign.request_status={study_status}\ 更新t_series_dx.request_status ={series_status}"): db.execute(exec_study_sql) try: db.execute(exec_series_sql) except Exception: logger.log("更新DB异常") db.close() with allure.step("调用上传文件接口"): begin_time = datetime.now() rs = requests.post(url, files=files,headers=headers1) end_time = datetime.now() over_time = end_time-begin_time print("上传时间花费:",over_time) with allure.step("开始结果校验"): response_code = testdata["validate"]["code"] response_mess = testdata["validate"]["message"] print("testdata",testdata) assert response_code == rs.json()["code"] assert response_mess in rs.json()["message"] if __name__ == '__main__': TestStudyUpload.test_study_upload_001()
testcase/testCTP/testCTPDX.py
import json import os import allure import pytest import requests from common.dbconn import Dbconn from common.getBaseInfo import GetBaseInfo from common.getHeaders import GetHeaders from common.getYmlData import GetYmlData from common.logger import logger from common.pathSetting import PathSetting from conftest import get_my_sql_info def update_status(patient_name,study_status,series_dx_status): """ 当前只考虑1study 1series t_study.status t_series_dx.request_status 1.未校正 0 NA 2.已校正 2 NA 3.校正异常 5 NA 4.校正中 1 NA 5.分析中 3 3 6.已分析 4 4 7.分析异常 6 5 """ update_study_sql = f'update t_study ts set ts.status ={study_status} where exists \ (select 1 from t_patient tt where tt.id= ts.patient and tt.patient_name =\'{patient_name}\');' update_series_sql = f'update t_series_dx tsd set tsd.request_status ={series_dx_status} where\ EXISTS(select 1 from t_patient tp where tsd.patient = tp.id and tp.patient_name = \'{patient_name}\');' db = Dbconn(TestCTPDX.mysql_info) db.execute(update_study_sql) if series_dx_status != '': db.execute(update_series_sql) db.close def get_series_data(): series_path = PathSetting().path_setting("test_datas_path") + "\\testCTP\\test_series_dx.yml" series_data = GetYmlData().get_yml_data(series_path) for i, j in enumerate(series_data): j["series_number"] = series_data[i]["series_number"] j["name"] = series_data[i]["name"] j["study_method"] = series_data[i]["study_method"] j["study_url"] = series_data[i]["study_url"] j["series_method"] = series_data[i]["series_method"] j["series_url"] = series_data[i]["series_url"] j["patient_name"] = series_data[i]["patient_name"] j["data"]["aifLocation"] = series_data[i]["data"]["aifLocation"] j["data"]["vofLocation"] = series_data[i]["data"]["vofLocation"] j["validate"]["code"] = series_data[i]["validate"]["code"] j["validate"]["result"] = series_data[i]["validate"]["result"] return series_data @allure.feature("CTP诊断") class TestCTPDX: headers = GetHeaders().get_header(os.environ.get("token1")) config_path = PathSetting().path_setting("test_config_path")+"\\baseEnv.ini" baseurl = GetBaseInfo().get_value_byoption(config_path,"testenv","baseurl") study_path = PathSetting().path_setting("test_datas_path")+"\\testCTP\\test_series_dx.yml" study_data = GetYmlData().get_yml_data(study_path) mysql_info = get_my_sql_info() study_dx = "" series_data = get_series_data() @staticmethod def get_study_dx(url): try: res = requests.post(url=url,params="",headers=TestCTPDX.headers1) study_dx = res.json()["data"]["studyDx"] return study_dx except Exception as e: logger.error(f"发起study诊断异常{e},API地址={url},token={TestCTPDX.headers1}") @allure.story("根据测试用例进行series诊断") @pytest.mark.parametrize("series_data",series_data) def test_send_series_dx(self,series_data): with allure.step(f"获取测试用例参数{series_data}"): study_url = TestCTPDX.baseurl+series_data["study_url"] series_url = TestCTPDX.baseurl+series_data["series_url"] patient_name = series_data["patient_name"] series_number = series_data["series_number"] case_name = series_data["name"] data = series_data["data"] with allure.step(f"定义查询当前患者{patient_name}的studyID的SQL"): select_study_id = f'select id from t_study ts where exists(\ select 1 from t_patient tp where ts.patient = tp.id and tp.patient_name =\'{patient_name}\');' db = Dbconn(TestCTPDX.mysql_info) study_id = db.select(select_study_id)[0]["id"] db.close() with allure.step(f"重置测试用例中的series诊断状态:{case_name}"): if "已分析" in case_name: study_status = 4 series_status = 4 elif "分析异常" in case_name: study_status = 6 series_status = 5 elif "分析中" in case_name: study_status = 3 series_status = 3 else: series_status = '' if "未校正" in case_name: study_status = 0 elif "校正中" in case_name: study_status = 1 elif "已校正" in case_name: study_status = 2 else: study_status = 5 update_status(patient_name,study_status,series_status) with allure.step("STEP1:发起study诊断,并获取study_dx"): url = study_url+f"{study_id}" TestCTPDX.study_dx = TestCTPDX().get_study_dx(url) with allure.step(f"STEP2:根据StudyDX:{TestCTPDX.study_dx},series_number:{series_number}获取SeriesId"): db = Dbconn(TestCTPDX.mysql_info) select_series_id = f'select id from t_series ts where ts.series_number =\'{series_number}\' and exists(\ select 1 from t_study_dx tsd\ where ts.study = tsd.study and tsd.id ={TestCTPDX.study_dx})' rst1 = db.select(select_series_id) db.close() series_id = rst1[0]["id"] with allure.step(f"STEP3:根据获取的study_dx:{TestCTPDX.study_dx}及seriesId:{series_id}封装入参"): series_data["data"]["seriesId"] = series_id series_data["data"]["studyDx"] = TestCTPDX.study_dx with allure.step("STEP3:发起Series诊断"): try: res = requests.post(series_url,json=data,headers=TestCTPDX.headers1) except Exception as e: logger.error(f"接口返回异常{e}") with allure.step("校验接口返回值"): assert res.json()["result"] == series_data["validate"]["result"] assert res.json()["code"] == series_data["validate"]["code"] if __name__ == '__main__': TestCTPDX().test_send_series_dx()
testdatas/testCTP/testCTPlist.yml
- name: 根据患者名称查询 url: studies/page method: get data: current: 1 pageSize: 20 patientName: CAO CHENG JIE modality: CT validate: code: 0 data: total: 1 - name: 根据患者ID查询 url: studies/page method: get data: current: 1 pageSize: 20 patientId: 3048500 modality: CT validate: code: 0 data: total: 1 - name: 根据Study_id查询 url: studies/page method: get data: current: 1 pageSize: 20 studyId: 3201641 modality: CT validate: code: 0 data: total: 1
testdatas/testCTP/testCTPDX.yml
- name: 已校正的影像发起诊断 study_method: get study_url: dx/study/perfusion/ series_method: post series_url: dx/series/perfusion patient_name: AUT0D01 series_number: 10 data: seriesId: 0000, # 使用的时候需要重写 studyDx: 000, # aifLocation: x: 178.8928743573265 y: 220.85573987789206 z: 0 t: 15 "vofLocation": x: 272.7357005141389 y: 220.85573987789206 z: 0 t: 15 validate: result: true code: 0
testdatas/teststudylist/teststudylist.yml
- name: 根据患者名称查询 url: studies/page method: get data: current: 1 pageSize: 20 patientName: Patient_360 validate: code: 0 data: total: 1
testdatas/teststudylist/testalign.yml
- name: 未校正状态发起校正操作 url: study/realign data: patient_name: AUT0D02 study_status: 0 validate: code: 0 message: OK. exp_status: 2 - name: 已校正状态发起校正操作 url: study/realign data: patient_name: AUT0D02 study_status: 2 validate: code: 0 message: OK. exp_status: 2
testdatas/teststuydlist/testupload.yml
- name: 未校正状态进行导入 url: study/upload method: post data: file_path: \testStudyList\uploaddatas\dicom_study_Patient_370\ patient_name: Patient_370 study_status: 0 series_status: 0 validate: code: 0 message: Uploaded images submit successfully - name: 导入小于25张图片 url: study/upload method: post data: file_path: \testStudyList\uploaddatas\dicom_study_Patient_370_less25\ patient_name: Patient_370 study_status: 0 series_status: 0 validate: code: 0 message: Uploaded images submit successfully