initial commit

This commit is contained in:
2025-04-29 18:09:00 +08:00
commit 4faed52de5
690 changed files with 13481 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
from apps.project.models import Project
from utils.util import *
from utils.chen_response import ChenResponse
from django.db.models import Q
def create_round_context(project_obj: Project, round_id: str):
"""根据轮次,生成测评报告中的测评结果"""
# 0. 首先定义个轮次对应中文
round_chinese = ['', '', '', '', '', '', '', '', '', '']
round_id = int(round_id)
round_str_id = str(round_id)
# 1. 首先先查询给定轮次的round对向
round_obj = project_obj.pField.filter(key=round_str_id).first()
# 如果没有轮次对象则返回错误
if not round_obj:
return ChenResponse(code=400, status=400, message='注意您没有设置第二轮测试,请添加!')
dut_qs_r2 = round_obj.rdField # 当前轮被测件dut对象qs
dut_qs_r1 = project_obj.pdField.filter(round__key=str(round_id - 1)) # 上一个轮次被测件dut对象qs
# 查询当前轮次duts是否有源代码如果没有返回错误
so_dut = dut_qs_r2.filter(type='SO').first() # 当前轮次源代码被测件对象
if not so_dut:
return ChenResponse(code=400, status=400, message='注意您某轮次没有编写源代码被测件信息,务必添加...')
# 查询上一个轮次的dut中的源代码、需求文档
r1_so_dut = dut_qs_r1.filter(type='SO').first()
# 3. 文档审查清单
doc_list = []
round_duts = round_obj.rdField.filter(Q(type='SJ') | Q(type='XQ') | Q(type='XY'))
for dut in round_duts:
dut_dict = {
'name': dut.name,
'ident': dut.ref,
'version': dut.version
}
doc_list.append(dut_dict)
# 4. 发现多少个问题,什么类型多少个问题
problems = project_obj.projField.all().distinct() # !important:大变量-项目所有问题
problems_r2 = problems.filter(case__round__key=round_str_id) # 当前轮次所有问题
# 7. 第二轮动态测试用例个数(动态测试-非静态分析、文档审查、代码审查、代码走查4个)
case_r2_qs = round_obj.rcField.filter(~Q(test__testType='2'), ~Q(test__testType='3'), ~Q(test__testType='8'),
~Q(test__testType='15')) # !warning:中变量-第一轮动态测试用例qs
testType_list, testType_count = create_str_testType_list(case_r2_qs)
## 动态测试(第一轮)各个类型测试用例执行表/各个测试需求表
demand_r2_dynamic_qs = round_obj.rtField.filter(~Q(testType='2'), ~Q(testType='3'), ~Q(testType='8'),
~Q(testType='15')) # !warning:中变量:第一轮动态测试的测试项
summary_r2_demand_info, summry_r2_demandType_info = create_demand_summary(demand_r2_dynamic_qs,
project_obj.ident)
# 8.第二轮所有动态问题统计
problems_dynamic_r2 = problems_r2.filter(~Q(case__test__testType='2'), ~Q(case__test__testType='3'),
~Q(case__test__testType='8'),
~Q(case__test__testType='15')) # !critical:大变量:第一轮动态问题单qs
problem_dynamic_r2_type_str = create_problem_type_str(problems_dynamic_r2)
problem_dynamic_r2_grade_str = create_problem_grade_str(problems_dynamic_r2)
r2_dynamic_str = "本轮测试未发现新问题。"
has_r2_dynamic = False
if len(problems_dynamic_r2) > 0:
has_r2_dynamic = True
r2_dynamic_str = (f"{round_chinese[int(round_id)]}轮动态测试共发现问题{problems_dynamic_r2.count()}个,"
f"其中{problem_dynamic_r2_type_str}"
f"{problem_dynamic_r2_grade_str}")
context = {
'project_name': project_obj.name,
'r1_so_version': r1_so_dut.version,
'so_version': so_dut.version,
'case_dynamic_r2_count': case_r2_qs.count(),
'dynamic_testType_list': ''.join(testType_list),
'dynamic_testType_count': testType_count,
'r2_exe_info_all': summary_r2_demand_info,
'r2_exe_info_type': summry_r2_demandType_info,
'has_r2_dynamic': has_r2_dynamic,
'r2_dynamic_str': r2_dynamic_str,
'round_id': round_chinese[round_id],
}
return context

View File

