增加批量增加用例、测试项、设计需求功能

This commit is contained in:
2025-12-19 18:08:19 +08:00
parent f3806687b0
commit 3e048ea876
235 changed files with 1984 additions and 201 deletions

View File

@@ -357,7 +357,7 @@ class GenerateControllerBG(ControllerBase):
context = create_round_context(project_obj, round_str)
template_path = Path.cwd() / 'media' / project_path_str / 'form_template' / 'bg' / '测试内容和结果_第二轮次.docx'
doc = DocxTemplate(template_path)
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(
Path.cwd() / "media" / project_path_str / "output_dir/bg" / f"测试内容和结果_第{context['round_id']}轮次.docx")
@@ -629,7 +629,7 @@ class GenerateControllerBG(ControllerBase):
temporary_file = Path.cwd() / 'media' / project_path_str / 'form_template' / 'bg' / 'temporary' / '研总需归追踪_temp.docx'
out_put_file = Path.cwd() / 'media' / project_path_str / 'output_dir' / 'bg' / '研总需归追踪.docx'
doc = DocxTemplate(input_file)
doc.render(context)
doc.render(context, autoescape=True)
doc.save(temporary_file)
# 通过docx合并单元格
if temporary_file.is_file():
@@ -686,7 +686,7 @@ class GenerateControllerBG(ControllerBase):
'data_list': data_list
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/bg" / "问题汇总表.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -724,7 +724,7 @@ class GenerateControllerBG(ControllerBase):
context = {
'modi_list': modi_list,
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/bg" / "摸底清单.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")

View File

@@ -1,3 +1,4 @@
from datetime import datetime
from ninja.errors import HttpError
from ninja_extra import ControllerBase, api_controller, route
from ninja_extra.permissions import IsAuthenticated
@@ -34,7 +35,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
@transaction.atomic
def create_testdemand(self, id: int): # type:ignore
"""目前生成第一轮测试项"""
tplTestDemandGenerate_path = Path.cwd() / "media" / project_path(id) / "form_template" / "dg" / "测试项及方法.docx"
tplTestDemandGenerate_path = Path.cwd() / "media" / project_path(
id) / "form_template" / "dg" / "测试项及方法.docx"
doc = DocxTemplate(tplTestDemandGenerate_path)
# 获取指定的项目对象
project_qs = get_object_or_404(Project, id=id)
@@ -57,6 +59,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
"index": index + 1,
"rindex": str(index + 1).rjust(2, '0'),
"subName": content.subName,
"subDescription": content.subDescription,
# 修改遍历content下面的stepcontent变量是TestDemandContent表
"subStep": [
{'index': index + 1, 'operation': step_obj.operation, 'expect': step_obj.expect}
@@ -78,7 +81,6 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# ***Inspect-end***
html_parser = RichParser(single_qs.design.description)
desc_list = html_parser.get_final_list(doc)
# 查询关联design以及普通design
doc_list = [{'dut_name': single_qs.dut.name, 'design_chapter': single_qs.design.chapter,
'design_name': single_qs.design.name}]
@@ -88,6 +90,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
doc_list.append(ddict)
# 组装单个测试项
## 打印本项目是FPGA还是CPU
testdemand_dict = {
"name": single_qs.name,
"key": single_qs.key,
@@ -96,16 +99,13 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
"doc_list": doc_list,
"design_description": desc_list,
"test_demand_content": content_list,
"testMethod": testmethod_str,
"testMethod": testmethod_str.strip(),
"adequacy": single_qs.adequacy.replace("\n", "\a"),
"testDesciption": single_qs.testDesciption.replace("\n", "\a") # 测试项描述
"testDesciption": single_qs.testDesciption.replace("\n", "\a"), # 测试项描述
"testType": get_testType(single_qs.testType, 'testType'),
}
list_list[type_index].append(testdemand_dict)
# 定义渲染context字典
context = {
"project_name": project_qs.name
}
output_list = []
for (index, li) in enumerate(list_list):
@@ -122,8 +122,15 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 排序1测试类型排序
output_list = sorted(output_list, key=(lambda x: x["sort"]))
context["data"] = output_list
doc.render(context)
# 定义渲染context字典
context = {
"project_name": project_qs.name,
"is_JD": True if project_qs.report_type == '9' else False,
"data": output_list,
"isFPGA": '1' in project_qs.plant_type
}
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / "测试项及方法.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -148,7 +155,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 找出所属项目
project_qs = get_object_or_404(Project, id=id)
# 根据项目找出被测件-只找第一轮次
duties_qs = project_qs.pdField.filter(Q(type='XQ') | Q(type='SJ') | Q(type='XY') | Q(type='YZ')).filter(
duties_qs = project_qs.pdField.filter(
Q(type='XQ') | Q(type='SJ') | Q(type='XY') | Q(type='YZ')).filter(
round__key='0')
# 先定义个字典
std_documents = []
@@ -169,7 +177,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 先找出所属项目
project_qs = get_object_or_404(Project, id=id)
contact_dict = model_to_dict(project_qs,
fields=['entrust_unit', 'entrust_contact', 'entrust_contact_phone', 'dev_unit',
fields=['entrust_unit', 'entrust_contact', 'entrust_contact_phone',
'dev_unit',
'dev_contact', 'dev_contact_phone', 'test_unit', 'test_contact',
'test_contact_phone'])
# 根据entrust_unit、dev_unit、test_unit查找Contact中地址信息
@@ -190,8 +199,18 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
def create_timeaddress(self, id: int):
doc_timer = DocTime(id)
context = doc_timer.dg_address_time()
context = self.change_time_to_another(context, ['beginTime_strf', 'dgCompileStart', 'dgCompileEnd',
'designStart', 'designEnd'])
return create_dg_docx('测评时间和地点.docx', context, id)
# 2025/12/11将20250417格式改为2025年04月17日 - 封装函数,传入字典和键值,修改对应键值信息
def change_time_to_another(self, context: dict, key_list: list[str]):
for key in key_list:
time_val = context.get(key, None)
if time_val:
context[key] = datetime.strptime(time_val, "%Y%m%d").strftime("%Y年%m月%d")
return context
# 生成【主要功能和性能指标】文档片段
@route.get('/create/indicators', url_name='create-indicators')
@transaction.atomic
@@ -255,7 +274,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
'md_demand_list': md_demand_list,
'is_has_modi': is_has_modi
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '主要功能和性能指标.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -273,7 +292,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '测评对象.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -344,7 +363,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '静态测试环境说明.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -378,14 +397,16 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 动态测评环境说明
@route.get('/create/dynamic_env', url_name='create-dynamic_env')
def create_dynamic_env(self, id: int):
project_obj: Project = get_object_or_404(Project, id=id)
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '动态测试环境说明.docx'
doc = DocxTemplate(input_path)
replace, frag, rich_text_list = self._generate_frag(id, doc, '动态测试环境说明')
context = {
'project_name': project_obj.name,
"replace": replace,
"user_content": frag and rich_text_list
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '动态测试环境说明.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -395,10 +416,12 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 动态软件项
@route.get('/create/dynamic_soft', url_name='create-dynamic_soft')
def create_dynamic_soft(self, id: int):
project_obj: Project = get_object_or_404(Project, id=id)
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '动态软件项.docx'
doc = DocxTemplate(input_path)
replace, frag, rich_text_list = self._generate_frag(id, doc, '动态软件项')
context = {
'project_name': project_obj.name,
"replace": replace,
"user_content": frag and rich_text_list
}
@@ -462,6 +485,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 渲染上下文
context = {
'project_name': project_qs.name,
'is_JD': True if project_qs.report_type == '9' else False,
'security_level': security,
'language': "\a".join(language_list),
'version': version,
@@ -492,7 +516,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 获取所有已录入测试类型
test_types = project_qs.ptField.values("testType").distinct()
# 通过测试类型查询字典中的中文
type_name_list = list(map(lambda qs_item: get_str_dict(qs_item['testType'], 'testType'), test_types))
type_name_list = list(
map(lambda qs_item: get_str_dict(qs_item['testType'], 'testType'), test_types))
# 定义测试类型一览的顺序注意word里面也要一样
word_types = ['文档审查', '静态分析', '代码审查', '逻辑测试', '功能测试', '性能测试', '边界测试',
'恢复性测试', '安装性测试', '数据处理测试', '余量测试', '强度测试', '接口测试',
@@ -503,6 +528,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
if exist_type == test_type:
type_index.append(str(index))
context = {
"security_level": get_str_dict(project_qs.security_level, 'security_level'),
"testTypes": "".join(type_name_list),
"project_name": project_qs.name,
"type_index": type_index
@@ -526,15 +552,24 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 根据关键等级检查是否有代码审查
security = project_qs.security_level
isDmsc = True if int(security) <= 2 else False
# 获取第一轮所有测试项QuerySet
project_round_one = project_qs.pField.filter(key=0).first()
testDemand_qs = project_round_one.rtField.all()
# grouped_data的键是测试类型名称值为测试项名称数组
grouped_data = {}
for item in testDemand_qs:
grouped_data.setdefault(get_str_dict(item.testType, 'testType'), []).append(item.name)
# 获取当前测试项的测试类型
test_types = project_qs.ptField.values("testType").distinct()
type_name_list = list(map(lambda qs_item: get_str_dict(qs_item['testType'], 'testType'), test_types))
test_types = testDemand_qs.values("testType").distinct()
type_name_list = list(
map(lambda qs_item: get_str_dict(qs_item['testType'], 'testType'), test_types))
context = {
"project_name": project_qs.name,
# 查询关键等级-类似“关键”输出
"security_level_str": get_str_abbr(security, 'security_level'),
"isDmsc": isDmsc,
"test_types": type_name_list
"test_types": type_name_list,
"grouped_data": grouped_data,
}
return create_dg_docx("测试策略.docx", context, id)
@@ -563,6 +598,7 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
type_str_list.append(f"{key}{value}")
context = {
'project_name': project_qs.name,
'test_item_count': testDemands.count(),
'length': length,
'type_str': "".join(type_str_list),
}
@@ -614,30 +650,39 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 形成一个数组['1','2','3','4','9']后面用来判断测试项的章节号
project_qs = get_object_or_404(Project, id=id)
design_list = [] # 先按照design的思路进行追踪
# 判断是否为鉴定测评,有则生成该表
if project_qs.report_type == '9':
project_round_one = project_qs.pField.filter(key=0).first()
testType_list, last_chapter_items = create_csx_chapter_dict(project_round_one)
# 找出第一轮的研总
yz_dut = project_round_one.rdField.filter(type='YZ').first()
if yz_dut:
# 查询出验证所有design
yz_designs = yz_dut.rsField.all()
# 遍历所有研总的design
for design in yz_designs:
design_dict = {'name': design.name, 'chapter': design.chapter, 'test_demand': []}
# 获取一个design的所有测试项
test_items = design.dtField.all()
for test_item in test_items:
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"), test_item.ident])
# 查字典方式确认章节号最后一位
test_item_last_chapter = last_chapter_items[test_item.testType].index(test_item.key) + 1
test_chapter = ".".join([test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter, 'ident': reveal_ident}
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
# 查询第一轮次
project_round_one = project_qs.pField.filter(key=0).first()
testType_list, last_chapter_items = create_csx_chapter_dict(project_round_one)
# 找出第一轮的研总
yz_dut = project_round_one.rdField.filter(type='YZ').first()
if yz_dut:
# 查询出验证所有design
yz_designs = yz_dut.rsField.all()
# 遍历所有研总的design
for design in yz_designs:
design_dict = {'name': design.name, 'chapter': design.chapter, 'test_demand': []}
# 获取一个design的所有测试项
test_items = design.dtField.all()
# 连接两个QuerySet默认去重
test_items = test_items.union(design.odField.all())
print('ok:', test_items)
for test_item in test_items:
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"), test_item.ident])
# 查字典方式确认章节号最后一位
test_item_last_chapter = last_chapter_items[test_item.testType].index(
test_item.key) + 1
test_chapter = ".".join(
[test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter,
'ident': reveal_ident}
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
try:
design_list = sorted(design_list, key=chapter_key)
except Exception as e:
print("研总的追踪排序报错,错误原因:", e)
context = {
'design_list': design_list
}
@@ -671,10 +716,13 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"), test_item.ident])
# 查字典方式确认章节号最后一位
test_item_last_chapter = last_chapter_items[test_item.testType].index(test_item.key) + 1
test_chapter = ".".join([test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter, 'ident': reveal_ident}
test_item_last_chapter = last_chapter_items[test_item.testType].index(
test_item.key) + 1
test_chapter = ".".join(
[test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter,
'ident': reveal_ident}
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
@@ -691,13 +739,21 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"), test_item.ident])
# 查字典方式确认章节号最后一位
test_item_last_chapter = last_chapter_items[test_item.testType].index(test_item.key) + 1
test_chapter = ".".join([test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter, 'ident': reveal_ident}
test_item_last_chapter = last_chapter_items[test_item.testType].index(
test_item.key) + 1
test_chapter = ".".join(
[test_item_prefix, str(testType_list.index(test_item.testType) + 1),
str(test_item_last_chapter)])
test_item_dict = {'name': test_item.name, 'chapter': test_chapter,
'ident': reveal_ident}
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
# 根据design的chapter排序-为防止报错崩溃使用try-但难排查
try:
design_list = sorted(design_list, key=chapter_key)
except Exception as e:
print("追踪排序报错,错误原因:", e)
context = {
'design_list': design_list
}
@@ -719,8 +775,9 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
items_list = []
for test_item in test_items:
# 第二个处理被测件为"XQ",第二个处理被测件为'SO'并且为测试项testType为['8', '15', '3', '2']的
if test_item.dut.type == 'XQ' or (test_item.dut.type == 'SO' and test_item.testType in ['8', '15', '3',
'2']):
if test_item.dut.type == 'XQ' or (
test_item.dut.type == 'SO' and test_item.testType in ['8', '15', '3',
'2']):
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"), test_item.ident])
# 查字典方式确认章节号最后一位
@@ -782,3 +839,13 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
else:
return ChenResponse(message='未找到源代码被测件', code=400)
return create_dg_docx('代码质量度量分析表.docx', context, id)
# 工具方法-给sorted排序使用-知识点python里面可以元组排序
def chapter_key(item):
big_num = [5000, 5000, 5000, 5000]
if "." in item['chapter']:
# 如果是有章节号的则排序
return [int(part) for part in item['chapter'].split(".")]
if item['test_demand'][0]['name'] in ['文档审查', '静态分析', '代码审查', '代码走查']:
return [0, 0, 0, 0]
return big_num

View File

@@ -87,7 +87,7 @@ class GenerateControllerHJL(ControllerBase):
context_round['version_info'] = version_info
# 开始渲染每个轮次的二级文档
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hjl' / f"{cname}轮被测软件基本信息.docx"
doc.render(context=context_round)
doc.render(context=context_round, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -204,7 +204,7 @@ class GenerateControllerHJL(ControllerBase):
context["data"] = output_list
# 最后渲染
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hjl' / f"{cname}轮测试用例记录.docx"
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:

View File

@@ -103,7 +103,7 @@ class GenerateControllerHSM(ControllerBase):
context_round['version_info'] = version_info
# 开始渲染每个轮次的二级文档
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮被测软件基本信息.docx"
doc.render(context=context_round)
doc.render(context=context_round, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -141,7 +141,7 @@ class GenerateControllerHSM(ControllerBase):
round_context['last_version'] = so_dut_last.version
round_context['round_chinese'] = cname
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮文档概述.docx"
doc.render(context=round_context)
doc.render(context=round_context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -171,9 +171,11 @@ class GenerateControllerHSM(ControllerBase):
'publish_date': timer.dg_cover_time, 'source': project_obj.test_unit}
std_documents.append(dg_duty)
# 需要添加说明、记录
sm_duty = {'doc_name': f'{project_obj.name}软件测试说明', 'ident_version': f'PT-{project_obj.ident}-TD-1.00',
sm_duty = {'doc_name': f'{project_obj.name}软件测试说明',
'ident_version': f'PT-{project_obj.ident}-TD-1.00',
'publish_date': timer.sm_cover_time, 'source': project_obj.test_unit}
jl_duty = {'doc_name': f'{project_obj.name}软件测试记录', 'ident_version': f'PT-{project_obj.ident}-TN',
jl_duty = {'doc_name': f'{project_obj.name}软件测试记录',
'ident_version': f'PT-{project_obj.ident}-TN',
'publish_date': timer.jl_cover_time, 'source': project_obj.test_unit}
std_documents.extend([sm_duty, jl_duty])
@@ -196,7 +198,7 @@ class GenerateControllerHSM(ControllerBase):
'std_documents': std_documents_round
}
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮技术依据文件.docx"
doc.render(context=context)
doc.render(context=context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -254,7 +256,7 @@ class GenerateControllerHSM(ControllerBase):
context_round['so_str'] = f"被测软件代码{now_dm_version}版本和{last_dm_version}版本"
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮软件更改部分.docx"
doc.render(context_round)
doc.render(context_round, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -346,7 +348,7 @@ class GenerateControllerHSM(ControllerBase):
output_list = sorted(output_list, key=(lambda x: x["sort"]))
context["data"] = output_list
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮回归测试需求.docx"
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -403,7 +405,7 @@ class GenerateControllerHSM(ControllerBase):
output_list = sorted(output_list, key=(lambda x: x["sort"]))
context["data"] = output_list
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮回归测试用例概述.docx"
doc.render(context=context)
doc.render(context=context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -494,7 +496,7 @@ class GenerateControllerHSM(ControllerBase):
context["data"] = output_list
context["round_han"] = cname
save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/hsm' / f"{cname}轮测试用例.docx"
doc.render(context=context)
doc.render(context=context, autoescape=True)
try:
doc.save(save_path)
except PermissionError:
@@ -585,7 +587,7 @@ class GenerateControllerHSM(ControllerBase):
temporary_file = Path.cwd() / 'media' / project_path_str / 'form_template' / 'hsm' / 'temporary' / f'{cname}轮用例追踪_temp.docx'
out_put_file = Path.cwd() / 'media' / project_path_str / 'output_dir' / 'hsm' / f'{cname}轮用例追踪.docx'
doc = DocxTemplate(input_file)
doc.render(context)
doc.render(context, autoescape=True)
doc.save(temporary_file)
# 通过docx合并单元格
if temporary_file.is_file():

View File

@@ -146,7 +146,7 @@ class GenerateControllerJL(ControllerBase):
output_list = sorted(output_list, key=(lambda x: x["sort"]))
context["data"] = output_list
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path_str / "output_dir/jl" / "测试用例记录.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")

View File

@@ -135,7 +135,7 @@ class GenerateControllerSM(ControllerBase):
# 排序
output_list = sorted(output_list, key=(lambda x: x["sort"]))
context["data"] = output_list
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path_str / "output_dir/sm" / "测试用例.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -265,7 +265,7 @@ class GenerateControllerSM(ControllerBase):
temporary_file = Path.cwd() / 'media' / project_path_str / 'form_template' / 'sm' / 'temporary' / '说明追踪_temp.docx'
out_put_file = Path.cwd() / 'media' / project_path_str / 'output_dir' / 'sm' / '说明追踪.docx'
doc = DocxTemplate(input_file)
doc.render(context)
doc.render(context, autoescape=True)
doc.save(temporary_file)
# 通过docx合并单元格
if temporary_file.is_file():

View File

@@ -147,7 +147,7 @@ class GenerateControllerWtd(ControllerBase):
'project_ident': project_obj.ident,
'problem_list': data_list,
}
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path_str / "output_dir/wtd" / '问题详情表.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")

View File

@@ -85,11 +85,15 @@ class RichParser:
if oneline.startswith("data:image/png;base64"):
base64_bytes = base64.b64decode(oneline.replace("data:image/png;base64,", ""))
# ~~~设置了固定宽度、高度~~~
final_list.append(InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(img_size), height=Mm(height)))
final_list.append(
InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(img_size), height=Mm(height)))
else:
final_list.append(oneline)
if len(final_list) <= 0:
final_list.append("")
# 针对tinymce中粘贴表格最后一行显示句号问题这里统一删除
if final_list[-1] == '\xa0':
final_list.pop()
return final_list
# 4.2.最终方法,在上面方法基础上,增加格式,例如<p>增加缩进,图片居中,<p>包含“图x”则居中

View File

@@ -18,7 +18,7 @@ def merge_all_cell(table: Table) -> None:
temp_text = cell.text
else:
if cell.text == temp_text:
if cell.text == '': # 不知道什么原因必须这样判断下
if cell.text == '': # 不知道什么原因必须这样判断下
cell.text = '/'
text_temp = cell.text
ce = cell.merge(col_right.cells[index - 1])
@@ -31,7 +31,7 @@ def create_sm_docx(template_name: str, context: dict, id: int) -> ChenResponse:
"""生成最终说明文档工具函数"""
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'sm' / template_name
doc = DocxTemplate(input_path)
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/sm" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -42,7 +42,7 @@ def create_dg_docx(template_name: str, context: dict, id: int) -> ChenResponse:
"""生成最终大纲文档工具函数"""
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / template_name
doc = DocxTemplate(input_path)
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -53,7 +53,7 @@ def create_bg_docx(template_name: str, context: dict, id: int) -> ChenResponse:
"""生成最终报告文档工具函数"""
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'bg' / template_name
doc = DocxTemplate(input_path)
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/bg" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
@@ -64,7 +64,7 @@ def create_wtd_docx(template_name: str, context: dict, id: int) -> ChenResponse:
"""生成最终问题单文档工具函数"""
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'wtd' / template_name
doc = DocxTemplate(input_path)
doc.render(context)
doc.render(context, autoescape=True)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/wtd" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")

View File

@@ -52,7 +52,7 @@ class GenerateSeitaiController(ControllerBase):
'project_ident': self.project_obj.ident,
'project_name': self.project_obj.name,
'test_purpose': "装备鉴定和列装定型" if is_jd else "软件交付和使用",
'sec_title': sec_title,
'sec_title': '密级:' + sec_title,
'sec': sec_title,
'duty_person': duty_person,
'member': self.project_obj.member[0] if len(
@@ -64,7 +64,8 @@ class GenerateSeitaiController(ControllerBase):
self.get_xq_doc_informations()
result = generate_temp_doc('dg', payload.id, frag_list=payload.frag)
if isinstance(result, dict):
return ChenResponse(status=400, code=400, message=result.get('msg', 'dg未报出错误原因反正在生成文档出错'))
return ChenResponse(status=400, code=400,
message=result.get('msg', 'dg未报出错误原因反正在生成文档出错'))
dg_replace_path, dg_seitai_final_path = result
# ~~~~start2025/04/19-新增渲染单个字段(可能封装为函数-对temp文件下的jinja字段处理~~~~
# 现在已经把alias和stdContent对应起来了
@@ -122,7 +123,8 @@ class GenerateSeitaiController(ControllerBase):
self.project_obj = get_object_or_404(Project, id=payload.id)
# seitai文档所需变量
is_jd = True if self.project_obj.report_type == '9' else False
member = self.project_obj.member[0] if len(self.project_obj.member) > 0 else self.project_obj.duty_person
member = self.project_obj.member[0] if len(
self.project_obj.member) > 0 else self.project_obj.duty_person
self.temp_context = {
'project_name': self.project_obj.name,
'project_ident': self.project_obj.ident,
@@ -164,7 +166,8 @@ class GenerateSeitaiController(ControllerBase):
# 将cname存入一个list以便后续拼接给下载函数
cname_list.append(cname)
is_jd = True if self.project_obj.report_type == '9' else False
member = self.project_obj.member[0] if len(self.project_obj.member) > 0 else self.project_obj.duty_person
member = self.project_obj.member[0] if len(
self.project_obj.member) > 0 else self.project_obj.duty_person
# 回归轮次的标识和版本
so_dut: Dut = hround.rdField.filter(type='SO').first()
if not so_dut:
@@ -216,7 +219,8 @@ class GenerateSeitaiController(ControllerBase):
# 取出当前轮次key减1就是上一轮次
cname = self.chinese_round_name[int(hround.key)] # 输出二、三...
cname_list.append(cname)
member = self.project_obj.member[0] if len(self.project_obj.member) > 0 else self.project_obj.duty_person
member = self.project_obj.member[0] if len(
self.project_obj.member) > 0 else self.project_obj.duty_person
is_jd = True if self.project_obj.report_type == '9' else False
so_dut: Dut = hround.rdField.filter(type='SO').first()
if not so_dut:
@@ -260,7 +264,8 @@ class GenerateSeitaiController(ControllerBase):
"""生成最后的问题单"""
self.project_obj = get_object_or_404(Project, id=payload.id)
# seitai文档所需变量
member = self.project_obj.member[0] if len(self.project_obj.member) > 0 else self.project_obj.duty_person
member = self.project_obj.member[0] if len(
self.project_obj.member) > 0 else self.project_obj.duty_person
is_jd = True if self.project_obj.report_type == '9' else False
self.temp_context = {
"project_name": self.project_obj.name,
@@ -272,7 +277,8 @@ class GenerateSeitaiController(ControllerBase):
} | DocTime(payload.id).wtd_final_time()
result = generate_temp_doc('wtd', payload.id, frag_list=payload.frag)
if isinstance(result, dict):
return ChenResponse(status=400, code=400, message=result.get('msg', 'wtd未报出错误原因反正在生成文档出错'))
return ChenResponse(status=400, code=400,
message=result.get('msg', 'wtd未报出错误原因反正在生成文档出错'))
wtd_replace_path, wtd_seitai_final_path = result
text_frag_name_list, doc_docx = get_jinja_stdContent_element(wtd_replace_path)
# 文本片段操作
@@ -290,7 +296,8 @@ class GenerateSeitaiController(ControllerBase):
self.project_obj = get_object_or_404(Project, id=payload.id)
# seitai文档所需变量
## 1.判断是否为JD
member = self.project_obj.member[0] if len(self.project_obj.member) > 0 else self.project_obj.duty_person
member = self.project_obj.member[0] if len(
self.project_obj.member) > 0 else self.project_obj.duty_person
is_jd = True if self.project_obj.report_type == '9' else False
self.temp_context = {
'project_name': self.project_obj.name,
@@ -306,7 +313,8 @@ class GenerateSeitaiController(ControllerBase):
} | DocTime(payload.id).bg_final_time()
result = generate_temp_doc('bg', payload.id, frag_list=payload.frag)
if isinstance(result, dict):
return ChenResponse(status=400, code=400, message=result.get('msg', 'bg未报出错误原因反正在生成文档出错'))
return ChenResponse(status=400, code=400,
message=result.get('msg', 'bg未报出错误原因反正在生成文档出错'))
bg_replace_path, bg_seitai_final_path = result
text_frag_name_list, doc_docx = get_jinja_stdContent_element(bg_replace_path)
# 文本片段操作
@@ -375,14 +383,16 @@ class CreateFragmentController(ControllerBase):
frags = self.get_fragment_name_by_document_name(id, documentType)
# 如果没有文档片段-说明没有生成二段文档
if not frags:
return ChenResponse(status=500, code=500, message='文档片段还未生成,请关闭后再打开/或者先下载基础文档')
return ChenResponse(status=500, code=500,
message='文档片段还未生成,请关闭后再打开/或者先下载基础文档')
# 到这里说fragments_files数组有值返回文件名数组
return ChenResponse(data=[fragment for fragment in frags], message='返回文档片段成功')
@staticmethod
def get_fragment_name_by_document_name(id: int, document_name: str):
# 1.找到模版的路径 - 不用异常肯定存在
document_path = main_download_path / project_path(id) / 'form_template' / 'products' / f"{document_name}.docx"
document_path = main_download_path / project_path(
id) / 'form_template' / 'products' / f"{document_name}.docx"
# 2.识别其中的文档片段
frag_list = get_frag_from_document(document_path)
# 3.这里处理报告里第十轮次前端展示问题
@@ -402,7 +412,8 @@ class CreateFragmentController(ControllerBase):
filter_frags = list(filter(lambda x: '测试内容和结果' not in x['frag_name'], frag_list))
# 再找到白名单的“测试内容和结果_”的片段
content_and_result_frags = list(
filter(lambda x: '测试内容和结果' in x['frag_name'] and x['frag_name'] in white_list_frag, frag_list))
filter(lambda x: '测试内容和结果' in x['frag_name'] and x['frag_name'] in white_list_frag,
frag_list))
# 再组合起来返回
filter_frags.extend(content_and_result_frags)
return filter_frags

View File

@@ -31,6 +31,7 @@ class AITestController(ControllerBase):
"children": [
{
"name": "外部32MHz时钟布线到HCLKBUF级冲测试",
"subDescription": "验证外部32MH布线的测试子项描述",
"subStep": [
{
"operation": "配置FPGA逻辑将外部32MHz晶振输入连接到HCLKBUF缓冲器。",
@@ -45,6 +46,7 @@ class AITestController(ControllerBase):
]
}, {
"name": "内部10KHz时钟布线到CLKINT缓冲测试",
"subDescription": "验证内部10KHz时钟布线到CLKINT缓冲的测试子项描述",
"subStep": [
{
"operation": "在FPGA中启用内部10KHz时钟源并将其连接至CLKINT缓冲器。",
@@ -59,6 +61,7 @@ class AITestController(ControllerBase):
]
}, {
"name": "异常情况下的时钟处理测试",
"subDescription": "验证异常情况下的时钟处理测试的测试子项描述",
"subStep": [
{
"operation": "断开外部32MHz晶振输入后尝试进行HCLKBUF配置。",
@@ -76,7 +79,7 @@ class AITestController(ControllerBase):
}
]
return {
"history":[["我是没有用的",json.dumps(res)]]
"history": [["我是没有用的", json.dumps(res)]]
}
# 这是其他common内容接口

View File

@@ -8,8 +8,9 @@ from utils.chen_pagination import MyPagination
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.db.models.functions import Replace
from django.db.models import Q, F, Value
from django.db.models import F, Value
from typing import List
from django.utils import timezone
from utils.chen_response import ChenResponse
from utils.chen_crud import multi_delete_case
from apps.project.models import Design, Dut, Round, TestDemand, Case, CaseStep, Project, Problem
@@ -20,8 +21,9 @@ from utils.util import get_testType
from utils.codes import HTTP_INDEX_ERROR, HTTP_EXISTS_CASES
from apps.project.tools.copyCase import case_move_to_test, case_copy_to_test, case_to_case_copy_or_move
from utils.smallTools.interfaceTools import conditionNoneToBlank
from apps.project.tool.batchTools import parse_case_content_string
# 导入case的schema
from apps.project.schemas.case import CaseModelOutSchemaWithoutProblem
from apps.project.schemas.case import CaseModelOutSchemaWithoutProblem, BatchCreateCaseInputSchema
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['测试用例接口'])
class CaseController(ControllerBase):
@@ -120,7 +122,7 @@ class CaseController(ControllerBase):
@transaction.atomic
def create_case(self, payload: CaseCreateInputSchema):
asert_dict = payload.dict(exclude_none=True)
# 构造design_key
# 构造demand_key
test_whole_key = "".join(
[payload.round_key, "-", payload.dut_key, '-', payload.design_key, '-', payload.test_key])
# 查询当前key应该为多少
@@ -157,6 +159,52 @@ class CaseController(ControllerBase):
CaseStep.objects.bulk_create(data_list) # type:ignore
return qs
# 批量新增用例
@route.post("/case/multi_save", url_name="case-batch-create")
@transaction.atomic
def multi_case_save(self, payload: BatchCreateCaseInputSchema):
project_obj = get_object_or_404(Project, id=payload.project_id)
user_name = self.context.request.user.name
keys = []
demands = project_obj.ptField.all() # 当前项目所有测试项
for case_data in payload.cases:
# 解析放在前面防止出错
stepsOrErrorResponse = parse_case_content_string(case_data.test_step)
if isinstance(stepsOrErrorResponse, ChenResponse):
return stepsOrErrorResponse
# 查询当前测试项下case数量以设置case的key
demand_key = case_data.parent_key
demand_obj = demands.filter(key=demand_key).first()
case_count = demand_obj.tcField.count()
key_string = ''.join([demand_key, "-", str(case_count)])
keys.append(key_string)
case_dict = {
"ident": demand_obj.ident,
"name": case_data.name,
"key": key_string,
"initialization": case_data.initialization,
"premise": case_data.premise,
"summarize": case_data.summarize,
"designPerson": user_name,
"testPerson": user_name,
"monitorPerson": user_name,
"project": project_obj,
"round": demand_obj.round,
"dut": demand_obj.dut,
"design": demand_obj.design,
"test": demand_obj,
"exe_time": timezone.now(),
"timing_diagram": case_data.sequence,
"title": case_data.name
}
case_new_obj = Case.objects.create(**case_dict)
case_step_list = []
for step in stepsOrErrorResponse:
case_step_list.append(CaseStep(**{"case": case_new_obj, "operation": step['operation'],
"expect": step['expect']}))
CaseStep.objects.bulk_create(case_step_list)
return ChenResponse(code=60000, status=200, data=keys, message='成功录入用例')
# 更新测试用例
@route.put("/case/update/{id}", response=CaseCreateOutSchema, url_name="case-update")
@transaction.atomic
@@ -283,7 +331,6 @@ class CaseController(ControllerBase):
@route.post("/case/replace/", url_name='case-replace')
@transaction.atomic
def replace_case_step_content(self, payload: ReplaceCaseSchema):
print(payload)
# 1.首先查询项目
project_obj: Project = get_object_or_404(Project, id=payload.project_id)
# 2.查询[所有轮次]的selectRows的id

View File

@@ -20,6 +20,7 @@ from apps.project.schemas.design import DeleteSchema, DesignFilterSchema, Design
ReplaceDesignContentSchema
from apps.project.tools.delete_change_key import design_delete_sub_node_key
from utils.smallTools.interfaceTools import conditionNoneToBlank
from apps.project.tools.auto_create_data import auto_create_renji
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['设计需求数据'])
class DesignController(ControllerBase):
@@ -162,7 +163,7 @@ class DesignController(ControllerBase):
design_delete_sub_node_key(single_qs)
return ChenResponse(message="设计需求删除成功!")
# 给复制功能级联选择器查询所有的设计需求
# 给复制功能级联选择器查询所有的设计需求【这是查项目所有的设计需求】
@route.get("/designDemand/getRelatedDesign", url_name='dut-relatedDesign')
def getRelatedDesign(self, id: int):
project_qs = get_object_or_404(Project, id=id)
@@ -196,3 +197,15 @@ class DesignController(ControllerBase):
# 4.提交更新
replace_count = design_qs.update(**replace_kwargs)
return {'count': replace_count}
# 点击生成人机交互界面测试-注意必须要有界面的软件
@route.get("/create_renji/", url_name='renji')
@transaction.atomic
def create_rj(self, round_id: int, project_id: int):
user_name = self.context.request.user.name # 获取当前用户名
project_obj: Project = get_object_or_404(Project, id=project_id)
dut_qs = Dut.objects.filter(round__key=round_id, project=project_obj, type='XQ').first()
if dut_qs:
auto_create_renji(user_name, dut_qs, project_obj)
return ChenResponse(status=200, message='自动生成人机界面交互测试成功!', data=dut_qs.key)
return ChenResponse(status=402, message='您还未录入需求规格说明文档,请录入后再试')

View File

@@ -1,3 +1,5 @@
from multiprocessing.spawn import old_main_modules
from ninja_extra import api_controller, ControllerBase, route
from ninja import Query
from ninja_jwt.authentication import JWTAuth
@@ -17,13 +19,15 @@ from apps.project.models import Design, Dut, Round, TestDemand, TestDemandConten
from apps.project.schemas.testDemand import DeleteSchema, TestDemandModelOutSchema, TestDemandFilterSchema, \
TestDemandTreeReturnSchema, TestDemandTreeInputSchema, TestDemandCreateOutSchema, \
TestDemandCreateInputSchema, ReplaceDemandContentSchema, PriorityReplaceSchema, \
TestDemandRelatedSchema, TestDemandExistRelatedSchema, DemandCopyToDesignSchema
TestDemandRelatedSchema, TestDemandExistRelatedSchema, DemandCopyToDesignSchema, \
TestDemandMultiCreateInputSchema
# 导入ORM
from apps.project.models import Project
# 导入工具
from apps.project.tools.copyDemand import demand_copy_to_design
from apps.project.tools.delete_change_key import demand_delete_sub_node_key
from utils.smallTools.interfaceTools import conditionNoneToBlank
from apps.project.tool.batchTools import parse_test_content_string
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['测试项接口'])
class TestDemandController(ControllerBase):
@@ -134,13 +138,13 @@ class TestDemandController(ControllerBase):
asert_dict.pop("dut_key")
asert_dict.pop("design_key")
asert_dict.pop("testContent")
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 创建测试项 - 以及子项/子项步骤
qs = TestDemand.objects.create(**asert_dict)
for item in payload.dict()['testContent']:
content_obj = TestDemandContent.objects.create(
testDemand=qs,
subName=item['subName']
subName=item['subName'],
subDescription=item['subDescription']
)
TestDemandContentStep.objects.bulk_create([
TestDemandContentStep(
@@ -151,6 +155,69 @@ class TestDemandController(ControllerBase):
])
return qs
# 批量新增测试项
@route.post("/testDemand/multi_save", url_name="testDemand-multi-create")
@transaction.atomic
def create_multi_test_demand(self, payload: TestDemandMultiCreateInputSchema):
# 1.首先判断测试项标识是否重复
project_qs = Project.objects.filter(id=payload.project_id).first()
designs = project_qs.psField.all()
## 给返回response的data数据以便前端更新树状目录
keys = []
## 遍历payload.demands数组
for index, demandOne in enumerate(payload.demands):
if demandOne.ident and project_qs:
old_obj = project_qs.ptField.filter(ident=demandOne.ident).first()
if old_obj and old_obj.testType == demandOne.testType:
message_temp = f"{index}个测试项标识重复,请修改"
return ChenResponse(status=200, code=500101, data=index, message=message_temp)
# 标识不重复就开始录入了
for index, demand in enumerate(payload.demands):
create_sub_demands = parse_test_content_string(demand.testContent)
if isinstance(create_sub_demands, ChenResponse):
return create_sub_demands
else:
# 这说明解析成功了
# 首先查询所属design、dut、round方便新增
design_obj: Design = designs.filter(key=demand.parent_key).first() # 因为前端限制必然有
dut_obj = design_obj.dut
round_obj = design_obj.round
test_demand_count = TestDemand.objects.filter(project=project_qs,
design=design_obj).count()
key_string = ''.join([design_obj.key, "-", str(test_demand_count)])
keys.append(key_string)
create_demand_dict = {
'ident': demand.ident,
'name': demand.name,
'adequacy': demand.adequacy,
'priority': demand.priority,
'testType': demand.testType,
'testMethod': demand.testMethod,
'title': demand.name,
'key': key_string,
'project': project_qs,
'round': round_obj,
'dut': dut_obj,
'design': design_obj,
'testDesciption': demand.testDesciption
}
demand_created = TestDemand.objects.create(**create_demand_dict)
# 录入测试子项
for sub in create_sub_demands:
content_obj = TestDemandContent.objects.create(
testDemand=demand_created,
subName=sub['subName'],
subDescription=sub['subDescription']
)
TestDemandContentStep.objects.bulk_create([
TestDemandContentStep(
testDemandContent=content_obj,
**step.dict() if not isinstance(step, dict) else step
)
for step in sub['subStep']
])
return ChenResponse(code=200991, status=200, data=keys, message='成功录入')
# 更新测试项
@route.put("/testDemand/update/{id}", response=TestDemandCreateOutSchema, url_name="testDemand-update")
@transaction.atomic
@@ -184,7 +251,8 @@ class TestDemandController(ControllerBase):
if item['subName']:
content_obj = TestDemandContent.objects.create(
testDemand=testDemand_qs,
subName=item["subName"]
subName=item["subName"],
subDescription=item["subDescription"]
)
TestDemandContentStep.objects.bulk_create([
TestDemandContentStep(
@@ -224,7 +292,7 @@ class TestDemandController(ControllerBase):
demand_delete_sub_node_key(single_qs) # 删除后需重排子节点
return ChenResponse(message="测试需求删除成功!")
# 查询一个项目的所有测试项
# 查询一个项目的所有测试项【当前轮次】
@route.get("/testDemand/getRelatedTestDemand", url_name="testDemand-getRelatedTestDemand")
@transaction.atomic
def getRelatedTestDemand(self, id: int, round: str):
@@ -236,7 +304,7 @@ class TestDemandController(ControllerBase):
for design in designs:
design_dict = {'label': design.name, 'value': design.id, 'children': []}
for test_item in design.dtField.all():
test_item_dict = {'label': test_item.name, 'value': test_item.id}
test_item_dict = {'label': test_item.name, 'value': test_item.id, 'key': test_item.key}
design_dict['children'].append(test_item_dict)
data_list.append(design_dict)
return ChenResponse(message='获取成功', data=data_list)

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0 on 2025-12-15 09:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('project', '0017_alter_testdemandcontentstep_id'),
]
operations = [
migrations.AddField(
model_name='testdemandcontent',
name='subDescription',
field=models.CharField(blank=True, max_length=1024, null=True, verbose_name='测试子项一句话描述'),
),
]

View File

@@ -13,12 +13,15 @@ class Project(CoreModel):
objects = models.Manager()
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="项目标识",
help_text="项目标识", unique=True) # 唯一
name = models.CharField(max_length=100, blank=True, null=True, verbose_name="项目名称", help_text="项目名称")
name = models.CharField(max_length=100, blank=True, null=True, verbose_name="项目名称",
help_text="项目名称")
beginTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="开始时间",
verbose_name="开始时间")
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间", verbose_name="结束时间")
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间",
verbose_name="结束时间")
duty_person = models.CharField(max_length=64, verbose_name="负责人", help_text="负责人")
member = models.JSONField(null=True, blank=True, help_text="项目成员", verbose_name="项目成员", default=create_list)
member = models.JSONField(null=True, blank=True, help_text="项目成员", verbose_name="项目成员",
default=create_list)
# 8月新增字段
quality_person = models.CharField(max_length=64, verbose_name="质量保证员", help_text="质量保证员")
vise_person = models.CharField(max_length=64, verbose_name="质量监督员", help_text="质量监督员")
@@ -30,7 +33,8 @@ class Project(CoreModel):
default=create_list)
plant_type = models.JSONField(null=True, blank=True, help_text="平台类型", verbose_name="平台类型",
default=create_list)
report_type = models.CharField(max_length=64, blank=True, null=True, verbose_name="报告类型", help_text="报告类型")
report_type = models.CharField(max_length=64, blank=True, null=True, verbose_name="报告类型",
help_text="报告类型")
language = models.JSONField(null=True, blank=True, help_text="被测语言", verbose_name="被测语言",
default=create_list)
standard = models.JSONField(null=True, blank=True, help_text="依据标准", verbose_name="依据标准",
@@ -56,10 +60,12 @@ class Project(CoreModel):
help_text="测评中心电话")
test_email = models.CharField(max_length=64, blank=True, null=True, verbose_name="测评中心邮箱",
help_text="测评中心邮箱")
step = models.CharField(max_length=8, blank=True, null=True, verbose_name="项目阶段", help_text="项目阶段")
step = models.CharField(max_length=8, blank=True, null=True, verbose_name="项目阶段",
help_text="项目阶段")
abbreviation = models.JSONField(null=True, blank=True, help_text="缩略语", verbose_name="缩略语",
default=create_list)
soft_type = models.SmallIntegerField(verbose_name='软件类型', choices=((1, '新研'), (2, '改造'), (3, '沿用')),
soft_type = models.SmallIntegerField(verbose_name='软件类型',
choices=((1, '新研'), (2, '改造'), (3, '沿用')),
default=1)
runtime = models.CharField(max_length=8, blank=True, null=True, verbose_name="运行环境",
help_text="运行环境")
@@ -85,23 +91,31 @@ class Round(CoreModel):
help_text="轮次名称")
beginTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="开始时间",
verbose_name="开始时间")
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间", verbose_name="结束时间")
grade = models.CharField(max_length=64, blank=True, null=True, verbose_name="等级", help_text="等级", default='1')
best_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="最优工况电压",
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间",
verbose_name="结束时间")
grade = models.CharField(max_length=64, blank=True, null=True, verbose_name="等级", help_text="等级",
default='1')
best_condition_voltage = models.CharField(max_length=64, blank=True, null=True,
verbose_name="最优工况电压",
help_text="最优工况电压")
best_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="最优工况温度",
help_text="最优工况温度")
typical_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="典型工况电压",
typical_condition_voltage = models.CharField(max_length=64, blank=True, null=True,
verbose_name="典型工况电压",
help_text="典型工况电压")
typical_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="典型工况温度",
typical_condition_tem = models.CharField(max_length=64, blank=True, null=True,
verbose_name="典型工况温度",
help_text="典型工况温度")
low_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="最低工况电压",
low_condition_voltage = models.CharField(max_length=64, blank=True, null=True,
verbose_name="最低工况电压",
help_text="最低工况电压")
low_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="最低工况温度",
help_text="最低工况温度")
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pField", on_delete=models.CASCADE,
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='pQuery')
level = models.CharField(max_length=15, verbose_name='树状级别第一级', help_text="树状级别第一级", default='0')
level = models.CharField(max_length=15, verbose_name='树状级别第一级', help_text="树状级别第一级",
default='0')
key = models.CharField(max_length=15, verbose_name='给前端的树状级别', help_text="给前端的树状级别")
title = models.CharField(max_length=15, verbose_name='给前端的name', help_text="给前端的name")
# 新增执行地点
@@ -120,30 +134,38 @@ class Dut(CoreModel):
objects = models.Manager()
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="被测件标识",
help_text="被测件标识") # 后面加上unique=True
type = models.CharField(max_length=16, blank=True, null=True, verbose_name="被测件类型", help_text="被测件类型")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="被测件名称", help_text="被测件名称")
type = models.CharField(max_length=16, blank=True, null=True, verbose_name="被测件类型",
help_text="被测件类型")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="被测件名称",
help_text="被测件名称")
# 2025年4月28日更新分为总函数、有效代码行数、注释行数
total_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='总行数')
effective_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='有效代码行数')
comment_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='注释行数')
# 更新结束
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称",
help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-key", help_text="树-key")
# 被测件添加版本、发布单位、发布时间
version = models.CharField(max_length=64, blank=True, null=True, verbose_name="发布版本", help_text="发布版本")
version = models.CharField(max_length=64, blank=True, null=True, verbose_name="发布版本",
help_text="发布版本")
release_union = models.CharField(max_length=64, blank=True, null=True, verbose_name="发布版本",
help_text="发布版本")
release_date = models.DateField(auto_now_add=True, null=True, blank=True, help_text="发布时间",
verbose_name="发布时间")
# 新增用户文档的编号
ref = models.CharField(max_length=32, blank=True, null=True, verbose_name="文档编号", help_text="文档编号")
ref = models.CharField(max_length=32, blank=True, null=True, verbose_name="文档编号",
help_text="文档编号")
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level",
help_text="树-level",
default=1) # 默认为1
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pdField", on_delete=models.CASCADE,
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pdField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='pdQuery')
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rdField", on_delete=models.CASCADE,
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rdField",
on_delete=models.CASCADE,
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rdQuery')
def __str__(self):
@@ -159,7 +181,8 @@ class DutMetrics(models.Model):
objects = models.Manager()
id = ShortUUIDField(primary_key=True, help_text="id", verbose_name="id")
# 外键Dut一个Dut储存一个指标
dut = models.OneToOneField(Dut, on_delete=models.CASCADE, related_name='metrics', related_query_name='metrics',
dut = models.OneToOneField(Dut, on_delete=models.CASCADE, related_name='metrics',
related_query_name='metrics',
db_constraint=False, verbose_name='归属源代码被测件')
avg_function_lines = models.IntegerField(verbose_name='平均模块大小')
avg_cyclomatic = models.IntegerField(verbose_name='平均圈复杂度')
@@ -173,19 +196,25 @@ class Design(CoreModel):
objects = models.Manager()
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="设计需求标识",
help_text="设计需求标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="设计需求名称", help_text="设计需求名称")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="设计需求名称",
help_text="设计需求名称")
demandType = models.CharField(max_length=8, blank=True, null=True, verbose_name="设计需求类型",
help_text="设计需求类型")
description = HTMLField(blank=True, null=True, verbose_name="设计需求描述", help_text="设计需求描述")
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称",
help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey",
help_text="round-dut-designkey")
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level",
help_text="树-level",
default=2) # 默认为2
chapter = models.CharField(max_length=64, blank=True, verbose_name="设计需求章节号", help_text="设计需求章节号")
project = models.ForeignKey(to="Project", db_constraint=False, related_name="psField", on_delete=models.CASCADE,
chapter = models.CharField(max_length=64, blank=True, verbose_name="设计需求章节号",
help_text="设计需求章节号")
project = models.ForeignKey(to="Project", db_constraint=False, related_name="psField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='psQuery')
round = models.ForeignKey(to="Round", db_constraint=False, related_name="dsField", on_delete=models.CASCADE,
round = models.ForeignKey(to="Round", db_constraint=False, related_name="dsField",
on_delete=models.CASCADE,
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rsQuery')
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="rsField", on_delete=models.CASCADE,
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rsQuery')
@@ -214,29 +243,42 @@ class TestDemand(CoreModel):
"""测试项"""
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="测试需求标识",
help_text="测试需求标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="测试需求名称", help_text="测试需求名称")
adequacy = models.CharField(max_length=256, blank=True, null=True, verbose_name="充分条件", help_text="充分条件")
priority = models.CharField(max_length=8, blank=True, null=True, verbose_name="优先级", help_text="优先级")
testType = models.CharField(max_length=8, null=True, blank=True, help_text="测试类型", verbose_name="测试类型",
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="测试需求名称",
help_text="测试需求名称")
adequacy = models.CharField(max_length=256, blank=True, null=True, verbose_name="充分条件",
help_text="充分条件")
priority = models.CharField(max_length=8, blank=True, null=True, verbose_name="优先级",
help_text="优先级")
testType = models.CharField(max_length=8, null=True, blank=True, help_text="测试类型",
verbose_name="测试类型",
default="1")
testMethod = models.JSONField(blank=True, help_text="测试方法", verbose_name="测试方法", default=create_list)
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey-testdemand",
testMethod = models.JSONField(blank=True, help_text="测试方法", verbose_name="测试方法",
default=create_list)
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称",
help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True,
verbose_name="round-dut-designkey-testdemand",
help_text="round-dut-designkey-testdemand")
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level",
help_text="树-level",
default=3) # 默认为3
project = models.ForeignKey(to="Project", db_constraint=False, related_name="ptField", on_delete=models.CASCADE,
project = models.ForeignKey(to="Project", db_constraint=False, related_name="ptField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='ptQuery')
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rtField", on_delete=models.CASCADE,
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rtField",
on_delete=models.CASCADE,
verbose_name='归属轮次', help_text='归属轮次', related_query_name='dutQuery')
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="dutField", on_delete=models.CASCADE,
verbose_name='归属被测件', help_text='归属被测件', related_query_name='dtQuery')
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dtField", on_delete=models.CASCADE,
verbose_name='归属设计需求', help_text='归属设计需求', related_query_name='dtQuery')
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dtField",
on_delete=models.CASCADE,
verbose_name='归属设计需求', help_text='归属设计需求',
related_query_name='dtQuery')
otherDesign = models.ManyToManyField(to="Design", db_constraint=False, related_name="odField",
related_query_name='odQuery', blank=True)
# 新模版要求:测试项描述对整个测试项进行描述
testDesciption = models.CharField(max_length=1024, blank=True, null=True, verbose_name='测试项描述', default="",
testDesciption = models.CharField(max_length=1024, blank=True, null=True, verbose_name='测试项描述',
default="",
help_text='测试项描述')
def __str__(self):
@@ -246,11 +288,15 @@ class TestDemandContent(CoreModel):
objects = models.Manager()
"""测试方法中的测试子项内容"""
testDemand = models.ForeignKey(to="TestDemand", db_constraint=False, related_name="testQField",
on_delete=models.CASCADE, verbose_name='归属的测试项', help_text='归属的测试项',
on_delete=models.CASCADE, verbose_name='归属的测试项',
help_text='归属的测试项',
related_query_name='testQField')
# 2025年4月16去掉subDesc、condition、observe
# 4月17修改因为新增步骤所以把operation和expect弄到下面Model里面了新增字段
# 2025/4/16去掉subDesc、condition、observe
# 2025/4/17修改因为新增步骤所以把operation和expect弄到下面Model里面了新增字段
subName = models.CharField(max_length=1024, blank=True, null=True, verbose_name='测试子项名称')
# 2025/12/15修改CPU新增“测试子项描述”FPGA渲染单个
subDescription = models.CharField(max_length=1024, blank=True, null=True,
verbose_name='测试子项一句话描述')
def __str__(self):
return f'测试子项:{self.subName}'
@@ -260,38 +306,55 @@ class TestDemandContentStep(CoreModel):
objects = models.Manager()
operation = models.CharField(max_length=3072, blank=True, null=True, verbose_name='测试子项操作')
expect = models.CharField(max_length=1024, blank=True, null=True, verbose_name='期望')
testDemandContent = models.ForeignKey(to="TestDemandContent", db_constraint=False, related_name="testStepField",
testDemandContent = models.ForeignKey(to="TestDemandContent", db_constraint=False,
related_name="testStepField",
on_delete=models.CASCADE, verbose_name='归属的测试项',
help_text='归属的测试项',
related_query_name='testStepField')
class Case(CoreModel):
objects = models.Manager()
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例标识", help_text="用例标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例名称", help_text="用例名称")
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例标识",
help_text="用例标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例名称",
help_text="用例名称")
initialization = models.CharField(max_length=128, blank=True, null=True, verbose_name="初始条件",
help_text="初始化条件")
premise = models.CharField(max_length=128, blank=True, null=True, verbose_name="前提和约束", help_text="前提和约束")
summarize = models.CharField(max_length=256, blank=True, null=True, verbose_name="用例综述", help_text="用例综述")
designPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="设计人员", help_text="设计人员")
testPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="测试人员", help_text="测试人员")
premise = models.CharField(max_length=128, blank=True, null=True, verbose_name="前提和约束",
help_text="前提和约束")
summarize = models.CharField(max_length=256, blank=True, null=True, verbose_name="用例综述",
help_text="用例综述")
designPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="设计人员",
help_text="设计人员")
testPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="测试人员",
help_text="测试人员")
monitorPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="审核人员",
help_text="审核人员")
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pcField", on_delete=models.CASCADE,
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pcField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='pcQuery')
isLeaf = models.BooleanField(default=True, verbose_name="树状图最后一个节点", help_text="树状图最后一个节点")
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rcField", on_delete=models.CASCADE,
isLeaf = models.BooleanField(default=True, verbose_name="树状图最后一个节点",
help_text="树状图最后一个节点")
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rcField",
on_delete=models.CASCADE,
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rcQuery')
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="ducField", on_delete=models.CASCADE,
verbose_name='归属被测件', help_text='归属被测件', related_query_name='ducQuery')
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dcField", on_delete=models.CASCADE,
verbose_name='归属设计需求', help_text='归属设计需求', related_query_name='dcQuery')
test = models.ForeignKey(to="TestDemand", db_constraint=False, related_name="tcField", on_delete=models.CASCADE,
verbose_name='归属测试需求', help_text='归属测试需求', related_query_name='tcQuery')
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey-testdemand-case",
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dcField",
on_delete=models.CASCADE,
verbose_name='归属设计需求', help_text='归属设计需求',
related_query_name='dcQuery')
test = models.ForeignKey(to="TestDemand", db_constraint=False, related_name="tcField",
on_delete=models.CASCADE,
verbose_name='归属测试需求', help_text='归属测试需求',
related_query_name='tcQuery')
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称",
help_text="树-名称")
key = models.CharField(max_length=64, blank=True, null=True,
verbose_name="round-dut-designkey-testdemand-case",
help_text="round-dut-designkey-testdemand-case")
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level",
help_text="树-level",
default=4) # 默认为4
# 2024年5月31日新增属性执行时间
exe_time = models.DateField(blank=True, null=True, verbose_name='执行时间', help_text='执行时间')
@@ -310,14 +373,17 @@ class Case(CoreModel):
class CaseStep(CoreModel):
objects = models.Manager()
operation = HTMLField(blank=True, null=True, verbose_name="测试步骤-操作", help_text="测试步骤-操作")
expect = models.CharField(max_length=3072, blank=True, null=True, verbose_name="用例预期", help_text="用例预期")
expect = models.CharField(max_length=3072, blank=True, null=True, verbose_name="用例预期",
help_text="用例预期")
result = HTMLField(blank=True, null=True, verbose_name="测试步骤-结果", help_text="测试步骤-结果")
passed = models.CharField(max_length=8, null=True, blank=True, help_text="是否通过", verbose_name="是否通过",
passed = models.CharField(max_length=8, null=True, blank=True, help_text="是否通过",
verbose_name="是否通过",
default="3")
# status = models.CharField(max_length=8, null=True, blank=True, help_text="执行状态", verbose_name="执行状态",
# default="3")
case = models.ForeignKey(to="Case", db_constraint=False, related_name="step",
on_delete=models.CASCADE, verbose_name='归属的测试用例', help_text='归属的测试用例',
on_delete=models.CASCADE, verbose_name='归属的测试用例',
help_text='归属的测试用例',
related_query_name='stepQ')
def __str__(self):
@@ -326,34 +392,46 @@ class CaseStep(CoreModel):
class Problem(CoreModel):
objects = models.Manager()
# ident为PT_RXXXX_ident这里需要根据测试项类型进行排序处理
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单标识", help_text="问题单标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单名称", help_text="问题单名称")
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单标识",
help_text="问题单标识")
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单名称",
help_text="问题单名称")
# 问题状态1-已闭环 2-开放 3-推迟 4-撤销
status = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷状态", help_text="缺陷状态")
status = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷状态",
help_text="缺陷状态")
# 问题等级1-一般 2-严重 3-建议 4-重大
grade = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷等级", help_text="缺陷等级")
grade = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷等级",
help_text="缺陷等级")
# 问题类型1-其他问题 2-文档问题 3-程序问题 4-设计问题 5-需求问题 6-数据问题
type = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷类型", help_text="缺陷类型")
type = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷类型",
help_text="缺陷类型")
closeMethod = models.JSONField(null=True, blank=True, help_text="闭环方式", verbose_name="闭环方式",
default=create_list_1)
operation = HTMLField(blank=True, null=True, verbose_name="问题描述", help_text="问题描述")
result = HTMLField(blank=True, null=True, verbose_name="问题结果/影响", help_text="问题结果/影响")
postPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="提出人员", help_text="提出人员")
postDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="提单日期", verbose_name="提单日期")
postPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="提出人员",
help_text="提出人员")
postDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="提单日期",
verbose_name="提单日期")
designerPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="开发人员",
help_text="开发人员")
designDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="确认日期",
verbose_name="确认日期")
verifyPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="验证人员", help_text="验证人员")
verifyPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="验证人员",
help_text="验证人员")
verifyDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="验证日期",
verbose_name="验证日期")
project = models.ForeignKey(to="Project", db_constraint=False, related_name="projField", on_delete=models.CASCADE,
project = models.ForeignKey(to="Project", db_constraint=False, related_name="projField",
on_delete=models.CASCADE,
verbose_name='归属项目', help_text='归属项目', related_query_name='projQuery')
case = models.ManyToManyField(to="Case", db_constraint=False, related_name="caseField", verbose_name='归属测试用例',
case = models.ManyToManyField(to="Case", db_constraint=False, related_name="caseField",
verbose_name='归属测试用例',
help_text='归属测试用例-多对多', related_query_name='caseQuery')
solve = models.TextField(verbose_name='开发人员填写-改正措施',
help_text='开发人员填写-改正措施该字段需要关联“status=1”', blank=True, null=True)
analysis = HTMLField(blank=True, null=True, verbose_name="开发人员填写-原因分析", help_text="开发人员填写-原因分析")
help_text='开发人员填写-改正措施该字段需要关联“status=1”', blank=True,
null=True)
analysis = HTMLField(blank=True, null=True, verbose_name="开发人员填写-原因分析",
help_text="开发人员填写-原因分析")
effect_scope = HTMLField(blank=True, null=True, verbose_name="开发人员填写-影响域分析",
help_text="开发人员填写-影响域分析")
verify_result = HTMLField(blank=True, null=True, verbose_name="回归结果", help_text="回归结果")

View File

@@ -126,6 +126,20 @@ class CaseCreateInputSchema(Schema):
# 新增时序图字段
timing_diagram: str = Field("", alias="timing_diagram")
# 批量新增测试用例
class OneCaseBatchCreateSchema(Schema):
parent_key: str
name: str
summarize: Optional[str] = ""
initialization: Optional[str] = ""
premise: Optional[str] = ""
sequence: Optional[str] = "" # 时序图
test_step: str
class BatchCreateCaseInputSchema(Schema):
project_id: int = Field(..., validation_alias=AliasChoices('project_id', 'projectId'))
cases: List[OneCaseBatchCreateSchema] = []
# 由demand创建case的输入Schema
class DemandNodeSchema(Schema):
project_id: int

View File

@@ -15,6 +15,7 @@ class TestContentStepSchema(ModelSchema):
fields = ['operation', 'expect']
class TestContentSchema(ModelSchema):
subDescription: Optional[str] = ""
subStep: List[TestContentStepSchema] = [] # 可能为空
class Meta:
@@ -76,6 +77,8 @@ class TestDemandCreateOutSchema(ModelSchema):
# 新增测试子项单个子项的Schema
class TestContentInputSchema(Schema):
subName: str = None
# 2025/12/15-对CPU增加测试子项描述
subDescription: Optional[str] = "" # 未提供时为空字符串
subStep: Optional[List[TestContentStepSchema]] = []
# 新增/更新测试项Schema
@@ -95,6 +98,22 @@ class TestDemandCreateInputSchema(Schema):
testType: str = Field(None, alias="testType")
testDesciption: str = Field("", alias='testDesciption')
# 批量新增测试项Schema-2个Schema
class TestDemandOneInput(Schema):
parent_key: str # 直接给设计需求的key前端去组装
ident: str
name: str
priority: str = "1"
adequacy: str
testContent: str # 注意这个在接口里面分情况判断
testMethod: List[str] = []
testType: str
testDesciption: Optional[str] = ""
class TestDemandMultiCreateInputSchema(Schema):
project_id: int
demands: List[TestDemandOneInput]
# 处理前端请求-设计需求关联测试需求(测试项)
class TestDemandRelatedSchema(Schema):
data: List[int] = None

View File

@@ -0,0 +1,111 @@
import re
from utils.chen_response import ChenResponse
def parse_test_content_string(content: str):
"""
解析前端传来的批量新增测试项testContent字段
"""
# 判断是否为空字符串
if not content or not content.strip():
return []
create_subDemands = [] # 储存测试子项内容
current_subDemand: None | dict = None
lines = content.strip().split("\n")
line_number = 0
for i, line in enumerate(lines):
line_number = i + 1
line = line.strip()
# 跳过空行
if not line:
continue
# 检查是否以^开头
if line.startswith("^"):
# 标识一个测试子项开始了
if current_subDemand:
create_subDemands.append(current_subDemand)
# 解析新测试子项
try:
# 检查是否包含分隔符@,包含则分割
if '@' in line:
[item_name, item_desc] = line.split("@")
else:
item_name = line
item_desc = ""
# 判断名称是否为空
item_name = item_name.replace("^", "", count=1)
if not item_name:
message = f"您字符串中,第{line_number}行没有测试子项名称"
return ChenResponse(status=200, code=500102, data=line_number, message=message)
# 组装一个测试子项
current_subDemand = {
'subName': item_name,
'subDescription': item_desc,
'subStep': []
}
except Exception as e:
message = f"您字符串中,第{line_number}行解析错误,错误原因请检查"
print('解析^行报错,后台详情:', e)
return ChenResponse(status=200, code=500103, data=line_number, message=message)
elif '@' in line and current_subDemand is not None:
try:
[operation, expect] = line.split('@')
current_subDemand['subStep'].append({ # type:ignore
'operation': operation,
'expect': expect
})
except Exception as e:
message = f"{line_number}发现您子项步骤格式有问题,请检查"
print('解析步骤行报错,后台详情:', e)
return ChenResponse(status=200, code=500104, data=line_number, message=message)
else:
# 这里就是即没有^也没有@的情况,直接跳出本次循环即可
continue
# 添加最后一个测试项
if current_subDemand:
create_subDemands.append(current_subDemand)
return create_subDemands
def parse_case_content_string(content: str):
"""
解析前端传来的批量新增测试用例test_step字段
"""
# 如果为空返回空列表-不会引起错误因为前端限制
if not content or not content.strip():
return []
create_step = [] # 储存测试子项内容
current_step: None | dict = None
lines = content.strip().split("\n")
line_number = 0
for i, line in enumerate(lines):
line_number = i + 1
line = line.strip()
# 跳过空行
if not line:
continue
# 判断是否有“@”,如果没有则给用户报错
if "@" not in line:
message = f"{line_number}行没有使用@符号分割,请检查!"
return ChenResponse(status=200, code=60001, data=line_number, message=message)
# 这里必然有@组装current_step
[operation, expect] = line.strip().split("@")
# 错误处理两种情况,操作为空,预期为空
if not operation.strip():
message = f"{line_number}行@符号前面的输入内容为空,请检查!"
return ChenResponse(status=200, code=60002, data=line_number, message=message)
if not expect.strip():
message = f"{line_number}行@符号后面的预期为空,请检查!"
return ChenResponse(status=200, code=60003, data=line_number, message=message)
# 组装当前步骤行
current_step = {
"operation": operation,
"expect": expect
}
if current_step:
create_step.append(current_step)
return create_step

View File

@@ -10,6 +10,9 @@ from apps.project.models import (
CaseStep
)
# 导入人机交互界面固定数据
from apps.project.tools.rj_data_cont import rj_data
def auto_create_jt_and_dm(user_name: str, dut_qs: Dut, project_obj: Project):
"""传入源代码dut以及测试人员名称username自动在dut下面生成静态分析和代码审查设计需求、测试项、用例"""
# 先查询dut_qs下面有多少design以便写里面的key
@@ -188,20 +191,22 @@ def auto_create_wd(user_name: str, dut_qs: Dut, project_obj: Project):
'testMethod': ["3"],
'title': '文档审查',
'testDesciption': '本次文档审查包括的内容如下:\a'
'1软件研制总结报告\a'
'2软件开发计划\a'
'3软件运行方案说明\a'
'4软件接口需求规格说明\a'
'5软件系统设计说明\a'
'6软件接口设计说明\a'
'7软件需求规格说明\a'
'8软件配置项设计说明\a'
'9软件测试说明\a'
'10软件测试报告\a'
'11产品规格说明\a'
'12软件版本说明\a'
'13软件用户手册\a'
'14固件保障手册',
'1软件需求规格说明\a'
'2软件详细设计说明\a'
'3软件开发计划\a'
'4软件配置管理计划\a'
'5软件质量保证计划\a'
'6软件单元测试计划\a'
'7软件单元测试说明\a'
'8软件单元测试报告\a'
'9配置项测试计划\a'
'10配置项测试说明\a'
'11配置项测试报告\a'
'12软件用户手册\a'
'13软件研制总结报告\a'
'14软件版本说明\a'
'15软件产品规格说明\a'
'16固件保障手册',
'key': ''.join([new_wd_design_obj.key, '-', '0']),
'level': '3',
'project': project_obj,
@@ -216,7 +221,10 @@ def auto_create_wd(user_name: str, dut_qs: Dut, project_obj: Project):
operation='根据文档审查表人工逐项检查,检查此项目文档的齐套性、完整性、规范性:\a'
'1使用人工审查方法按照附录A中文档齐套性审查单检查需求类、设计类、用户类、测试类文档是否齐套\a'
'2使用人工审查方法按照附录A中需求规格说明审查单对软件需求规格说明逐项检查\a'
'3使用人工审查方法按照附录A中软件设计文档审查单逐项检查。'
'3使用人工审查方法按照附录A中软件设计文档审查单逐项检查。',
expect='被测软件文档种类齐全,内容完整,描述准确,格式规范;\a'
'2需求文档内容完整描述准确格式规范文档文文一致、文实相符\a'
'3设计说明文档内容完整描述准确格式规范文档文文一致、文实相符。',
)
new_wd_case_obj = Case.objects.create(
ident='WDSC',
@@ -247,3 +255,74 @@ def auto_create_wd(user_name: str, dut_qs: Dut, project_obj: Project):
expect='文档满足完整性、准确性、规范性和一致性的要求',
result='文档检查单全部审查通过,文档内容完整、文档描述准确、'
'文档格式规范、文档文文一致', )
def auto_create_renji(user_name: str, dut_qs: Dut, project_obj: Project):
"""传入用户名、在dut下创建、项目对象自动创建人机交互界面的设计需求、测试项、测试用例"""
# 先查询dut_qs下有多少desgin用于设置key
design_index = dut_qs.rsField.count()
for item in rj_data:
# 创建设计需求
rj_design_create_dict = {
'ident': item['ident'],
'name': item['desgin_name'],
'demandType': '6',
'description': item['xq_desc'],
'title': item['desgin_name'],
'key': ''.join([dut_qs.key, '-', str(design_index)]),
'level': '2',
'chapter': '/',
'project': project_obj,
'round': dut_qs.round,
'dut': dut_qs
}
design_index += 1
new_design_rj: Design = Design.objects.create(**rj_design_create_dict)
# 创建测试项
rj_demand_create_dict = {
'ident': item['ident'],
'name': item['test_item_name'],
'adequacy': item['chongfen'],
'priority': '2',
'testType': '12',
'testMethod': ["4"],
'testDesciption': '在界面进行观察与操作,对照需求规格说明的功能需求进行比对,对照用户手册进行比对',
'title': item['test_item_name'],
'key': ''.join([new_design_rj.key, '-', '0']),
'level': '3',
'project': project_obj,
'round': new_design_rj.round,
'dut': new_design_rj.dut,
'design': new_design_rj,
}
new_demand_rj = TestDemand.objects.create(**rj_demand_create_dict)
new_demand_content_obj = TestDemandContent.objects.create(testDemand=new_demand_rj,
subName=item['test_item_name'])
for operation in item['test_method']:
TestDemandContentStep.objects.create(testDemandContent=new_demand_content_obj,
operation=operation['caozuo']
, expect=operation['yuqi'])
# 创建测试用例
new_case_rj = Case.objects.create(
ident=item['ident'],
name=item['test_item_name'],
initialization='已获取被测件的用户手册',
premise='软件可正常运行,界面初始化完成',
summarize=item['xq_desc'],
designPerson=user_name,
testPerson=user_name,
monitorPerson=user_name,
project=project_obj,
isLeaf=True,
round=new_demand_rj.round,
dut=new_demand_rj.dut,
design=new_demand_rj.design,
test=new_demand_rj,
title=item['test_item_name'],
key=''.join([new_demand_rj.key, '-', '0']),
level='4'
)
for operation in item['test_method']:
CaseStep.objects.create(case=new_case_rj,
operation=operation['caozuo'],
expect=operation['yuqi'],
result='界面操作结果符合预期', )

