Files
cdtestplant_v1/apps/createDocument/controllers/dg.py

852 lines
43 KiB
Python
Raw Normal View History

from datetime import datetime
2025-04-29 18:09:00 +08:00
from ninja.errors import HttpError
from ninja_extra import ControllerBase, api_controller, route
from django.db import transaction
from django.db.models import Q
from docxtpl import DocxTemplate
from pathlib import Path
from utils.chen_response import ChenResponse
# 导入数据库ORM
from apps.project.models import Project, Contact, Abbreviation
from apps.dict.models import Dict
# 导入工具函数
from utils.util import get_str_dict, get_list_dict, get_testType, get_ident, get_str_abbr
from utils.chapter_tools.csx_chapter import create_csx_chapter_dict
from django.shortcuts import get_object_or_404
from django.forms.models import model_to_dict
from apps.createDocument.extensions.util import create_dg_docx
from apps.createDocument.extensions.parse_rich_text import RichParser
from apps.createDocument.extensions.documentTime import DocTime
from utils.path_utils import project_path
# 记录生成日志
from apps.createSeiTaiDocument.extensions.logger import GenerateLogger
# 导入mixins-处理文档片段
from apps.createDocument.extensions.mixins import FragementToolsMixin
2026-01-28 16:50:40 +08:00
# 导入工具
from apps.createDocument.extensions.tools import demand_sort_by_designKey
2025-04-29 18:09:00 +08:00
# @api_controller("/generate", tags=['生成大纲文档'], auth=JWTAuth(), permissions=[IsAuthenticated])
@api_controller("/generate", tags=['生成大纲文档'])
class GenerateControllerDG(ControllerBase, FragementToolsMixin):
logger = GenerateLogger('测评大纲')
@route.get("/create/testdemand", url_name="create-testdemand")
@transaction.atomic
def create_testdemand(self, id: int): # type:ignore
"""目前生成第一轮测试项"""
tplTestDemandGenerate_path = Path.cwd() / "media" / project_path(
id) / "form_template" / "dg" / "测试项及方法.docx"
2025-04-29 18:09:00 +08:00
doc = DocxTemplate(tplTestDemandGenerate_path)
# 获取指定的项目对象
project_qs = get_object_or_404(Project, id=id)
# 先查询dict字典查出总共有多少个testType
test_type_len = Dict.objects.get(code='testType').dictItem.count()
type_number_list = [i for i in range(1, test_type_len + 1)]
list_list = [[] for _ in range(1, test_type_len + 1)]
# 查出第一轮所有testdemand
project_round_one = project_qs.pField.filter(key=0).first()
2026-01-28 16:50:40 +08:00
testDemand_qs = project_round_one.rtField.all().select_related('design')
# 按照自己key排序这样可以按照design的key排序
sorted_demand_qs = sorted(testDemand_qs, key=demand_sort_by_designKey)
2025-04-29 18:09:00 +08:00
# 遍历第一轮测试项默认是ID排序
2026-01-28 16:50:40 +08:00
for single_qs in sorted_demand_qs:
2025-04-29 18:09:00 +08:00
type_index = type_number_list.index(int(single_qs.testType))
# 先查询其testDemandContent信息
content_list = []
for (index, content) in enumerate(single_qs.testQField.all()):
content_dict = {
"index": index + 1,
"rindex": str(index + 1).rjust(2, '0'),
"subName": content.subName,
"subDescription": content.subDescription,
2025-04-29 18:09:00 +08:00
# 修改遍历content下面的stepcontent变量是TestDemandContent表
"subStep": [
{'index': index + 1, 'operation': step_obj.operation, 'expect': step_obj.expect}
for (index, step_obj) in enumerate(content.testStepField.all())
],
}
content_list.append(content_dict)
# 查询测试项中testMethod
testmethod_str = ''
for dict_item_qs in Dict.objects.get(code="testMethod").dictItem.all():
for tm_item in single_qs.testMethod:
if tm_item == dict_item_qs.key:
testmethod_str += dict_item_qs.title + " "
# 富文本解析
# ***Inspect-start检查设计需求的描述是否为空***
if single_qs.design.description == '':
design_info = single_qs.design.ident + '-' + single_qs.design.name
self.logger.write_warning_log('测试项', f'设计需求中的描述为空,请检查 -> {design_info}')
# ***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}]
for relate_design in single_qs.otherDesign.all():
ddict = {'dut_name': relate_design.dut.name, 'design_chapter': relate_design.chapter,
'design_name': relate_design.name}
doc_list.append(ddict)
# 组装单个测试项
## 打印本项目是FPGA还是CPU
2025-04-29 18:09:00 +08:00
testdemand_dict = {
"name": single_qs.name,
"key": single_qs.key,
"ident": get_ident(single_qs),
"priority": get_str_dict(single_qs.priority, "priority"),
"doc_list": doc_list,
"design_description": desc_list,
"test_demand_content": content_list,
"testMethod": testmethod_str.strip(),
2025-04-29 18:09:00 +08:00
"adequacy": single_qs.adequacy.replace("\n", "\a"),
"testDesciption": single_qs.testDesciption.replace("\n", "\a"), # 测试项描述
"testType": get_testType(single_qs.testType, 'testType'),
2025-04-29 18:09:00 +08:00
}
list_list[type_index].append(testdemand_dict)
output_list = []
for (index, li) in enumerate(list_list):
qs = Dict.objects.get(code="testType").dictItem.get(key=str(index + 1))
context_str = qs.title
sort = qs.sort
table = {
"type": context_str,
"item": li,
"sort": sort
}
output_list.append(table)
# 排序1测试类型排序
output_list = sorted(output_list, key=(lambda x: x["sort"]))
# 定义渲染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)
2025-04-29 18:09:00 +08:00
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / "测试项及方法.docx")
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
@route.get("/create/yiju", url_name='create-yiju')
@transaction.atomic
def create_yiju(self, id: int):
# 先找出所属项目
project_qs = get_object_or_404(Project, id=id)
# 找出该项目的真实依据文件qs
yiju_list = get_list_dict('standard', project_qs.standard)
context = {
'std_documents': yiju_list
}
return create_dg_docx('标准依据文件.docx', context, id)
@route.get("/create/techyiju", url_name='create-techyiju')
@transaction.atomic
def create_techyiju(self, id: int):
# 找出所属项目
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(
2025-04-29 18:09:00 +08:00
round__key='0')
# 先定义个字典
std_documents = []
for duty in duties_qs:
one_duty = {'doc_name': duty.name, 'ident_version': duty.ref + '-' + duty.version,
'publish_date': duty.release_date, 'source': duty.release_union}
std_documents.append(one_duty)
# 生成二级文档
context = {
'std_documents': std_documents
}
return create_dg_docx('技术依据文件.docx', context, id)
@route.get("/create/contact", url_name='create-contact')
@transaction.atomic
def create_contact(self, id: int):
# 先找出所属项目
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',
2025-04-29 18:09:00 +08:00
'dev_contact', 'dev_contact_phone', 'test_unit', 'test_contact',
'test_contact_phone'])
# 根据entrust_unit、dev_unit、test_unit查找Contact中地址信息
entrust_addr = Contact.objects.get(name=contact_dict['entrust_unit']).addr
dev_addr = Contact.objects.get(name=contact_dict['dev_unit']).addr
test_addr = Contact.objects.get(name=contact_dict['test_unit']).addr
contact_dict['entrust_addr'] = entrust_addr
contact_dict['dev_addr'] = dev_addr
contact_dict['test_addr'] = test_addr
context = {
'datas': contact_dict
}
return create_dg_docx('联系人和方式.docx', context, id)
# 生成测评时间和地点
@route.get('/create/timeaddress', url_name='create-timeaddress')
@transaction.atomic
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'])
2025-04-29 18:09:00 +08:00
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
2025-04-29 18:09:00 +08:00
# 生成【主要功能和性能指标】文档片段
@route.get('/create/indicators', url_name='create-indicators')
@transaction.atomic
def create_indicators(self, id: int):
# 获取文档片段模版路径
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '主要功能和性能指标.docx'
doc = DocxTemplate(input_path)
# 获取项目对象
project_obj: Project = get_object_or_404(Project, id=id)
# 定义JINJA上下文
# 获取第一轮次所有功能、性能的设计需求[目前只支持XQ和YZ]因为要获取dut信息进行连表查询
q_ex = Q(dut__type='XQ') | Q(dut__type='YZ')
design_qs = project_obj.psField.filter(q_ex, round__key='0').select_related('dut')
# 1.功能性能覆盖表
# 定义功能/性能两个列表
func_design_list = []
performance_design_list = []
# 遍历设计需求,然后放入两个列表
for design_obj in design_qs:
# 功能指标描述
description = RichParser(design_obj.description).get_final_p_list()
# 覆盖情况-【查询测试项-测试子项名称】
demand_qs = design_obj.dtField.all()
str_list = []
for demand in demand_qs:
# 再查询测试子项
for subDemand in demand.testQField.all():
str_list.append(subDemand.subName)
coverage_str = "".join(str_list)
design_context_obj = {
'chapter_info': f"{design_obj.dut.name}{design_obj.chapter}-{design_obj.name}",
'indicator': "\a".join(description),
'coverage': f"{design_obj.name}进行全覆盖测试,包含{coverage_str},验证所描述内容是否满足需求等文档的要求"
}
demandType_str = get_str_dict(design_obj.demandType, 'demandType')
# 判断是否包含“功能”/“性能”字样
if '功能' in demandType_str:
func_design_list.append(design_context_obj)
elif '性能' in demandType_str:
performance_design_list.append(design_context_obj)
# 2.摸底指标清单
# 先查第一轮次所有测试项
is_has_modi = False
md_demand_list = []
round1_demand_qs = project_obj.ptField.filter(round__key='0')
for one_demand in round1_demand_qs:
testType_str = get_str_dict(one_demand.testType, 'testType')
if '摸底' in testType_str:
is_has_modi = True
md_demand_list.append({
'xq_source': "隐含需求",
'desc': one_demand.testDesciption,
'demand_name': one_demand.name,
'demand_ident': "".join(["XQ_MD_", one_demand.ident])
})
# 上下文添加
context = {
'project_name': project_obj.name,
'func_design_list': func_design_list,
'performance_design_list': performance_design_list,
'md_demand_list': md_demand_list,
'is_has_modi': is_has_modi
}
doc.render(context, autoescape=True)
2025-04-29 18:09:00 +08:00
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '主要功能和性能指标.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 生成测评对象 - 包括大纲、说明、回归说明和报告
@route.get('/create/softComposition', url_name='create-softComposition')
@transaction.atomic
def create_softComposition(self, id: int):
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 = {
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
doc.render(context, autoescape=True)
2025-04-29 18:09:00 +08:00
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '测评对象.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 生成被测软件接口章节
@route.get('/create/interface', url_name='create-interface')
def create_interface(self, id: int):
project_qs = get_object_or_404(Project, id=id)
project_name = project_qs.name
interfaceNameList = []
# 查询接口列表
iters = project_qs.psField.filter(demandType=3)
iters_length = len(iters)
index = 0
for inter in iters:
interfaceNameList.append(inter.name)
index += 1
if index < iters_length:
interfaceNameList.append('')
# 对每个接口进行字典处理
interface_list = []
for interface in iters:
interface_dict = {
'name': interface.name,
'ident': interface.ident,
'source': interface.source,
'to': interface.to,
'type': interface.type,
'protocal': interface.protocal,
}
interface_list.append(interface_dict)
context = {
'project_name': project_name,
'iters': interfaceNameList,
'iter_list': interface_list,
}
return create_dg_docx('被测软件接口.docx', context, id)
# 生成顶层技术文件
@route.get('/create/top_file', url_name='create-performance')
def create_top_file(self, id: int):
project_obj: Project = get_object_or_404(Project, id=id)
is_JD = True if project_obj.report_type == '9' else False
dut_qs = project_obj.pdField.filter(type='YZ')
dut_list = [{
'index': index + 2 if is_JD else index + 1,
'name': dut_obj.name,
'ident_and_version': '-'.join([dut_obj.ref, dut_obj.version]),
'publish_date': dut_obj.release_date,
'source': dut_obj.release_union,
} for index, dut_obj in enumerate(dut_qs)]
context = {
'project_name': project_obj.name,
'is_JD': is_JD,
'dut_list': dut_list,
}
return create_dg_docx('顶层技术文件.docx', context, id)
# 静态测试环境说明
@route.get('/create/static_env', url_name='create-static_env')
def create_static_env(self, id: int):
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 = {
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
doc.render(context, autoescape=True)
2025-04-29 18:09:00 +08:00
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '静态测试环境说明.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 静态软件项
@route.get('/create/static_soft', url_name='create-static_soft')
def create_static_soft(self, id: int):
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 = {
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
return create_dg_docx("静态软件项.docx", context, id)
# 静态硬件和固件项
@route.get('/create/static_hard', url_name='create-static_hard')
def create_static_hard(self, id: int):
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 = {
"replace": replace, # 指定是否由数据库文档片段进行生成
"user_content": frag and rich_text_list
}
return create_dg_docx("静态硬件和固件项.docx", context, id)
# 动态测评环境说明
@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)
2025-04-29 18:09:00 +08:00
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,
2025-04-29 18:09:00 +08:00
"replace": replace,
"user_content": frag and rich_text_list
}
doc.render(context, autoescape=True)
2025-04-29 18:09:00 +08:00
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / '动态测试环境说明.docx')
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 动态软件项
@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)
2025-04-29 18:09:00 +08:00
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,
2025-04-29 18:09:00 +08:00
"replace": replace,
"user_content": frag and rich_text_list
}
return create_dg_docx("动态软件项.docx", context, id)
# 动态软件项
@route.get('/create/dynamic_hard', url_name='create-dynamic_hard')
def create_dynamic_hard(self, id: int):
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 = {
"replace": replace,
"user_content": frag and rich_text_list
}
return create_dg_docx("动态硬件和固件项.docx", context, id)
# 测试数据
@route.get('/create/test_data', url_name='create-test_data')
def create_test_data(self, id: int):
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 = {
"replace": replace,
"user_content": frag and rich_text_list
}
return create_dg_docx("测评数据.docx", context, id)
# 环境差异性分析
@route.get('/create/env_diff', url_name='create-env_diff')
def create_env_diff(self, id: int):
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 = {
"replace": replace,
"user_content": frag and rich_text_list
}
return create_dg_docx("环境差异性分析.docx", context, id)
# 生成被测软件-基本信息
@route.get('/create/baseInformation', url_name='create-baseInformation')
def create_information(self, id: int):
project_qs = get_object_or_404(Project, id=id)
security = get_str_dict(project_qs.security_level, 'security_level')
languages = get_list_dict('language', project_qs.language)
runtime = get_str_dict(project_qs.runtime, 'runtime')
devplant = get_str_dict(project_qs.devplant, 'devplant')
language_list = []
for language in languages:
language_list.append(language.get('ident_version'))
# 版本先找第一轮
project_round = project_qs.pField.filter(key=0).first()
first_round_SO = project_round.rdField.filter(type='SO').first()
if not first_round_SO:
return ChenResponse(code=400, status=400, message='您还未创建轮次,请进入工作区创建')
version = first_round_SO.version
line_count = int(first_round_SO.total_lines)
dev_unit = project_qs.dev_unit
# 渲染上下文
context = {
'project_name': project_qs.name,
'is_JD': True if project_qs.report_type == '9' else False,
2025-04-29 18:09:00 +08:00
'security_level': security,
'language': "\a".join(language_list),
'version': version,
'line_count': line_count,
'effective_line': int(first_round_SO.effective_lines),
'recv_date': project_qs.beginTime.strftime("%Y-%m-%d"),
'dev_unit': dev_unit,
'soft_type': project_qs.get_soft_type_display(),
'runtime': runtime,
'devplant': devplant
}
return create_dg_docx('被测软件基本信息.docx', context, id)
# 生成测试级别和测试类型
@route.get('/create/levelAndType', url_name='create-levelAndType')
def create_levelAndType(self, id: int):
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, '测试级别和测试类型')
if replace:
context = {
"replace": replace,
"user_content": frag and rich_text_list
}
else:
# 如果没有片段替换,则利用数据生成信息
project_qs = get_object_or_404(Project, id=id)
# 获取所有已录入测试类型
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))
2025-04-29 18:09:00 +08:00
# 定义测试类型一览的顺序注意word里面也要一样
word_types = ['文档审查', '静态分析', '代码审查', '逻辑测试', '功能测试', '性能测试', '边界测试',
'恢复性测试', '安装性测试', '数据处理测试', '余量测试', '强度测试', '接口测试',
'人机交互界面测试', '兼容性测试']
type_index = []
for index, test_type in enumerate(word_types):
for exist_type in type_name_list:
if exist_type == test_type:
type_index.append(str(index))
context = {
"security_level": get_str_dict(project_qs.security_level, 'security_level'),
2025-04-29 18:09:00 +08:00
"testTypes": "".join(type_name_list),
"project_name": project_qs.name,
"type_index": type_index
}
return create_dg_docx("测试级别和测试类型.docx", context, id)
# 生成测试策略
@route.get('/create/strategy', url_name='create-strategy')
def create_strategy(self, id: int):
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, '测试策略')
if replace:
context = {
"replace": replace,
"user_content": frag and rich_text_list
}
else:
# 如果没有片段替换,则利用数据生成信息
project_qs = get_object_or_404(Project, id=id)
# 根据关键等级检查是否有代码审查
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)
2025-04-29 18:09:00 +08:00
# 获取当前测试项的测试类型
test_types = testDemand_qs.values("testType").distinct()
type_name_list = list(
map(lambda qs_item: get_str_dict(qs_item['testType'], 'testType'), test_types))
2025-04-29 18:09:00 +08:00
context = {
"project_name": project_qs.name,
# 查询关键等级-类似“关键”输出
"security_level_str": get_str_abbr(security, 'security_level'),
"isDmsc": isDmsc,
"test_types": type_name_list,
"grouped_data": grouped_data,
2025-04-29 18:09:00 +08:00
}
return create_dg_docx("测试策略.docx", context, id)
# 生成-测试内容充分性及测试方法有效性
@route.get('/create/adequacy_effectiveness', url_name='create-adequacy_effectiveness')
def create_adequacy_effectiveness(self, id: int):
project_qs = get_object_or_404(Project, id=id)
# 统计测试种类数量-只统计第一轮测试
project_round_one = project_qs.pField.filter(key=0).first()
if not project_round_one:
return ChenResponse(status=400, code=400, message="未找到首轮测试信息!")
# 通过字典获取-测试方法
type_dict = {} # key为测试类型value为数量
testDemands = project_round_one.rtField.all()
for testDemand in testDemands:
# 获取每个测试项测试类型
test_type = get_list_dict('testType', [testDemand.testType])[0].get('ident_version')
# 如果字典没有该key则创建并value=1
if not test_type in type_dict:
type_dict[test_type] = 1
else:
type_dict[test_type] += 1
length = len(type_dict)
type_str_list = []
for key, value in type_dict.items():
type_str_list.append(f"{key}{value}")
context = {
'project_name': project_qs.name,
'test_item_count': testDemands.count(),
2025-04-29 18:09:00 +08:00
'length': length,
'type_str': "".join(type_str_list),
}
return create_dg_docx('测试内容充分性及测试方法有效性分析.docx', context, id)
# 生成-测评项目组组成和分工
@route.get('/create/group', url_name='create_group')
def create_group(self, id: int):
project_qs = get_object_or_404(Project, id=id)
context = {
'duty_person': project_qs.duty_person,
'member_str': "".join(project_qs.member),
'quality_person': project_qs.quality_person,
'vise_person': project_qs.vise_person,
'config_person': project_qs.config_person,
'dev_unit': project_qs.dev_unit,
}
return create_dg_docx('测评组织及任务分工.docx', context, id)
# 生成-测评条件保障
@route.get('/create/guarantee', url_name='create-guarantee')
def create_guarantee(self, id: int):
project_qs = get_object_or_404(Project, id=id)
context = {
'project': project_qs
}
return create_dg_docx('测评条件保障.docx', context, id)
# 生成-缩略语
@route.get('/create/abbreviation', url_name='create-abbreviation')
def create_abbreviation(self, id: int):
project_qs = get_object_or_404(Project, id=id)
abbreviations = []
for abbr in project_qs.abbreviation:
abbr_dict = {'title': abbr, 'des': Abbreviation.objects.filter(title=abbr).first().des}
abbreviations.append(abbr_dict)
context = {
'abbreviations': abbreviations
}
return create_dg_docx('缩略语.docx', context, id)
# 生成研制总要求-测试项追踪关系表
@route.get('/create/yzComparison', url_name='create-yzComparison')
def create_yzComparison(self, id: int):
"""目前追踪需求项的章节号是硬编码按6.2章节起步6.2.1~x.x.x依次排序"""
# 规定测试项的章节号开头
test_item_prefix = '6.2'
# 计算有多少种testType - '文档审查'/'功能测试' ->
# 形成一个数组['1','2','3','4','9']后面用来判断测试项的章节号
project_qs = get_object_or_404(Project, id=id)
design_list = [] # 先按照design的思路进行追踪
# 查询第一轮次
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())
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)
2025-04-29 18:09:00 +08:00
context = {
'design_list': design_list
}
return create_dg_docx('研制总要求追踪表.docx', context, id)
# 生成需求规格说明-测试项追踪关系表
@route.get('/create/xqComparison', url_name='create-xqComparison')
def create_xqComparison(self, id: int):
project_qs = get_object_or_404(Project, id=id)
test_item_prefix = '6.2'
design_list = []
project_round_one = project_qs.pField.filter(key=0).first()
if project_round_one:
testType_list, last_chapter_items = create_csx_chapter_dict(project_round_one)
# 找出第一轮的被测件为'XQ'
xq_dut = project_round_one.rdField.filter(type='XQ').first()
# 找出第一轮被测件为'SO',其中的测试项
so_dut = project_round_one.rdField.filter(type='SO').first()
if so_dut:
so_designs = so_dut.rsField.all()
for design in so_designs:
design_dict = {'name': "/", 'chapter': "/", 'test_demand': []}
# 获取一个design的所有测试项
test_items = []
test_items.extend(design.dtField.all())
test_items.extend(design.odField.all())
for test_item in test_items:
# 只对文档审查、静态分析、代码走查、代码审查进行处理
if test_item.testType in ['8', '15', '3', '2']:
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}
2025-04-29 18:09:00 +08:00
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
if xq_dut:
xq_designs = xq_dut.rsField.all()
for design in xq_designs:
design_dict = {'name': design.name, 'chapter': design.chapter, 'test_demand': []}
# 获取一个design的所有测试项
test_items = []
test_items.extend(design.dtField.all())
test_items.extend(design.odField.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}
2025-04-29 18:09:00 +08:00
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)
2025-04-29 18:09:00 +08:00
context = {
'design_list': design_list
}
return create_dg_docx('需求规格说明追踪表.docx', context, id)
raise HttpError(400, "生成需求追踪表出错")
# 生成测试项-需求规格说明关系表【反向】
@route.get('/create/fanXqComparison', url_name='create-fanXqComparison')
def create_fanXqComparison(self, id: int):
project_qs = get_object_or_404(Project, id=id)
test_item_prefix = '6.2'
# 取出第一轮所有测试项的章节处理列表和字典
project_round_one = project_qs.pField.filter(key=0).first()
testType_list, last_chapter_items = create_csx_chapter_dict(project_round_one)
# 查询第一轮所有测试项
test_items = []
test_items.extend(project_round_one.rtField.all())
# 最后渲染列表
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']):
2025-04-29 18:09:00 +08:00
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)])
# 如果是SO里面的
if test_item.testType in ['8', '15', '3', '2'] and test_item.dut.type == 'SO':
test_item_dict = {'name': test_item.name, 'chapter': test_chapter, 'ident': reveal_ident,
'design': {
'name': "/", 'chapter': "/"
}}
else:
test_item_dict = {'name': test_item.name, 'chapter': test_chapter, 'ident': reveal_ident,
'design': {
'name': test_item.design.name, 'chapter': test_item.design.chapter
}}
items_list.append(test_item_dict)
context = {
'items_list': items_list,
}
return create_dg_docx('反向需求规格追踪表.docx', context, id)
# 生成代码质量度量分析表
@route.get('/create/codeQuality', url_name='create-codeQuality')
def create_codeQuality(self, id: int):
project_qs = get_object_or_404(Project, id=id)
project_round_one = project_qs.pField.filter(key=0).first()
context = {}
context.update({'project_name': project_qs.name})
if project_round_one:
source_dut: Dut = project_round_one.rdField.filter(type='SO').first() # type:ignore
if source_dut:
context.update({'version': source_dut.version}) # type:ignore
context.update({'size': int(source_dut.total_lines)})
context.update({'total_code_line': int(source_dut.effective_lines)})
context.update({'comment_line': int(source_dut.comment_lines)})
comment_ratio = int(source_dut.comment_lines) / int(source_dut.total_lines)
context.update({
'comment_ratio': f"{comment_ratio * 100:.2f}%",
'comment_ratio_right': '满足' if comment_ratio >= 0.2 else '不满足'
})
# 如果已经有metrics
if hasattr(source_dut, 'metrics'):
context.update({
'black_line': source_dut.metrics.total_blanks,
'function_count': source_dut.metrics.function_count,
'avg_function_lines': source_dut.metrics.avg_function_lines,
'avg_function_lines_right': '满足' if source_dut.metrics.avg_function_lines <= 200 else '不满足',
'avg_fan_out': source_dut.metrics.avg_fan_out,
'avg_fan_out_right': '满足' if source_dut.metrics.avg_fan_out <= 7 else '不满足',
'avg_cyclomatic': source_dut.metrics.avg_cyclomatic,
'avg_cyclomatic_right': '满足' if source_dut.metrics.avg_cyclomatic <= 10 else '不满足',
'max_cyclomatic': source_dut.metrics.max_cyclomatic,
'max_cyclomatic_right': '满足' if source_dut.metrics.max_cyclomatic <= 80 else '不满足',
'high_cyclomatic_ratio': source_dut.metrics.high_cyclomatic_ratio,
'high_cyclomatic_ratio_right': '满足' if source_dut.metrics.high_cyclomatic_ratio <= 0.2 else '不满足',
})
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