修复:测试记录序号、测评报告统计、富文本渲染word字符问题

This commit is contained in:
2026-02-09 15:41:17 +08:00
parent 2f58bdc668
commit 74d3d22ffe
85 changed files with 1236 additions and 52 deletions

View File

@@ -19,7 +19,7 @@ from utils.util import get_str_dict, get_list_dict, create_problem_grade_str, cr
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.content_result_tool import create_round_context, create_influence_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
@@ -354,9 +354,11 @@ class GenerateControllerBG(ControllerBase):
# 每个轮次都需要生成一个测试内容和标题
project_path_str = project_path(id)
for round_str in round_str_list:
context = create_round_context(project_obj, round_str)
context, round_obj = create_round_context(project_obj, round_str)
template_path = Path.cwd() / 'media' / project_path_str / 'form_template' / 'bg' / '测试内容和结果_第二轮次.docx'
doc = DocxTemplate(template_path)
# ~~~额外添加:除第一轮次的影响域分析~~~
context['influence'] = create_influence_context(doc, round_obj, project_obj)
doc.render(context, autoescape=True)
try:
doc.save(
@@ -442,7 +444,7 @@ class GenerateControllerBG(ControllerBase):
design_dict['demands'] = '\a'.join(demand_list)
# 通过还是未通过
design_dict['pass'] = '通过'
design_dict['index'] = design_index
design_dict['index'] = design_index # noqa
data_list.append(design_dict)
design_index += 1

View File

@@ -10,7 +10,7 @@ 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, InlineImage, Subdoc
from docxtpl import DocxTemplate, InlineImage
from pathlib import Path
from utils.chen_response import ChenResponse
# 导入数据库ORM
@@ -82,12 +82,12 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
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
@@ -110,7 +110,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
"test_demand_content": content_list,
"testMethod": testmethod_str.strip(),
"adequacy": single_qs.adequacy.replace("\n", "\a"),
"testDesciption": single_qs.testDesciption.replace("\n", "\a"), # 测试项描述
# 测试项描述FPGA或'静态分析'、'文档审查'、'代码审查'
"testDesciption": single_qs.testDesciption.replace("\n", "\a"),
"testType": get_testType(single_qs.testType, 'testType'),
}
list_list[type_index].append(testdemand_dict)
@@ -470,11 +471,11 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 通用生成静态软件项、静态硬件项、动态软件项、动态硬件信息的context包含fontnote和table
@classmethod
def create_table_context(cls, table_data: list[list[str]], doc: DocxTemplate) -> Subdoc:
def create_table_context(cls, table_data: list[list[str]], doc: DocxTemplate):
"""注意:该函数会增加一列序号列"""
subdoc = doc.new_subdoc()
rows = len(table_data)
cols = len(table_data[0]) + 1 # 多渲染序号列
cols = len(table_data[0]) + 1 # 多渲染一个序号列
table = subdoc.add_table(rows=rows, cols=cols)
# 单元格处理
for row in range(rows):

View File

@@ -1,6 +1,6 @@
from pathlib import Path
from copy import deepcopy
from typing import Union, TypedDict, Optional
from typing import Union
from ninja_extra import api_controller, ControllerBase, route
from django.db import transaction
from django.shortcuts import get_object_or_404
@@ -20,6 +20,7 @@ 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 utils.util import get_str_abbr
from apps.createDocument.extensions.content_result_tool import create_influence_context
# 导入生成日志记录模块
from apps.createSeiTaiDocument.extensions.logger import GenerateLogger
# 导入排序
@@ -236,35 +237,9 @@ class GenerateControllerHSM(ControllerBase):
message=f'您第{chinese_round_name[int(hround.key)]}轮次中缺少源代码版本信息,请添加')
last_dm_version = last_round_so_dut.version
now_dm_version = so_dut.version
# 这里插入影响域分析部分
## 先查找是否有影响域分析填写
area_qs = InfluenceArea.objects.filter(round=hround)
## 如果存在则查询items
item_render_list = []
if area_qs.exists():
area_obj = area_qs.first()
items_qs = area_obj.influence_items.all()
if items_qs.exists():
index = 1
for item in items_qs:
# 1.处理关联case - 找第一轮cases
case_str_list = []
for case in project_obj.pcField.filter(key__in=item.effect_cases):
case_ident_index = str(int(case.key.split("-")[-1]) + 1).zfill(3)
case_str_list.append("_".join(["YL", get_str_abbr(case.test.testType, "testType"), case.ident, case_ident_index]))
# 2.处理富文本框
parser = RichParser(item.change_des)
item_dict = {
"change_type": item.change_type,
"change_influ": item.change_influ,
"case_str_list": case_str_list,
"change_des": parser.get_final_list(doc, img_size=40, height=30), # 富文本未处理
"index": str(index),
}
index = index + 1
item_render_list.append(item_dict)
# 将影响域分析加入context
context_round['influence'] = item_render_list # noqa
# 这里插入影响域分析部分并加入context
context_round['influence'] = create_influence_context(doc, hround, project_obj) # noqa
context_round['influence'] = None
# 如果存在这个轮次的需求文档,则查询上个版本
last_xq_version = ""
if xq_dut:

View File

@@ -1,7 +1,9 @@
from apps.project.models import Project
from apps.project.models import Project, Round, InfluenceArea
from docxtpl import DocxTemplate
from utils.util import *
from utils.chen_response import ChenResponse
from django.db.models import Q
from apps.createDocument.extensions.parse_rich_text import RichParser
def create_round_context(project_obj: Project, round_id: str):
"""根据轮次,生成测评报告中的测评结果"""
@@ -77,4 +79,37 @@ def create_round_context(project_obj: Project, round_id: str):
'r2_dynamic_str': r2_dynamic_str,
'round_id': round_chinese[round_id],
}
return context
return context, round_obj
# ~~~影响域分析内容返回influence的render_list~~~
def create_influence_context(doc: DocxTemplate, round_obj: Round, project_obj: Project) -> None | list:
area_qs = InfluenceArea.objects.filter(round=round_obj)
item_render_list = []
## 如果存在则查询items
if area_qs.exists():
area_obj = area_qs.first()
items_qs = area_obj.influence_items.all()
if items_qs.exists():
index = 1
for item in items_qs:
# 1.处理关联case - 找第一轮cases
case_str_list = []
for case in project_obj.pcField.filter(key__in=item.effect_cases):
case_ident_index = str(int(case.key.split("-")[-1]) + 1).zfill(3)
case_str_list.append("_".join(["YL", get_str_abbr(case.test.testType, "testType"), case.ident, case_ident_index]))
# 2.处理富文本框
parser = RichParser(item.change_des)
item_dict = {
"change_type": item.change_type,
"change_influ": item.change_influ,
"case_str_list": case_str_list,
"change_des": parser.get_final_list(doc, img_size=40, height=30), # 富文本未处理
"index": str(index),
}
index = index + 1
item_render_list.append(item_dict)
if len(item_render_list) > 0:
return item_render_list
else:
return None

View File

@@ -22,6 +22,8 @@ class RichParser:
# 最终的解析后的列表
self.data_list = []
self.line_parse()
# 匹配“表1-3”或“表1”等字符的正则
self.biao_pattern = re.compile(r"\d+(?:-\d+)?")
# 1.函数将self.bs.contents去掉\n获取每行数据
def remove_n_in_contents(self):
@@ -127,6 +129,13 @@ class RichParser:
for oneline in self.data_list:
if isinstance(oneline, list) or oneline.startswith("data:image/png;base64"):
continue
else:
final_list.append(oneline)
cleaned_line = oneline
cleaned_line = re.sub(r'\s+', '', cleaned_line)
cleaned_line = cleaned_line.replace(')', '')
cleaned_line = cleaned_line.strip()
# 去掉以“表3”的行
if self.biao_pattern.search(cleaned_line):
continue
if cleaned_line:
final_list.append(cleaned_line)
return final_list

View File

@@ -95,3 +95,4 @@ def delete_dir_files(path: Path) -> Any:
for file in path.iterdir():
if file.is_file():
file.unlink()

View File