View File

@@ -0,0 +1,141 @@
# 人机交互界面测试自动生成
rj_data = [
{
"desgin_name": "人机交互完整性",
"test_item_name": "人机交互完整性测试",
"ident": "WZXC",
"xq_desc": "测试软件人机交互界面的完整性",
"chongfen": "测试用例覆盖人机交互界面完整性子项要求的全部内容。\a"
"所有用例执行完毕,对于未执行的用例说明未执行原因。",
"test_method": [
{
"caozuo": "检查界面菜单名称、标签名称、按键名称的完整性",
"yuqi": "名称完整"
},
{
"caozuo": "检查界面显示区内容的完整性",
"yuqi": "显示内容完整"
},
{
"caozuo": "检查界面菜单功能的完整性",
"yuqi": "菜单功能完整"
},
]
},
{
"desgin_name": "人机交互界面一致性",
"test_item_name": "人机交互界面一致性测试",
"ident": "YZXC",
"xq_desc": "测试软件人机交互界面的一致性",
"chongfen": "测试用例覆盖人机交互界面一致性子项要求的全部内容。\a"
"所有用例执行完毕,对于未执行的用例说明未执行原因。",
"test_method": [
{
"caozuo": "检查主界面的标题栏与使用说明书的一致性",
"yuqi": "主界面的标题栏与用户手册一致"
},
{
"caozuo": "检查次级界面、对话框等标题与使用说书的一致性",
"yuqi": "次级界面、对话框等标题与用户手册一致"
},
{
"caozuo": "检查界面用语与使用说明书的一致性",
"yuqi": "界面用语与用户手册一致"
},
{
"caozuo": "检查界面的字体、颜色、位置等是否统一、字体是否对齐",
"yuqi": "界面的字体、颜色、位置等与用户手册一致"
},
{
"caozuo": "检查同类数据的显示精度是否正确、统一",
"yuqi": "同类数据的显示精度与用户手册一致,精确并统一"
},
]
},
{
"desgin_name": "人机交互界面友好性",
"test_item_name": "人机交互界面友好性测试",
"ident": "YHXC",
"xq_desc": "测试软件人机交互界面的友好性",
"chongfen": "测试用例覆盖人机交互界面友好性子项要求的全部内容。\a"
"所有用例执行完毕,对于未执行的用例说明未执行原因。",
"test_method": [
{
"caozuo": "检查界面实时显示信息区是否实时刷新",
"yuqi": "实时刷新"
},
{
"caozuo": "检查提示信息显示位置是否合理、提示框或弹窗是否居中显示",
"yuqi": "合理、弹窗居中"
},
{
"caozuo": "检查界面用语、名称等是否存在错别字、乱码等",
"yuqi": "无错别字或乱码"
},
{
"caozuo": "检查界面字符显示是否清晰",
"yuqi": "字符显示清晰"
},
{
"caozuo": "检查界面字符或提示语是否统一、汉化",
"yuqi": "统一汉化显示"
},
]
},
{
"desgin_name": "人机交互界面易操作性",
"test_item_name": "人机交互界面易操作性测试",
"ident": "YCZX",
"xq_desc": "测试软件人机交互界面的易操作性",
"chongfen": "测试用例覆盖人机交互界面易操作性子项要求的全部内容。\a"
"所有用例执行完毕,对于未执行的用例说明未执行原因。",
"test_method": [
{
"caozuo": "检查界面菜单、图标、按键等是否分布整齐、有序",
"yuqi": "分布整齐有序"
},
{
"caozuo": "检查菜单深度的合理性",
"yuqi": "菜单深度不超过3级"
},
]
},
{
"desgin_name": "人机交互界面操作测试",
"test_item_name": "人机交互界面操作测试测试",
"ident": "CZCC",
"xq_desc": "测试软件人机交互的界面操作测试",
"chongfen": "测试用例覆盖人机交互界面操作测试子项要求的全部内容。\a"
"所有用例执行完毕,对于未执行的用例说明未执行原因。",
"test_method": [
{
"caozuo": "检查按键、编辑框等使能情况(闪烁)",
"yuqi": "闪烁"
},
{
"caozuo": "执行任务期间,进行按键的多处连击",
"yuqi": "不会出现假死或崩溃"
},
{
"caozuo": "检查列表,列表中的内容允许编辑、数据变更后自动更新显示",
"yuqi": "内容允许编辑、数据变更后自动更新显示"
},
{
"caozuo": "能够进行选择和取消,选择及取消后处理正确",
"yuqi": "能够进行选择和取消,选择及取消后处理正确"
},
{
"caozuo": "选择或取消某个单选框后再进行复选操作",
"yuqi": "操作正确"
},
{
"caozuo": "执行一个任务,查看界面操作及显示的正确性",
"yuqi": "界面操作及显示正确"
},
{
"caozuo": "在一个菜单功能项执行过程中用使用按钮选择其他功能",
"yuqi": "不会出现假死或崩溃情况"
},
]
},
]

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More