@@ -0,0 +1,207 @@
# 本模块主要以项目开始时间、结束时间、轮次开始时间、结束时间计算文档中的各个时间
from datetime import timedelta, date
from apps.project.models import Project
from django.shortcuts import get_object_or_404
from ninja.errors import HttpError # 从代码抛出该异常被ninja截取变为response
def format_remove_heng(dateT: date) -> str:
"""该函数将date对象的横杠-去掉输出str"""
return str(dateT).replace('-', '')
def times_by_cover_time(cover_time: date) -> dict:
"""该函数为每个产品文档根据封面时间,渲染签署页时间、文档变更记录时间"""
return {
'preparation_time_no_format': cover_time - timedelta(days=2),
'preparation_time': format_remove_heng(cover_time - timedelta(days=2)), # 拟制时间:为编制结束时间-2天
'inspect_time': format_remove_heng(cover_time - timedelta(days=1)), # 校对时间:为编制时间+1天
'auditing_time': format_remove_heng(cover_time),
'ratify_time': format_remove_heng(cover_time),
'create_doc_time': format_remove_heng(cover_time - timedelta(days=2)),
'doc_v1_time': format_remove_heng(cover_time)
}
class DocTime:
def __init__(self, project_id: int):
self.project = get_object_or_404(Project, id=project_id)
# 用户录入时间-项目
self.p_start = self.project.beginTime # 被测件接收时间/
self.p_end = self.project.endTime # 大纲测评时间周期结束时间/
# 遍历轮次时间-多个
self.round_count = self.project.pField.count()
self.round_time = [] # 轮次按顺序排序
for round in self.project.pField.all():
self.round_time.append({
'start': round.beginTime,
'end': round.endTime,
'location': round.location
})
# ~~~~由上面时间二次计算得出时间~~~~ -> TODO:可由用户设置间隔时间!!!!
self.dg_bz_start = self.p_start + timedelta(days=1) # 大纲编制开始时间,项目开始时间+1天
self.dg_bz_end = self.dg_bz_start + timedelta(days=6) # 大纲编制结束时间,大纲编制开始+6天
self.test_sj_start = self.dg_bz_end + timedelta(days=1) # 测评设计与实现时间,在大纲编制结束+1天
self.test_sj_end = self.test_sj_start + timedelta(days=5) # 测评设计与实现结束,在开始+5天
# ~~~~储存每个文档的cover_time~~~~
self.dg_cover_time = self.dg_bz_end
self.sm_cover_time = self.test_sj_end
self.jl_cover_time = self.round_time[0]['end']
self.wtd_cover_time = self.round_time[-1]['end']
# 该函数生成大纲文档片段-测评时间和地点的时间和地点信息
def dg_address_time(self):
"""直接返回context去渲染"""
# 需要判断round_time是否有值
if len(self.round_time) <= 0:
raise HttpError(status_code=400, message='您还未创建轮次时间,请填写后生成')
return {
'start_year': self.p_start.year,
'start_month': self.p_start.month,
'end_year': self.p_end.year,
'end_month': self.p_end.month,
'beginTime_strf': format_remove_heng(self.p_start),
'dgCompileStart': format_remove_heng(self.dg_bz_start),
'dgCompileEnd': format_remove_heng(self.dg_bz_end),
'designStart': format_remove_heng(self.test_sj_start),
'designEnd': format_remove_heng(self.test_sj_end),
'location': self.round_time[0]['location']
}
# 该函数生成报告文档片段-测评时间和地点【注意使用了dg_address_time -> 所以后续有修改注意前导】
def bg_address_time(self):
if len(self.round_time) <= 0:
raise HttpError(status_code=400, message='您还未创建轮次时间,请填写后生成')
# 先使用大纲的时间行数作为前三行
cname = ['首轮测试', '第二轮测试', '第三轮测试', '第四轮测试', '第五轮测试', '第六轮测试', '第七轮测试',
'第八轮测试', '第九轮测试', '第十轮测试']
dg_address_time = self.dg_address_time()
round_time_list = []
index = 0
for round_dict in self.round_time:
one_dict = {
'name': cname[index],
'start': format_remove_heng(round_dict['start']),
'end': format_remove_heng(round_dict['end']),
'location': round_dict['location']
}
index += 1
round_time_list.append(one_dict)
return {
'begin_year': dg_address_time['start_year'],
'begin_month': dg_address_time['start_month'],
'end_year': dg_address_time['end_year'],
'end_month': dg_address_time['end_month'],
'begin_time': dg_address_time['beginTime_strf'],
'dg_weave_start_date': dg_address_time['dgCompileStart'],
'dg_weave_end_date': dg_address_time['dgCompileEnd'],
'sj_weave_start_date': dg_address_time['designStart'],
'sj_weave_end_date': dg_address_time['designEnd'],
'round_time_list': round_time_list,
# 测评总结 -> 依据项目结束时间-7 ~ 项目结束时间
'summary_start_date': format_remove_heng(self.p_end - timedelta(days=7)),
'summary_end_date': format_remove_heng(self.p_end),
}
# 生成报告中测评完成情况 -> 必须依据其他内容生成时间【注意使用了bg_address_time -> 所以后续有修改注意前导】
def bg_completion_situation(self):
bg_timer_dict = self.bg_address_time()
xq_fx_time_end = self.dg_bz_start + timedelta(days=2)
ch_time_start = xq_fx_time_end + timedelta(days=1)
ch_time_end = self.dg_bz_end
if len(self.round_time) < 1:
raise HttpError(status_code=400, message='您还未创建第一轮测试的时间,请填写后再生成')
return {
'start_time_year': bg_timer_dict['begin_year'],
'start_time_month': bg_timer_dict['begin_month'],
'xq_fx_time_start_year': self.dg_bz_start.year,
'xq_fx_time_start_month': self.dg_bz_start.month,
'xq_fx_time_start_day': self.dg_bz_start.day,
'xq_fx_time_end_year': xq_fx_time_end.year, # 需求分析结束时间是大纲编制开始+2
'xq_fx_time_end_month': xq_fx_time_end.month,
'xq_fx_time_end_day': xq_fx_time_end.day,
'ch_start_year': ch_time_start.year,
'ch_start_month': ch_time_start.month,
'ch_start_day': ch_time_start.day,
'ch_end_year': ch_time_end.year,
'ch_end_month': ch_time_end.month,
'ch_end_day': ch_time_end.day,
'sj_start_year': self.test_sj_start.year,
'sj_start_month': self.test_sj_start.month,
'sj_start_day': self.test_sj_start.day,
'sj_end_year': self.test_sj_end.year,
'sj_end_month': self.test_sj_end.month,
'sj_end_day': self.test_sj_end.day,
'end_time_year': self.p_end.year,
'end_time_month': self.p_end.month,
'exec_start_time_year': self.round_time[0]['start'].year,
'exec_start_time_month': self.round_time[0]['start'].month,
'exec_start_time_day': self.round_time[0]['start'].day,
'exec_end_time_year': self.round_time[0]['end'].year,
'exec_end_time_month': self.round_time[0]['end'].month,
'exec_end_time_day': self.round_time[0]['end'].day,
}
# 该函数生成最终大纲的时间
def dg_final_time(self):
cover_time = self.dg_bz_end
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
# 新增给大纲模版10.2章节context
context.update(basic_line1=cover_time.strftime("%Y年%m月"), basic_line2=self.p_end.strftime("%Y年%m月"))
# 新增给大纲模版10.3.2章节的context
sm_context = self.sm_final_time()
context.update(sm_end_time=sm_context['preparation_time_no_format'].strftime("%Y年%m月"))
return context
# 该函数生成说明文档的时间 -> 依据项目时间而非用户第一轮填写时间!
def sm_final_time(self):
cover_time = self.test_sj_end # 封面时间:为大纲时间中“测评设计与实现”结束时间
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context
# 该函数生成记录文档的时间 -> 依据第一轮测试用户填写的事件
def jl_final_time(self):
if len(self.round_time) < 1:
raise HttpError(status_code=400, message='您还未创建第一轮测试的时间,请填写后再生成')
cover_time = self.round_time[0]['end'] # 封面时间为用户填写第一轮结束时间
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context
# 问题单的时间 -> 依据最后一轮次的结束时间+1天
def wtd_final_time(self):
if len(self.round_time) < 1:
raise HttpError(status_code=400, message='您还未创建第一轮测试的时间,请填写后再生成')
cover_time = self.round_time[-1]['end']
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context
# 回归测试说明时间 -> 根据第二轮、第三轮...的开始时间
def hsm_final_time(self, round_key: str):
if len(self.round_time) < int(round_key) + 1:
raise HttpError(status_code=400, message='您填写的回归轮次时间不正确,请填写后再生成')
cover_time = self.round_time[int(round_key)]['start']
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context
# 回归测试记录时间 -> 根据第二轮、第三轮...的结束时间
def hjl_final_time(self, round_key: str) -> dict:
if len(self.round_time) < int(round_key) + 1:
raise HttpError(status_code=400, message='您填写的回归轮次时间不正确,请填写后再生成')
cover_time = self.round_time[int(round_key)]['end']
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context
# 生成报告非过程时间 -> 根据项目结束时间来定
def bg_final_time(self) -> dict:
if len(self.round_time) <= 0:
raise HttpError(status_code=400, message='您还未创建轮次时间,请填写后生成')
cover_time = self.p_end
# 这里做判断,如果项目结束时间/最后一轮结束时间
if cover_time < self.round_time[-1]['end']:
raise HttpError(500, message='项目结束时间早于最后一轮次结束时间或等于开始时间,请修改项目结束时间')
context = times_by_cover_time(cover_time)
context.update(cover_time=cover_time.strftime("%Y年%m月%d"))
return context