@@ -182,6 +182,8 @@ def auto_create_wd(user_name: str, dut_qs: Dut, project_obj: Project):
}
new_wd_design_obj: Design = Design.objects.create(**wd_design_create_dict)
# 1.1.1.自动创建demand文档审查
is_JD = (project_obj.report_type == '9')
test_des = "本次三方文档审查内容包括软件需求规格说明、软件设计说明等"
wd_demand_create_dict = {
'ident': 'WDSC',
'name': '文档审查',
@@ -206,7 +208,7 @@ def auto_create_wd(user_name: str, dut_qs: Dut, project_obj: Project):
'13软件研制总结报告\a'
'14软件版本说明\a'
'15软件产品规格说明\a'
'16固件保障手册',
'16固件保障手册' if is_JD else test_des,
'key': ''.join([new_wd_design_obj.key, '-', '0']),
'level': '3',
'project': project_obj,

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -16,7 +16,7 @@ dependencies = [
"django-shortuuidfield>=0.1.3",
"django-tinymce>=5.0.0",
"docxcompose",
"docxtpl>=0.20.2",
"docxtpl[subdoc]>=0.20.2",
"faker>=40.4.0",
"ipykernel>=7.2.0",
"lizard>=1.21.0",
@@ -27,6 +27,7 @@ dependencies = [
"pandas>=3.0.0",
"python-docx>=1.2.0",
"python-ldap",
"setuptools<=81.0.0",
"ua-parser-builtins>=202601",
"user-agents>=2.2.0",
"waitress>=3.0.2",

23
uv.lock generated
View File

@@ -81,7 +81,7 @@ dependencies = [
{ name = "django-shortuuidfield" },
{ name = "django-tinymce" },
{ name = "docxcompose" },
{ name = "docxtpl" },
{ name = "docxtpl", extra = ["subdoc"] },
{ name = "faker" },
{ name = "ipykernel" },
{ name = "lizard" },
@@ -92,6 +92,7 @@ dependencies = [
{ name = "pandas" },
{ name = "python-docx" },
{ name = "python-ldap" },
{ name = "setuptools" },
{ name = "ua-parser-builtins" },
{ name = "user-agents" },
{ name = "waitress" },
@@ -110,7 +111,7 @@ requires-dist = [
{ name = "django-shortuuidfield", specifier = ">=0.1.3" },
{ name = "django-tinymce", specifier = ">=5.0.0" },
{ name = "docxcompose" },
{ name = "docxtpl", specifier = ">=0.20.2" },
{ name = "docxtpl", extras = ["subdoc"], specifier = ">=0.20.2" },
{ name = "faker", specifier = ">=40.4.0" },
{ name = "ipykernel", specifier = ">=7.2.0" },
{ name = "lizard", specifier = ">=1.21.0" },
@@ -121,6 +122,7 @@ requires-dist = [
{ name = "pandas", specifier = ">=3.0.0" },
{ name = "python-docx", specifier = ">=1.2.0" },
{ name = "python-ldap", path = "python_ldap-3.4.5-cp313-cp313-win_amd64.whl" },
{ name = "setuptools", specifier = "<=81.0.0" },
{ name = "ua-parser-builtins", specifier = ">=202601" },
{ name = "user-agents", specifier = ">=2.2.0" },
{ name = "waitress", specifier = ">=3.0.2" },
@@ -389,6 +391,11 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/a4/ad/e07939d8e020e513d3860400413ba1e0e06102c469639b440d921337efef/docxtpl-0.20.2-py3-none-any.whl", hash = "sha256:626d5c570a46a62b2ca73b4d08f1c240fa031a5bc45371e1466a4fe184923d10" },
]
[package.optional-dependencies]
subdoc = [
{ name = "docxcompose" },
]
[[package]]
name = "email-validator"
version = "2.3.0"
@@ -1051,11 +1058,11 @@ wheels = [
[[package]]
name = "setuptools"
version = "80.10.2"
version = "81.0.0"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/76/95/faf61eb8363f26aa7e1d762267a8d602a1b26d4f3a1e758e92cb3cb8b054/setuptools-80.10.2.tar.gz", hash = "sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a" }
wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/94/b8/f1f62a5e3c0ad2ff1d189590bfa4c46b4f3b6e49cef6f26c6ee4e575394d/setuptools-80.10.2-py3-none-any.whl", hash = "sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173" },
{ url = "https://mirrors.aliyun.com/pypi/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6" },
]
[[package]]
@@ -1209,9 +1216,9 @@ wheels = [
[[package]]
name = "wcwidth"
version = "0.5.3"
version = "0.6.0"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c2/62/a7c072fbfefb2980a00f99ca994279cb9ecf310cb2e6b2a4d2a28fe192b3/wcwidth-0.5.3.tar.gz", hash = "sha256:53123b7af053c74e9fe2e92ac810301f6139e64379031f7124574212fb3b4091" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159" }
wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/3c/c1/d73f12f8cdb1891334a2ccf7389eed244d3941e74d80dd220badb937f3fb/wcwidth-0.5.3-py3-none-any.whl", hash = "sha256:d584eff31cd4753e1e5ff6c12e1edfdb324c995713f75d26c29807bb84bf649e" },
{ url = "https://mirrors.aliyun.com/pypi/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad" },
]