from datetime import date, timedelta from pathlib import Path from ninja_extra import api_controller, ControllerBase, route from django.db import transaction from django.shortcuts import get_object_or_404 from django.db.models import Q, QuerySet from docxtpl import DocxTemplate from typing import Optional from docx import Document from ninja_extra.permissions import IsAuthenticated from ninja_jwt.authentication import JWTAuth # 导入模型 from apps.project.models import Project, Dut, TestDemand, Problem # 工具类函数 from apps.createDocument.extensions import util from utils.chen_response import ChenResponse from apps.createDocument.extensions.util import create_bg_docx, get_round1_problem from utils.util import get_str_dict, get_list_dict, create_problem_grade_str, create_str_testType_list, \ create_demand_summary, create_problem_type_str, create_problem_table, create_problem_type_table, \ get_str_abbr # 根据轮次生成测评内容文档context from apps.createDocument.extensions.content_result_tool import create_round_context from apps.createDocument.extensions.zhui import create_bg_round1_zhui from apps.createDocument.extensions.solve_problem import create_one_problem_dit from utils.path_utils import project_path from apps.createDocument.extensions.util import delete_dir_files from apps.createDocument.extensions.parse_rich_text import RichParser from apps.createDocument.extensions.documentTime import DocTime # 导入生成日志记录模块 from apps.createSeiTaiDocument.extensions.logger import GenerateLogger # @api_controller("/generateBG", tags=['生成报告文档系列'], auth=JWTAuth(), permissions=[IsAuthenticated]) @api_controller("/generateBG", tags=['生成报告文档系列']) class GenerateControllerBG(ControllerBase): logger = GenerateLogger('测评报告') # important:删除之前的文件 @route.get('/create/deleteBGDocument', url_name='delete-bg-document') def delete_bg_document(self, id: int): project_path_str = project_path(id) save_path = Path.cwd() / 'media' / project_path_str / 'output_dir/bg' delete_dir_files(save_path) @route.get("/create/techyiju", url_name="create-techyiju") @transaction.atomic def create_techyiju(self, id: int): project_obj = get_object_or_404(Project, id=id) duties_qs = project_obj.pdField.filter(Q(type='XQ') | Q(type='SJ') | Q(type='XY')) 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) # 添加大纲到这里 ## 判断是否为鉴定 doc_name = f'{project_obj.name}软件测评大纲' if project_obj.report_type == '9': doc_name = f'{project_obj.name}软件鉴定测评大纲' # 时间控制类 timer = DocTime(id) # 这里大纲版本升级如何处理 dg_duty = {'doc_name': doc_name, 'ident_version': f'PT-{project_obj.ident}-TO-1.00', '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', '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', 'publish_date': timer.jl_cover_time, 'source': project_obj.test_unit} # 循环所有轮次,除了第一轮 std_documents.extend([sm_duty, jl_duty]) rounds = project_obj.pField.exclude(key='0') name_list = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'] index = 1 for r in rounds: hsm_duty = {'doc_name': f'{project_obj.name}软件第{name_list[index]}轮测试说明', 'ident_version': f'PT-{project_obj.ident}-TD{str(index + 1)}-1.00', 'publish_date': r.beginTime, 'source': project_obj.test_unit} hjl_duty = {'doc_name': f'{project_obj.name}软件第{name_list[index]}轮测试记录', 'ident_version': f'PT-{project_obj.ident}-TN{str(index + 1)}', 'publish_date': r.endTime, 'source': project_obj.test_unit} std_documents.extend([hsm_duty, hjl_duty]) index += 1 # 生成二级文档 context = { 'std_documents': std_documents } return create_bg_docx("技术依据文件.docx", context, id) # 测评地点和时间接口 @route.get('/create/timeaddress') @transaction.atomic def create_timeaddress(self, id: int): timer = DocTime(id) context = timer.bg_address_time() return create_bg_docx('测评时间和地点.docx', context, id) # 在报告生成多个版本被测软件基本信息 @route.get('/create/baseInformation', url_name='create-baseInformation') def create_information(self, id: int): project_obj = get_object_or_404(Project, id=id) languages = get_list_dict('language', project_obj.language) language_list = [] for language in languages: language_list.append(language.get('ident_version')) # 获取轮次 rounds = project_obj.pField.all() round_list = [] for r in rounds: round_dict = {} # 获取SO的dut so_dut: Dut = r.rdField.filter(type='SO').first() if so_dut: round_dict['version'] = so_dut.version round_dict['line_count'] = int(so_dut.total_lines) round_dict['effective_line'] = int(so_dut.effective_lines) round_list.append(round_dict) context = { 'project_name': project_obj.name, 'soft_type': project_obj.get_soft_type_display(), 'security_level': get_str_dict(project_obj.security_level, 'security_level'), 'runtime': get_str_dict(project_obj.runtime, 'runtime'), 'devplant': get_str_dict(project_obj.devplant, 'devplant'), 'language': "\a".join(language_list), 'recv_date': project_obj.beginTime.strftime("%Y-%m-%d"), 'dev_unit': project_obj.dev_unit, 'version_info': round_list } return create_bg_docx('被测软件基本信息.docx', context, id) # 生成测评完成情况 @route.get('/create/completionstatus', url_name='create-completionstatus') def create_completionstatus(self, id: int): project_obj = get_object_or_404(Project, id=id) # 找到第一轮轮次对象、第二轮轮次对象 round1 = project_obj.pField.filter(key='0').first() # 第一轮测试项个数 round1_demand_qs = round1.rtField.all() # 第一轮用例个数 round1_case_qs = round1.rcField.all() # 这部分找出第一轮的所有测试类型,输出字符串,并排序 test_type_set: set = set() for case in round1_case_qs: demand: TestDemand = case.test test_type_set.add(demand.testType) round1_testType_list = list( map(lambda x: x['ident_version'], get_list_dict('testType', list(test_type_set)))) # 这里找出第一轮,源代码被测件,并获取版本 so_dut = round1.rdField.filter(type='SO').first() so_dut_verson = "$请添加第一轮的源代码信息$" if so_dut: so_dut_verson = so_dut.version # 这里找出除第一轮的其他轮次 rounds = project_obj.pField.exclude(key='0') rounds_str_chinese = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'] round_list = [] for r in rounds: # 找所属dut的so-dut so_dut = r.rdField.filter(type='SO').first() # 找出上一轮dut的so-dut last_problem_count = Problem.objects.filter( case__round__key=str(int(r.key) - 1)).distinct().count() current_round_problem_count = Problem.objects.filter(case__round__key=r.key).distinct().count() if current_round_problem_count > 0: current_round_description = f'引入新问题{current_round_problem_count}个' else: current_round_description = '经测试软件更改正确,并且未引入新的问题' r_dict = { 'version': so_dut.version if so_dut else '$请添加该轮次源代码信息$', 'round_index': rounds_str_chinese[int(r.key)], 'last_problem_count': last_problem_count, 'current_round_description': current_round_description, 'start_year': r.beginTime.year, 'start_month': r.beginTime.month, 'end_year': (r.beginTime + timedelta(days=4)).year, # 这里只是简单+4有待商榷 'end_month': (r.beginTime + timedelta(days=4)).month, } round_list.append(r_dict) # 这部分找到第一轮的问题 problem_qs = get_round1_problem(project_obj) context = { 'is_JD': True if project_obj.report_type == '9' else False, 'project_name': project_obj.name, 'start_time_year': project_obj.beginTime.year, 'start_time_month': project_obj.beginTime.month, 'round1_case_count': round1_case_qs.count(), 'round1_demand_count': round1_demand_qs.count(), 'round1_testType_str': '、'.join(round1_testType_list), 'testType_count': len(round1_testType_list), 'round1_version': so_dut_verson, 'round1_problem_count': len(problem_qs), 'end_time_year': date.today().year, 'end_time_month': date.today().month, 'round_list': round_list } # 注入时间 timer = DocTime(id) context.update(**timer.bg_completion_situation()) return create_bg_docx('测评完成情况.docx', context, id) # 生成综述 @route.get('/create/summary', url_name='create-summary') def create_summary(self, id: int): project_obj = get_object_or_404(Project, id=id) # 找出所有问题单 problem_qs = project_obj.projField.all() problem_grade_dict = {} problem_type_dict = {} # 建议问题统计 problem_suggest_count = 0 problem_suggest_solved_count = 0 for problem in problem_qs: grade_key: str = get_str_dict(problem.grade, 'problemGrade') type_key: str = get_str_dict(problem.type, 'problemType') # 问题等级字典-计数 if grade_key in problem_grade_dict.keys(): problem_grade_dict[grade_key] += 1 else: problem_grade_dict[grade_key] = 1 # 问题类型字典-计数 if type_key in problem_type_dict.keys(): problem_type_dict[type_key] += 1 else: problem_type_dict[type_key] = 1 # 建议问题统计 if problem.grade == '3': problem_suggest_count += 1 if problem.status == '1': problem_suggest_solved_count += 1 problem_grade_list = [] problem_type_list = [] for key, value in problem_grade_dict.items(): problem_grade_list.append("".join([f"{key}问题", f"{value}个"])) for key, value in problem_type_dict.items(): problem_type_list.append("".join([f"{key}", f"{value}个"])) # 用来生成建议问题信息 if problem_suggest_count > 0 and problem_suggest_count - problem_suggest_solved_count > 0: all_str = (f"测评过程中提出了{problem_suggest_count}个建议改进," f"其中{problem_suggest_solved_count}个建议改进已修改," f"剩余{problem_suggest_count - problem_suggest_solved_count}个未修改并经总体单位认可同意") elif problem_suggest_count > 0 and problem_suggest_count - problem_suggest_solved_count == 0: all_str = (f"测评过程中提出了{problem_suggest_count}个建议改进," f"全部建议问题已修改") else: all_str = f"测评过程中未提出建议项。" context = { 'problem_count': problem_qs.count(), 'problem_grade_str': "、".join(problem_grade_list), 'problem_type_str': '、'.join(problem_type_list), 'all_str': all_str, } return create_bg_docx('综述.docx', context, id) # 生成测试内容和结果[报告非常关键的一环-大模块] @route.get('/create/contentandresults_1', url_name='create-contentandresults_1') @transaction.atomic def create_content_results_1(self, id: int): project_obj = get_object_or_404(Project, id=id) project_ident = project_obj.ident # ~~~~首轮信息~~~~ round1 = project_obj.pField.filter(key='0').first() # !warning轮次1对象 # 1.处理首轮文档名称,新修改,这里取全部轮次的文档内容 doc_list = [] round1_duts = project_obj.pdField.filter(Q(type='SJ') | Q(type='XQ') | Q(type='XY')) index = 1 for dut in round1_duts: dut_dict = { 'name': dut.name, 'ident': dut.ref, 'version': dut.version, 'index': index } doc_list.append(dut_dict) index += 1 # 2.处理首轮文档问题的统计 - 注意去重 problems = project_obj.projField.all().distinct() # !important:大变量-项目所有问题 problems_r1 = problems.filter(case__round__key='0') # !important:大变量-首轮的所有问题 problems_doc_r1 = problems_r1.filter(case__test__testType='8') # 第一轮所有文档问题 # 3.第一轮代码审查问题统计/版本 source_r1_dut = round1.rdField.filter(type='SO').first() # !warning:小变量-第一轮源代码对象 program_r1_problems = problems_r1.filter(case__test__testType='2') # 4.第一轮代码走查问题统计/版本 zou_r1_problems = problems_r1.filter(case__test__testType='3') # 找下是否存在代码走查测试项 r1_demand_qs = round1.rtField.filter(testType='3') has_zou = True if r1_demand_qs.count() > 0 else False # 5.第一轮静态分析问题统计 static_problems = problems_r1.filter(case__test__testType='15') # 6.第一轮动态测试用例个数(动态测试-非静态分析、文档审查、代码审查、代码走查4个) case_r1_qs = round1.rcField.filter(~Q(test__testType='2'), ~Q(test__testType='3'), ~Q(test__testType='8'), ~Q(test__testType='15'), round__key='0') # !warning:中变量-第一轮动态测试用例qs testType_list, testType_count = create_str_testType_list(case_r1_qs) ## 动态测试(第一轮)各个类型测试用例执行表/各个测试需求表 demand_r1_dynamic_qs = round1.rtField.filter(~Q(testType='2'), ~Q(testType='3'), ~Q(testType='8'), ~Q(testType='15')) # !warning:中变量:第一轮动态测试的测试项 summary_r1_demand_info, summry_r1_demandType_info = create_demand_summary(demand_r1_dynamic_qs, project_ident) # N.第一轮所有动态问题统计 problems_dynamic_r1 = problems_r1.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_r1_type_str = create_problem_type_str(problems_dynamic_r1) problem_dynamic_r1_grade_str = create_problem_grade_str(problems_dynamic_r1) context = { 'project_name': project_obj.name, 'doc_list': doc_list, 'r1_doc_problem_count': problems_doc_r1.count(), 'r1_doc_problem_str': f"{',其中' + create_problem_grade_str(problems_doc_r1) if problems_doc_r1.count() > 0 else '即未发现问题'}", 'r1_version': source_r1_dut.version if source_r1_dut else "未录入首轮版本信息", 'r1_program_problem_count': program_r1_problems.count(), 'r1_program_problem_str': f'{",其中" + create_problem_grade_str(program_r1_problems) if program_r1_problems.count() > 0 else "即未发现问题"}', 'r1_zou_problem_count': zou_r1_problems.count(), 'r1_zou_problem_str': f'{",其中" + create_problem_grade_str(zou_r1_problems) if zou_r1_problems.count() > 0 else "即未发现问题"}', 'has_zou': has_zou, 'r1_static_problem_count': static_problems.count(), 'r1_static_problem_str': f"{',其中' + create_problem_grade_str(static_problems) if static_problems.count() > 0 else '即未发现问题'}", 'r1_case_count': case_r1_qs.count(), 'r1_case_testType': "、".join(testType_list), 'r1_case_testType_count': testType_count, 'r1_problem_counts': len(problems_dynamic_r1), 'r1_exe_info_all': summary_r1_demand_info, 'r1_exe_info_type': summry_r1_demandType_info, 'r1_dynamic_problem_str': problem_dynamic_r1_type_str, 'r1_dynamic_problem_grade_str': problem_dynamic_r1_grade_str, } return create_bg_docx("测试内容和结果_第一轮次.docx", context, id) # 查询除第一轮以外,生成其他轮次测试内容和结果 @route.get('/create/contentandresults_2', url_name='create-contentandresults_2') @transaction.atomic def create_content_results_2(self, id: int): project_obj = get_object_or_404(Project, id=id) # 查询除第一轮,其他有几轮 round_qs = project_obj.pField.filter(~Q(key='0')) round_str_list = [item.key for item in round_qs] # 每个轮次都需要生成一个测试内容和标题 project_path_str = project_path(id) for round_str in round_str_list: 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, autoescape=True) try: doc.save( Path.cwd() / "media" / project_path_str / "output_dir/bg" / f"测试内容和结果_第{context['round_id']}轮次.docx") except PermissionError: ChenResponse(code=400, status=400, message='您已打开生成文件,请关闭后再试...') # 软件问题统计 @route.get('/create/problem_statistics') @transaction.atomic def create_problem_statistics(self, id: int): project_obj = get_object_or_404(Project, id=id) problems = project_obj.projField.all().distinct() # 项目所有问题单 context = { 'closed_count': problems.filter(status='1').count(), 'noclosed_count': problems.count() - problems.filter(status='1').count(), 'problem_table': create_problem_table(problems), 'problem_table_2': create_problem_type_table(problems) } return create_bg_docx("软件问题统计.docx", context, id) # 测试有效性充分性说明 @route.get('/create/effect_and_adquacy', url_name='create-effect_and_adquacy') @transaction.atomic def create_effect_and_adquacy(self, id: int): project_obj = get_object_or_404(Project, id=id) # 判断是否为鉴定 is_JD = False if project_obj.report_type == '9': is_JD = True # 统计测试项数量 demand_qs = project_obj.ptField # 统计用例个数 case_qs = project_obj.pcField # 测试用例的类型统计个数 testType_list, testType_count = create_str_testType_list(case_qs.all()) # 问题单总个数 problem_qs = project_obj.projField context = { 'project_name': project_obj.name, 'demand_count': demand_qs.count(), 'case_count': case_qs.count(), 'testType_list': "、".join(testType_list), 'testType_count': testType_count, 'problem_count': problem_qs.count(), 'is_JD': is_JD, } return create_bg_docx('测试有效性充分性说明.docx', context, id) # 需求指标符合性情况 @route.get('/create/demand_effective', url_name='create-demand_effective') @transaction.atomic def create_demand_effective(self, id: int): project_obj = get_object_or_404(Project, id=id) # 查询所有需求规格说明的 - 设计需求 round1_design_qs = project_obj.psField.filter(round__key='0', dut__type='XQ') # qs:第一轮需求文档的设计需求 # 将第一轮需求文档名称 dut_name = f"《{project_obj.name}软件需求规格说明》" data_list = [] design_index = 1 for design in round1_design_qs: # 如果为“/”则写为隐含需求 if design.chapter.strip() == '/': design_dict = {'source': "隐含需求"} else: design_dict = {'source': "".join([dut_name, design.name, ':', design.chapter])} # 将设计需求描述筛入 rich_parser = RichParser(design.description) p_list = rich_parser.get_final_p_list() design_dict['description'] = '\a'.join(p_list) # 找出其中所有demand demand_qs = design.dtField.all() if not demand_qs.exists(): design_dict['demands'] = '未关联测试项' else: demand_list = [] index = 0 for demand in demand_qs: index += 1 demand_abbr = get_str_abbr(demand.testType, 'testType') demand_list.append(f'{index}、XQ_{demand_abbr}_{demand.ident}-{demand.name}') design_dict['demands'] = '\a'.join(demand_list) # 通过还是未通过 design_dict['pass'] = '通过' design_dict['index'] = design_index data_list.append(design_dict) design_index += 1 # ~~~~指标符合性表~~~~ data_yz_list = [] # qs:第一轮需求文档的设计需求 has_YZ = False round1_design_yz_qs = project_obj.psField.filter(round__key='0', dut__type='YZ') if round1_design_yz_qs.exists(): has_YZ = True # 如果有研制总要求的dut,继续 for design in round1_design_yz_qs: rich_parser2 = RichParser(design.description) p_list = rich_parser2.get_final_p_list() design_dict = { 'yz_des': "".join([design.chapter, '章节:', design.name, '\a', '\a'.join(p_list)])} # 找出其中所有demand demand_qs = design.dtField.all() if not demand_qs.exists(): design_dict['demands'] = '未关联测评大纲条款' else: # 大纲条款的列表 demand_list = [] demand_step_list = [] index = 0 for demand in demand_qs: index += 1 demand_list.append(f'{index}、{demand.ident}-{demand.name}') # 测试需求步骤的列表 step_list = [] for step in demand.testQField.all(): step_list.append(step.subName) demand_step_list.append('\a'.join(step_list)) design_dict['demands'] = '\a'.join(demand_list) design_dict['steps'] = '\a'.join(demand_step_list) # 通过还是未通过 design_dict['pass'] = '通过' data_yz_list.append(design_dict) # 处理没有steps字段 if 'steps' not in design_dict: design_dict['steps'] = '该设计需求未关联测评大纲条款' context = { 'data_list': data_list, 'data_yz_list': data_yz_list, 'has_YZ': has_YZ, } return create_bg_docx('需求指标符合性情况.docx', context, id) # 软件质量评价 @route.get('/create/quality_evaluate', url_name='create-quality_evaluate') @transaction.atomic def create_quality_evaluate(self, id: int): project_obj = get_object_or_404(Project, id=id) # 找出最后一轮 rounds = project_obj.pField.order_by('-key') # qs:轮次 last_dut_so: Optional[Dut] = None for round in rounds: # 查询其源代码dut dut_so = round.rdField.filter(type='SO').first() if dut_so: last_dut_so = dut_so break # 计算千行缺陷率 problem_count = project_obj.projField.count() # 如果没有轮次信息则返回错误 if not last_dut_so: return ChenResponse(code=400, status=400, message='您还未创建轮次,请进入工作区创建') # 计算注释率 ## 总行数 total_lines = int(last_dut_so.total_lines) ## 有效注释行 effective_comment_lines = int(last_dut_so.comment_lines) comment_ratio = (effective_comment_lines / total_lines) * 100 context = { 'last_version': last_dut_so.version, # 最后轮次代码版本 'comment_percent': format(comment_ratio, '.4f'), # 最后轮次代码注释率 'qian_comment_rate': format(problem_count / int(last_dut_so.total_lines) * 1000, '.4f'), 'avg_function_lines': "XXXX", 'avg_cyclomatic': 'XXXX', 'avg_fan_out': 'XXXX', } # 判断是否有metrics一对一模型关联 if hasattr(last_dut_so, 'metrics'): context['avg_function_lines'] = str(last_dut_so.metrics.avg_function_lines) context['avg_cyclomatic'] = str(last_dut_so.metrics.avg_cyclomatic) context['avg_fan_out'] = str(last_dut_so.metrics.avg_fan_out) return create_bg_docx('软件质量评价.docx', context, id) # 软件总体结论 @route.get('/create/entire', url_name='create-entire') @transaction.atomic def create_entire(self, id: int): project_obj = get_object_or_404(Project, id=id) # 是否鉴定 is_JD = False if project_obj.report_type == '9': is_JD = True # 找出最后一轮并且有源代码的dut rounds = project_obj.pField.order_by('-key') # qs:轮次 last_dut_so: Optional[Dut] = None for round in rounds: # 查询其源代码dut dut_so = round.rdField.filter(type='SO').first() if dut_so: last_dut_so = dut_so break # 找出所有被测件协议(XY)、需求规格说明(XQ)、设计说明(SJ) duties_qs = project_obj.pdField.filter(Q(type='XQ') | Q(type='SJ') | Q(type='XY')) # ***Inspect-start*** if not last_dut_so: self.logger.model = '测评报告' self.logger.write_warning_log('总体结论', f'项目没创建轮次,请检查') return None # ***Inspect-end*** context = { 'name': project_obj.name, 'last_version': last_dut_so.version, 'is_JD': is_JD, 'dut_list': [ { 'index': index + 1, 'name': dut_single.name, 'ref': dut_single.ref, 'version': dut_single.version, } for index, dut_single in enumerate(duties_qs) ], 'last_dut_so_ref': last_dut_so.ref, } return create_bg_docx('总体结论.docx', context, id) # 研总需求追踪 - 注意生成每个轮次的追踪 @route.get('/create/yzxq_track', url_name='create-yzxq_track') @transaction.atomic def create_yzxq_track(self, id: int): project_obj = get_object_or_404(Project, id=id) # 是否是鉴定的变量,如果为鉴定,则需要研总的追踪 is_JD = False if project_obj.report_type == '9': is_JD = True # 查询多少个轮次 round_count = project_obj.pField.count() round_str_list = [str(i) for i in range(round_count)] # 生成研总的design_list design_list_all = [] for round_str in round_str_list: # 找寻轮次里面源代码版本 dut_version = 'XXX' dut_so = Dut.objects.filter(round__key=round_str, type='SO').first() if dut_so: dut_version = dut_so.version if is_JD: design_list_yz = create_bg_round1_zhui(project_obj, dut_str='YZ', round_str=round_str) one_table_dict = { 'design_list': design_list_yz, 'version': 'V' + dut_version, 'title': '研制总要求' } design_list_all.append(one_table_dict) design_list_xq = create_bg_round1_zhui(project_obj, dut_str='XQ', round_str=round_str) one_table_dict_xq = { 'design_list': design_list_xq, 'version': 'V' + dut_version, 'title': '需求规格说明' } design_list_all.append(one_table_dict_xq) else: design_list_xq = create_bg_round1_zhui(project_obj, dut_str='XQ', round_str=round_str) one_table_dict_xq = { 'design_list': design_list_xq, 'version': 'V' + dut_version, 'title': '需求规格说明' } design_list_all.append(one_table_dict_xq) context = { 'design_list_all': design_list_all, } # 手动渲染tpl文档 project_path_str = project_path(id) input_file = Path.cwd() / 'media' / project_path_str / 'form_template' / 'bg' / '研总需归追踪.docx' 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, autoescape=True) doc.save(temporary_file) # 通过docx合并单元格 if temporary_file.is_file(): try: docu = Document(temporary_file) # 循环找到表格 for table in docu.tables: util.merge_all_cell(table) # 储存到合适位置 docu.save(out_put_file) return ChenResponse(code=200, status=200, message='文档生成成功...') except PermissionError: return ChenResponse(code=400, status=400, message='请检查文件是否打开,如果打开则关闭...') else: return ChenResponse(code=400, status=400, message='中间文档未找到,请检查你模版是否存在...') # 生成问题汇总表 @route.get('/create/problems_summary', url_name='create-problem_summary') @transaction.atomic def create_problem_summary(self, id: int): tpl_doc = Path.cwd() / "media" / project_path(id) / "form_template" / "bg" / "问题汇总表.docx" doc = DocxTemplate(tpl_doc) project_obj = get_object_or_404(Project, id=id) problem_prefix = "_".join(['PT', project_obj.ident]) problems = project_obj.projField # 先查询有多少轮次 round_count = project_obj.pField.count() round_str_list = [str(x) for x in range(round_count)] data_list = [] for round_str in round_str_list: # 查询所属当前轮次的SO-dut so_dut = Dut.objects.filter(round__key=round_str, type='SO').first() round_dict = { 'static': [], 'dynamic': [], 'version': so_dut.version if so_dut else "v1.0", } # 找出轮次中静态问题 r1_static_problems = problems.filter(case__round__key=round_str, case__test__testType__in=['2', '3', '8', '15']).distinct() for problem in r1_static_problems: problem_dict = create_one_problem_dit(problem, problem_prefix, doc) round_dict['static'].append(problem_dict) # 找出轮次中动态问题 r1_dynamic_problems = problems.filter(case__round__key=round_str).exclude( case__test__testType__in=['2', '3', '8', '15']).distinct() for problem in r1_dynamic_problems: problem_dict = create_one_problem_dit(problem, problem_prefix, doc) round_dict['dynamic'].append(problem_dict) data_list.append(round_dict) context = { 'data_list': data_list } 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="文档生成成功!") except PermissionError as e: return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e)) # 生成摸底清单 @route.get('/create/modi_list', url_name='create-modi-list') @transaction.atomic def create_modi_list(self, id: int): tpl_doc = Path.cwd() / "media" / project_path(id) / "form_template" / "bg" / "摸底清单.docx" doc = DocxTemplate(tpl_doc) project_obj = get_object_or_404(Project, id=id) # 查询所有轮次“摸底测试”的测试项 demands_qs = project_obj.ptField.all() modi_list = [] for demand in demands_qs: one_modi = {} testType_str = get_str_dict(demand.testType, 'testType') if "摸底" in testType_str: # 1.找到设计需求章节号以及描述 design = demand.design one_modi['source'] = f"{design.chapter}-{design.name}" if design.chapter != '/' else "隐含需求" one_modi['desc'] = "\a".join(RichParser(design.description).get_final_p_list()) # 2.找所有的case case_qs = demand.tcField.all() one_modi['result'] = [] for case in case_qs: # 找case的步骤 for step in case.step.all(): if step.passed == '1': # 只获取通过的 one_modi['result'].append("\a".join(RichParser(step.result).get_final_p_list())) modi_list.append(one_modi) # 渲染上下文 context = { 'modi_list': modi_list, } 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="文档生成成功!") except PermissionError as e: return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))