View File

@@ -0,0 +1,21 @@
from abc import ABC
from apps.project.models import Project
from django.shortcuts import get_object_or_404
from apps.dict.models import Fragment
from apps.createDocument.extensions.parse_rich_text import RichParser
class FragementToolsMixin(ABC):
"""该混合主要给文档片段进行功能封装"""
def _generate_frag(self, id: int, doc, doc_name: str):
"""传入项目id/"""
project_qs = get_object_or_404(Project, id=id)
replace = False # 是否替换标志
rich_text_list = []
# [deprecated]判断是否有当前项目的文档片段
fragments = project_qs.frag.all()
# 传入'片段名称'和判断is_main
frag: Fragment = fragments.filter(name=doc_name, is_main=True).first()
if frag:
replace = True
rich_text_list = RichParser(frag.content).get_final_format_list(doc)
return replace, frag, rich_text_list

View File

@@ -0,0 +1,127 @@
"""
专门解析富文本插件tinymce的html内容
"""
import pandas as pd
from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString
import base64
import io
from docxtpl import InlineImage
from docx.shared import Mm, Cm
import re
# text.replace('\xa0', ' '))
class RichParser:
def __init__(self, rich_text):
# 将rich_text的None变为空字符串鲁棒
if rich_text is None:
rich_text = ""
# 对原始html解析后的bs对象
self.bs = BeautifulSoup(rich_text, 'html.parser')
self.content = self.remove_n_in_contents()
# 最终的解析后的列表
self.data_list = []
self.line_parse()
# 1.函数将self.bs.contents去掉\n获取每行数据
def remove_n_in_contents(self):
content_list = []
for line in self.bs.contents:
if line != '\n':
content_list.append(line)
return content_list
# 2.逐个遍历self.content去掉table元素Tag对象单独解析
def line_parse(self):
for tag in self.content:
if isinstance(tag, NavigableString):
self.data_list.append(tag.text)
elif isinstance(tag, Tag):
if tag.name == 'p':
img_list = tag.find_all('img')
if len(img_list) > 0:
for img_item in img_list:
self.data_list.append(img_item.get('src'))
else:
self.data_list.append(tag.text)
elif tag.name == 'table':
df_dict_list = self.parse_tag2list(tag)
self.data_list.append(df_dict_list)
elif tag.name == 'div':
table_list = tag.find_all('table')
if len(table_list) > 0:
for table in table_list:
df_dict_list = self.parse_tag2list(table)
self.data_list.append(df_dict_list)
# 3.1.辅助方法,将<table>的Tag对象转为[[]]二维列表格式
def parse_tag2list(self, table_tag):
# str(tag)可直接变成<table>xxx</table>
pd_list = pd.read_html(io.StringIO(str(table_tag)))
# 将dataframe变为数组
df = pd_list[0]
# 处理第一行为数字的情况,如果为数字则删除第一行,让第二行为列名
if all(isinstance(col, int) for col in df.columns):
df.columns = df.iloc[0]
df = df.drop(0) # 删除原来的第一行
# 转为列表的列表(二维列表)
# return df.values.tolist()
return df.fillna('').T.reset_index().T.values.tolist()
# 3.2.辅助方法,打印解析后列表
def print_content(self):
for line in self.data_list:
print(line)
# 4.1.最终方法生成给docxtpl可用的列表 -> 注意需要传递DocxTemplate对象在接口函数里面初始化的
def get_final_list(self, doc, /, *, img_size=100, height=80):
"""注意关键字传参可修改图片大小img_size:int=100"""
final_list = []
for oneline in self.data_list:
# 这里要单独处理下二维列表
if isinstance(oneline, list):
final_list.append({'isTable': True, 'data': oneline})
continue
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)))
else:
final_list.append(oneline)
if len(final_list) <= 0:
final_list.append("")
return final_list
# 4.2.最终方法,在上面方法基础上,增加格式,例如<p>增加缩进,图片居中,<p>包含“图x”则居中
def get_final_format_list(self, doc, /, *, img_size=115, height=80):
final_list = []
for oneline in self.data_list:
# 这里要单独处理下二维列表
if isinstance(oneline, list):
final_list.append({'isTable': True, 'data': oneline})
continue
if oneline.startswith("data:image/png;base64"):
base64_bytes = base64.b64decode(oneline.replace("data:image/png;base64,", ""))
# 1.和上面函数变化图片更改为dict然后isCenter属性居中
final_list.append(
{'isCenter': True,
'data': InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(img_size), height=height)})
else:
# 2.和上面区别:如果<p>带有“图”则居中
if re.match(r"[表图]\d.*", oneline):
final_list.append({"isCenter": True, "data": oneline})
else:
final_list.append({"isCenter": False, "data": oneline})
if len(final_list) <= 0:
final_list.append("")
return final_list
# 5.最终方法去掉图片和table元素 -> 纯文本列表
def get_final_p_list(self):
final_list = []
for oneline in self.data_list:
if isinstance(oneline, list) or oneline.startswith("data:image/png;base64"):
continue
else:
final_list.append(oneline)
return final_list

