增加批量增加用例、测试项、设计需求功能
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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="文档生成成功!")
|
||||
|
||||
@@ -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下面的step,content变量是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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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="文档生成成功!")
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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="文档生成成功!")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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”则居中
|
||||
|
||||
@@ -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="文档生成成功!")
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
# ~~~~start:2025/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
|
||||
|
||||
Binary file not shown.
@@ -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内容接口
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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='您还未录入需求规格说明文档,请录入后再试')
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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='测试子项一句话描述'),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
@@ -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="回归结果")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
apps/project/tool/__pycache__/batchTools.cpython-313.pyc
Normal file
BIN
apps/project/tool/__pycache__/batchTools.cpython-313.pyc
Normal file
Binary file not shown.
111
apps/project/tool/batchTools.py
Normal file
111
apps/project/tool/batchTools.py
Normal 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
|
||||
Binary file not shown.
BIN
apps/project/tools/__pycache__/rj_data_cont.cpython-313.pyc
Normal file
BIN
apps/project/tools/__pycache__/rj_data_cont.cpython-313.pyc
Normal file
Binary file not shown.
@@ -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='界面操作结果符合预期', )
|
||||
|
||||
141
apps/project/tools/rj_data_cont.py
Normal file
141
apps/project/tools/rj_data_cont.py
Normal 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": "不会出现假死或崩溃情况"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1107
logs/root_log
1107
logs/root_log
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user