View File

@@ -0,0 +1,35 @@
from apps.project.models import Problem
from utils.util import get_str_dict
from apps.createDocument.extensions.parse_rich_text import RichParser
def parse_html(html_txt, a_list, doc):
"""解析HTML字段的文字和图片到列表,输入是HTML字段的txt以及列表输出列表"""
parser = RichParser(html_txt)
a_list.extend(parser.get_final_list(doc, img_size=80))
return a_list
def create_one_problem_dit(problem: Problem, problem_prefix: str, doc) -> dict:
"""问题单汇总表每个问题作为一行的数据"""
problem_dict = {
'ident': '_'.join([problem_prefix, problem.ident]),
'grade': get_str_dict(problem.grade, 'problemGrade'),
'type': get_str_dict(problem.type, 'problemType'),
'status': get_str_dict(problem.status, 'problemStatu')
}
# 问题操作 - HTML解析
desc_list = ['【问题描述】']
desc_list = parse_html(problem.operation, desc_list, doc)
desc_list_yq = [f'\a【问题影响】\a{problem.result}']
desc_list.extend(desc_list_yq)
problem_dict['desciption'] = desc_list
# 问题处理方式表格单元格
solve_list = [f'【原因分析】\a{problem.analysis}']
solve_list_effect = [f'\a【影响域】\a{problem.effect_scope}']
solve_list_basic = ['\a【处理方式】', problem.solve]
solve_list_verify = ['\a【回归验证】']
solve_list_verify = parse_html(problem.verify_result, solve_list_verify, doc)
solve_list.extend(solve_list_effect)
solve_list.extend(solve_list_basic)
solve_list.extend(solve_list_verify)
problem_dict['solve'] = solve_list
return problem_dict

View File

@@ -0,0 +1,97 @@
from pathlib import Path
from docxtpl import DocxTemplate
from docx.table import Table
from utils.chen_response import ChenResponse
from typing import Any
from apps.project.models import Project
from utils.path_utils import project_path
def merge_all_cell(table: Table) -> None:
"""生成需求研总对照表工具逐个找第二列和第三列单元格的text如果一致则合并"""
col_list = [table.columns[1], table.columns[2]]
# 合并第二列相同的单元格
for col_right in col_list:
index = 0
temp_text = ""
for cell in col_right.cells:
if index == 0:
temp_text = cell.text
else:
if cell.text == temp_text:
if cell.text == '': # 不知道什么原因必须这样判断下
cell.text = '/'
text_temp = cell.text
ce = cell.merge(col_right.cells[index - 1])
ce.text = text_temp
else:
temp_text = cell.text
index += 1
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)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/sm" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
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)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
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)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/bg" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
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)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir/wtd" / template_name)
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
def get_round1_problem(project: Project) -> Any:
"""
从项目返回第一轮问题单
:param project: Project项目Model对象
:return: 问题单的列表
"""
all_problem_qs = project.projField.all()
# 遍历每个问题,找出第一轮的问题
problem_set = set()
for problem in all_problem_qs:
flag = False
for case in problem.case.all():
if case.round.key == '0':
flag = True
if flag:
problem_set.add(problem)
return list(problem_set)
def delete_dir_files(path: Path) -> Any:
"""传入一个Path对象如果是文件夹则删除里面所有的文件不删除文件夹"""
if path.is_dir():
for file in path.iterdir():
if file.is_file():
file.unlink()

View File

@@ -0,0 +1,99 @@
from apps.project.models import Project
from utils.chapter_tools.csx_chapter import create_csx_chapter_dict
from utils.util import get_testType, get_case_ident
# 传入项目对象、dut的类型例如'XQ'、round_str字符串表示例如第一轮为'XQ',测试项其实章节前缀例如
def create_bg_round1_zhui(project_obj: Project, dut_str='XQ', round_str='0'):
"""传入项目对象,返回{仅第一轮}的design_list渲染到模版的列表"""
# 首先定义后面用问题单前缀
problem_prefix = "".join(['PT_', project_obj.ident, '_'])
# 如果是第一轮测试项章节号前缀则为6.2其他轮次为4.1
demand_prefix = '6.2' if round_str == '0' else "3.1"
design_list = []
round_obj = project_obj.pField.filter(key=round_str).first() # 轮次对象
case_index = 1
if round_obj:
testType_list, last_chapter_items = create_csx_chapter_dict(round_obj)
specific_dut = round_obj.rdField.filter(type=dut_str).first() # design的列表
if dut_str == 'XQ':
so_dut = round_obj.rdField.filter(type='SO').first()
if so_dut:
designs = so_dut.rsField.all()
for design in 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:
key_index = int(test_item.key.split("-")[-1]) + 1
test_index = str(key_index).rjust(3, '0')
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"),
test_item.ident, test_index])
test_item_last_chapter = last_chapter_items[test_item.testType].index(test_item.key) + 1
test_chapter = ".".join([demand_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,
'case_list': []}
for case in test_item.tcField.all():
# 用例如果关联了问题单,那么直接判断未通过,如果没有关联问题单,则找步骤里面是否有未执行
# 如果未执行,不显示未执行,显示“/”斜杠
is_passed = '通过'
problem_ident_list = []
for problem in case.caseField.all():
problem_ident_list.append("".join([problem_prefix, problem.ident]))
if len(problem_ident_list) > 0:
is_passed = '未通过'
case_dict = {
'index': case_index,
'name': case.name,
'ident': get_case_ident(reveal_ident, case),
'passed': is_passed,
'problem_ident_list': "\a".join(problem_ident_list)
}
test_item_dict['case_list'].append(case_dict)
case_index += 1
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
if specific_dut:
designs = specific_dut.rsField.all()
for design in 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:
key_index = int(test_item.key.split("-")[-1]) + 1
test_index = str(key_index).rjust(3, '0')
reveal_ident = "_".join(
["XQ", get_testType(test_item.testType, "testType"),
test_item.ident, test_index])
test_item_last_chapter = last_chapter_items[test_item.testType].index(test_item.key) + 1
test_chapter = ".".join([demand_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,
'case_list': []}
for case in test_item.tcField.all():
# 用例如果关联了问题单,那么直接判断未通过,如果没有关联问题单,则找步骤里面是否有未执行
# 如果未执行,不显示未执行,显示“/”斜杠
is_passed = '通过'
problem_ident_list = []
for problem in case.caseField.all():
problem_ident_list.append("".join([problem_prefix, problem.ident]))
if len(problem_ident_list) > 0:
is_passed = '未通过'
case_dict = {
"index": case_index,
'name': case.name,
'ident': get_case_ident(reveal_ident, case),
'passed': is_passed,
'problem_ident_list': "\a".join(problem_ident_list)
}
test_item_dict['case_list'].append(case_dict)
case_index += 1
design_dict['test_demand'].append(test_item_dict)
design_list.append(design_dict)
return design_list