新增:影响域分析web录入

This commit is contained in:
2026-02-07 17:26:34 +08:00
parent 0bee950a52
commit a76cd8674c
76 changed files with 859 additions and 133 deletions

View File

@@ -15,7 +15,7 @@ from pathlib import Path
from utils.chen_response import ChenResponse from utils.chen_response import ChenResponse
# 导入数据库ORM # 导入数据库ORM
from apps.project.models import Project, Contact, Abbreviation, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, \ from apps.project.models import Project, Contact, Abbreviation, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, \
DynamicSoftTable, DynamicHardwareTable DynamicSoftTable, DynamicHardwareTable, ProjectDynamicDescription, EvaluateData, EnvAnalysis
from apps.dict.models import Dict 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.util import get_str_dict, get_list_dict, get_testType, get_ident, get_str_abbr
@@ -290,67 +290,80 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
except PermissionError as e: except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e)) return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
# 生成dataSchemas的context - 服务于 1、测评对象 2、动态环境描述
@classmethod
def create_data_schema_list_context(cls, data_qs, doc: DocxTemplate):
if data_qs.exists():
data_list = []
for data_obj in data_qs.all():
item_context: dict[str, Any] = {"fontnote": data_obj.fontnote, 'type': data_obj.type}
# 根据数据类型处理content字段
if data_obj.type == 'text':
item_context['content'] = data_obj.content
elif data_obj.type == 'table':
# 使用subdoc
subdoc = doc.new_subdoc()
rows = len(data_obj.content)
cols = len(data_obj.content[0])
table = subdoc.add_table(rows=rows, cols=cols)
# 单元格处理
for row in range(rows):
for col in range(cols):
cell = table.cell(row, col)
cell.text = data_obj.content[row][col]
# 第一行设置居中
if row == 0:
# 黑体设置
cell.text = ""
pa = cell.paragraphs[0]
run = pa.add_run(str(data_obj.content[row][col]))
run.font.name = '黑体'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
run.font.bold = False
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 垂直居中
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
# 表格居中
table.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 设置边框
set_table_border_by_cell_position(table)
item_context['content'] = subdoc
elif data_obj.type == 'image':
base64_bytes = base64.b64decode(data_obj.content.replace("data:image/png;base64,", ""))
item_context['content'] = InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(120))
data_list.append(item_context)
context = {
"datas": data_list,
}
return context
return None
# 统将需要多个DataSchemas的一对一项目字段生成响应
@classmethod
def uniform_res_from_mul_data_schemas(cls, id: int, filename: str, r_filename: str, model) -> ChenResponse | None:
project_obj = get_object_or_404(Project, id=id)
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / filename
doc = DocxTemplate(input_path)
qs = model.objects.filter(project=project_obj)
if qs.exists():
data_qs = qs.first().data_schemas
context = cls.create_data_schema_list_context(data_qs, doc)
doc.render(context)
try:
doc.save(Path.cwd() / "media" / project_path(id) / "output_dir" / r_filename)
return ChenResponse(status=200, code=200, message="文档生成成功!")
except PermissionError as e:
return ChenResponse(status=400, code=400, message="模版文件已打开,请关闭后再试,{0}".format(e))
return None
# 生成测评对象 - 包括大纲、说明、回归说明和报告 # 生成测评对象 - 包括大纲、说明、回归说明和报告
@route.get('/create/softComposition', url_name='create-softComposition') @route.get('/create/softComposition', url_name='create-softComposition')
@transaction.atomic @transaction.atomic
def create_softComposition(self, id: int): def create_softComposition(self, id: int):
# 首先判断是否包含 - 项目信息-软件概述 # 首先判断是否包含 - 项目信息-软件概述
project_obj = get_object_or_404(Project, id=id) res = self.uniform_res_from_mul_data_schemas(id, '测评对象_2.docx', '测评对象.docx', ProjectSoftSummary)
input_path_2 = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评对象_2.docx' if res is not None:
doc = DocxTemplate(input_path_2) return res
soft_summary_qs = ProjectSoftSummary.objects.filter(project=project_obj)
if soft_summary_qs.exists():
data_qs = soft_summary_qs.first().data_schemas
if data_qs.exists():
# 如果存在则渲染此处
data_list = []
for data_obj in data_qs.all():
item_context: dict[str, Any] = {"fontnote": data_obj.fontnote, 'type': data_obj.type}
# 根据数据类型处理content字段
if data_obj.type == 'text':
item_context['content'] = data_obj.content
elif data_obj.type == 'table':
# 使用subdoc
subdoc = doc.new_subdoc()
rows = len(data_obj.content)
cols = len(data_obj.content[0])
table = subdoc.add_table(rows=rows, cols=cols)
# 单元格处理
for row in range(rows):
for col in range(cols):
cell = table.cell(row, col)
cell.text = data_obj.content[row][col]
# 第一行设置居中
if row == 0:
# 黑体设置
cell.text = ""
pa = cell.paragraphs[0]
run = pa.add_run(str(data_obj.content[row][col]))
run.font.name = '黑体'
run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
run.font.bold = False
pa.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 垂直居中
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
# 表格居中
table.alignment = WD_ALIGN_PARAGRAPH.CENTER
# 设置边框
set_table_border_by_cell_position(table)
item_context['content'] = subdoc
elif data_obj.type == 'image':
base64_bytes = base64.b64decode(data_obj.content.replace("data:image/png;base64,", ""))
item_context['content'] = InlineImage(doc, io.BytesIO(base64_bytes), width=Mm(120))
data_list.append(item_context)
context = {
"datas": data_list,
}
doc.render(context)
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))
# 原来文档片段或者初始内容 # 原来文档片段或者初始内容
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评对象.docx' input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评对象.docx'
doc = DocxTemplate(input_path) doc = DocxTemplate(input_path)
@@ -480,6 +493,8 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 处理非第一列 # 处理非第一列
else: else:
cell.text = table_data[row][col - 1] cell.text = table_data[row][col - 1]
# 垂直居中
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
# 单独处理第一行 # 单独处理第一行
for col in range(cols): for col in range(cols):
cell = table.cell(0, col) cell = table.cell(0, col)
@@ -559,9 +574,14 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
} }
return create_dg_docx("静态硬件和固件项.docx", context, id) return create_dg_docx("静态硬件和固件项.docx", context, id)
# 动态测评环境说明 # 动态测评环境说明 - 多dataSchemas格式
@route.get('/create/dynamic_env', url_name='create-dynamic_env') @route.get('/create/dynamic_env', url_name='create-dynamic_env')
def create_dynamic_env(self, id: int): def create_dynamic_env(self, id: int):
res = self.uniform_res_from_mul_data_schemas(id, '动态测试环境说明_2.docx',
'动态测试环境说明.docx', ProjectDynamicDescription)
if res is not None:
return res
# 老内容
project_obj: Project = get_object_or_404(Project, id=id) project_obj: Project = get_object_or_404(Project, id=id)
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '动态测试环境说明.docx' input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '动态测试环境说明.docx'
doc = DocxTemplate(input_path) doc = DocxTemplate(input_path)
@@ -616,6 +636,11 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 测试数据 # 测试数据
@route.get('/create/test_data', url_name='create-test_data') @route.get('/create/test_data', url_name='create-test_data')
def create_test_data(self, id: int): def create_test_data(self, id: int):
res = self.uniform_static_dynamic_response(id, '测评数据_2.docx',
'测评数据.docx', EvaluateData)
if res is not None:
return res
# 老内容
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评数据.docx' input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '测评数据.docx'
doc = DocxTemplate(input_path) doc = DocxTemplate(input_path)
replace, frag, rich_text_list = self._generate_frag(id, doc, '测评数据') replace, frag, rich_text_list = self._generate_frag(id, doc, '测评数据')
@@ -628,6 +653,27 @@ class GenerateControllerDG(ControllerBase, FragementToolsMixin):
# 环境差异性分析 # 环境差异性分析
@route.get('/create/env_diff', url_name='create-env_diff') @route.get('/create/env_diff', url_name='create-env_diff')
def create_env_diff(self, id: int): def create_env_diff(self, id: int):
project_obj: Project = get_object_or_404(Project, id=id)
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '环境差异性分析_2.docx'
doc = DocxTemplate(input_path)
qs = EnvAnalysis.objects.filter(project=project_obj)
if qs.exists():
obj = qs.first()
table_data = obj.table
subdoc = self.create_table_context(table_data, doc)
context = {
"description": obj.description,
"table": subdoc,
"fontnote": obj.fontnote,
}
doc.render(context, autoescape=True)
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))
# 老内容
input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '环境差异性分析.docx' input_path = Path.cwd() / 'media' / project_path(id) / 'form_template' / 'dg' / '环境差异性分析.docx'
doc = DocxTemplate(input_path) doc = DocxTemplate(input_path)
replace, frag, rich_text_list = self._generate_frag(id, doc, '环境差异性分析') replace, frag, rich_text_list = self._generate_frag(id, doc, '环境差异性分析')

View File

@@ -8,7 +8,7 @@ from django.db.models import QuerySet, Q
from docxtpl import DocxTemplate from docxtpl import DocxTemplate
from docx import Document from docx import Document
# 导入模型 # 导入模型
from apps.project.models import Project, Round, Dut from apps.project.models import Project, Round, Dut, InfluenceArea
from apps.dict.models import Dict from apps.dict.models import Dict
# 导入项目工具 # 导入项目工具
from utils.util import get_list_dict, get_str_dict, get_ident, get_case_ident, get_testType from utils.util import get_list_dict, get_str_dict, get_ident, get_case_ident, get_testType
@@ -19,6 +19,7 @@ from utils.path_utils import project_path
from apps.createDocument.extensions.util import delete_dir_files from apps.createDocument.extensions.util import delete_dir_files
from apps.createDocument.extensions.parse_rich_text import RichParser from apps.createDocument.extensions.parse_rich_text import RichParser
from apps.createDocument.extensions.documentTime import DocTime from apps.createDocument.extensions.documentTime import DocTime
from utils.util import get_str_abbr
# 导入生成日志记录模块 # 导入生成日志记录模块
from apps.createSeiTaiDocument.extensions.logger import GenerateLogger from apps.createSeiTaiDocument.extensions.logger import GenerateLogger
# 导入排序 # 导入排序
@@ -235,6 +236,30 @@ class GenerateControllerHSM(ControllerBase):
message=f'您第{chinese_round_name[int(hround.key)]}轮次中缺少源代码版本信息,请添加') message=f'您第{chinese_round_name[int(hround.key)]}轮次中缺少源代码版本信息,请添加')
last_dm_version = last_round_so_dut.version last_dm_version = last_round_so_dut.version
now_dm_version = so_dut.version now_dm_version = so_dut.version
# 这里插入影响域分析部分
## 先查找是否有影响域分析填写
area_qs = InfluenceArea.objects.filter(round=hround)
## 如果存在则查询items
if area_qs.exists():
area_obj = area_qs.first()
items_qs = area_obj.influence_items.all()
if items_qs.exists():
# 遍历items
item_render_list = []
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]))
item_dict = {
"change_type": item.change_type,
"change_influ": item.change_influ,
"case_str_list": case_str_list,
"change_des": item.change_des, # 富文本未处理
}
item_render_list.append(item_dict)
# 如果存在这个轮次的需求文档,则查询上个版本 # 如果存在这个轮次的需求文档,则查询上个版本
last_xq_version = "" last_xq_version = ""
if xq_dut: if xq_dut:

View File

@@ -396,3 +396,21 @@ class CaseController(ControllerBase):
Case.objects.bulk_update(updated_cases, ['exe_time']) Case.objects.bulk_update(updated_cases, ['exe_time'])
return ChenResponse(status=200, code=200, data=len(updated_cases), return ChenResponse(status=200, code=200, data=len(updated_cases),
message=f"成功更新{len(updated_cases)}个用例执行时间") message=f"成功更新{len(updated_cases)}个用例执行时间")
# 给级联选择器数据 -> 上一轮次所有用例
@route.get("/case/getRelatedCase", url_name='case-related-case')
def get_cases_related_case(self, id: int, round_key: str):
project_obj = get_object_or_404(Project, id=id)
previous_round_obj = project_obj.pField.filter(key=int(round_key) - 1).first()
# dut -> design
data_list = []
for dut in previous_round_obj.rdField.all():
dut_dict = {'label': dut.name, 'value': dut.id, 'children': []}
for design in dut.rsField.all():
design_dict = {'label': design.name, 'value': design.id, 'key': design.key, 'children': []}
for case in design.dcField.all():
case_dict = {'label': case.name, 'value': case.id, 'key': case.key}
design_dict['children'].append(case_dict)
dut_dict['children'].append(design_dict)
data_list.append(dut_dict)
return ChenResponse(message='获取成功', data=data_list)

View File

@@ -15,9 +15,9 @@ from ninja import Query
from utils.chen_response import ChenResponse from utils.chen_response import ChenResponse
from utils.chen_crud import create, multi_delete_project from utils.chen_crud import create, multi_delete_project
from apps.project.models import Project, Round, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, DynamicSoftTable, \ from apps.project.models import Project, Round, ProjectSoftSummary, StuctSortData, StaticSoftItem, StaticSoftHardware, DynamicSoftTable, \
DynamicHardwareTable DynamicHardwareTable, ProjectDynamicDescription, EvaluateData, EnvAnalysis
from apps.project.schemas.project import ProjectRetrieveSchema, ProjectFilterSchema, ProjectCreateInput, \ from apps.project.schemas.project import ProjectRetrieveSchema, ProjectFilterSchema, ProjectCreateInput, \
DeleteSchema, SoftSummarySchema, DataSchema, StaticDynamicData DeleteSchema, SoftSummarySchema, DataSchema, StaticDynamicData, EnvAnalysisSchema
from utils.util import get_str_dict from utils.util import get_str_dict
# 时间处理模块 # 时间处理模块
from apps.project.tool.timeList import time_return_to from apps.project.tool.timeList import time_return_to
@@ -99,6 +99,7 @@ class ProjectController(ControllerBase):
except FileNotFoundError: except FileNotFoundError:
return ChenResponse(code=500, status=500, message='文件不存在,请检查') return ChenResponse(code=500, status=500, message='文件不存在,请检查')
return ChenResponse(code=200, status=200, message="添加项目成功,并添加第一轮测试") return ChenResponse(code=200, status=200, message="添加项目成功,并添加第一轮测试")
return ChenResponse(code=400, status=400, message="未添加任何项目")
@route.put("/update/{project_id}") @route.put("/update/{project_id}")
@transaction.atomic @transaction.atomic
@@ -139,7 +140,7 @@ class ProjectController(ControllerBase):
project_media_path = media_path / ident project_media_path = media_path / ident
try: try:
rmtree(project_media_path) rmtree(project_media_path)
except FileNotFoundError as e: except FileNotFoundError:
return ChenResponse(status=400, code=400, message='项目模版目录可能不存在,可能之前已删除') return ChenResponse(status=400, code=400, message='项目模版目录可能不存在,可能之前已删除')
return ChenResponse(message="删除成功!") return ChenResponse(message="删除成功!")
@@ -270,48 +271,59 @@ class ProjectController(ControllerBase):
time = time_return_to(id) time = time_return_to(id)
return time return time
# 项目级信息前端告警数据获取 # [变] 项目级信息前端告警数据获取
@route.get("/project_info_status/") @route.get("/project_info_status/")
@transaction.atomic @transaction.atomic
def project_info_status(self, id: int): def project_info_status(self, id: int):
# 全部状态
all_status = {
"soft_summary": False,
"interface_image": False,
"static_soft_item": False,
"static_soft_hardware": False,
"dynamic_soft_item": False,
"dynamic_soft_hardware": False,
}
# 1.查看软件概述是否填写
project_obj = self.get_project_by_id(id) project_obj = self.get_project_by_id(id)
soft_summary_qs = ProjectSoftSummary.objects.filter(project=project_obj)
if soft_summary_qs.exists(): # 统一配置每个状态的检查逻辑
# 存在还要判断是否有子项 status_configs = {
if soft_summary_qs.first().data_schemas.exists(): "soft_summary": {
all_status['soft_summary'] = True "model": ProjectSoftSummary,
# 2.查看接口图是否填写 "check": lambda qs: qs.exists() and qs.first().data_schemas.exists()
image_qs = StuctSortData.objects.filter(project=project_obj) },
if image_qs.exists(): "interface_image": {
all_status['interface_image'] = True "model": StuctSortData,
# 3.查看静态软件项是否填写 "check": lambda qs: qs.exists()
static_item_qs = StaticSoftItem.objects.filter(project=project_obj) },
if static_item_qs.exists(): "static_soft_item": {
all_status['static_soft_item'] = True "model": StaticSoftItem,
# 4.静态硬件项 "check": lambda qs: qs.exists()
static_hardware_qs = StaticSoftHardware.objects.filter(project=project_obj) },
if static_hardware_qs.exists(): "static_soft_hardware": {
all_status['static_soft_hardware'] = True "model": StaticSoftHardware,
# 5.动态软件项 "check": lambda qs: qs.exists()
dynamic_soft_item_qs = DynamicSoftTable.objects.filter(project=project_obj) },
if dynamic_soft_item_qs.exists(): "dynamic_soft_item": {
all_status['dynamic_soft_item'] = True "model": DynamicSoftTable,
# 5.动态软件项 "check": lambda qs: qs.exists()
dynamic_hardware_qs = DynamicHardwareTable.objects.filter(project=project_obj) },
if dynamic_hardware_qs.exists(): "dynamic_soft_hardware": {
all_status['dynamic_soft_hardware'] = True "model": DynamicHardwareTable,
"check": lambda qs: qs.exists()
},
"dynamic_des": {
"model": ProjectDynamicDescription,
"check": lambda qs: qs.exists() and qs.first().data_schemas.exists()
},
"evaluate_data": {
"model": EvaluateData,
"check": lambda qs: qs.exists()
},
"env_analysis": {
"model": EnvAnalysis,
"check": lambda qs: qs.exists()
}
}
all_status = {}
for status_key, config in status_configs.items():
qs = config["model"].objects.filter(project=project_obj)
all_status[status_key] = config["check"](qs)
return ChenResponse(status=200, code=20000, data=all_status, message='查询成功') return ChenResponse(status=200, code=20000, data=all_status, message='查询成功')
# [变] 封装结构化数据新增-修改针对project - OneToOne - DataSchemas形式
@classmethod @classmethod
def bulk_create_data_schemas(cls, parent_obj, datas: list[DataSchema]): def bulk_create_data_schemas(cls, parent_obj, datas: list[DataSchema]):
""" """
@@ -321,11 +333,13 @@ class ProjectController(ControllerBase):
datas (list[DataSchema]): 数据模式对象列表 datas (list[DataSchema]): 数据模式对象列表
""" """
# 动态确定所属父model # 动态确定所属父model
field_name = None field_name = None # type:ignore
if isinstance(parent_obj, ProjectSoftSummary): if isinstance(parent_obj, ProjectSoftSummary):
field_name = 'soft_summary' field_name = 'soft_summary'
elif isinstance(parent_obj, Project): elif isinstance(parent_obj, Project):
field_name = 'project' field_name = 'project'
elif isinstance(parent_obj, ProjectDynamicDescription):
field_name = 'dynamic_description'
else: else:
raise HttpError(400, "添加的数据未在系统内,请联系管理员") raise HttpError(400, "添加的数据未在系统内,请联系管理员")
@@ -340,39 +354,66 @@ class ProjectController(ControllerBase):
data_list.append(new_data) data_list.append(new_data)
StuctSortData.objects.bulk_create(data_list) StuctSortData.objects.bulk_create(data_list)
# 封装只有model不同 -修改和新增dataSchemas针对project - OneToOne - DataSchemas形式
@classmethod
def create_or_modify_data_schemas(cls, id: int, model, data):
project_obj = get_object_or_404(Project, pk=id)
qs = model.objects.filter(project=project_obj)
if qs.exists():
obj = qs.first()
# 如果存在则修改:先删除再创建
obj.data_schemas.all().delete()
cls.bulk_create_data_schemas(obj, data)
else:
parent_obj = model.objects.create(project=project_obj)
cls.bulk_create_data_schemas(parent_obj, data)
# ~~~软件概述-新增和修改~~~ # ~~~软件概述-新增和修改~~~
@route.post('/soft_summary/') @route.post('/soft_summary/')
@transaction.atomic @transaction.atomic
def soft_summary(self, payload: SoftSummarySchema): def soft_summary(self, payload: SoftSummarySchema):
project_obj = self.get_project_by_id(payload.id) self.create_or_modify_data_schemas(payload.id, ProjectSoftSummary, payload.data)
# 安全获取-软件概述
soft_summary_qs = ProjectSoftSummary.objects.filter(project=project_obj) # ~~~动态环境描述-新增和修改~~~
if soft_summary_qs.exists(): @route.post('/dynamic_description/')
soft_summary = soft_summary_qs.first() @transaction.atomic
# 如果存在则修改:先删除然后创建 def dynamic_description(self, payload: SoftSummarySchema):
soft_summary.data_schemas.all().delete() self.create_or_modify_data_schemas(payload.id, ProjectDynamicDescription, payload.data)
self.bulk_create_data_schemas(soft_summary, payload.data)
else: @classmethod
# 不存在软件概述则创建 def get_res_from_info(cls, project_obj: Project, model) -> list[dict] | None:
soft_summary_obj = ProjectSoftSummary.objects.create(project=project_obj) """model: 当前一对一模型,直接获取结构化数据信息数组返回"""
self.bulk_create_data_schemas(soft_summary_obj, payload.data) qs = model.objects.filter(project=project_obj)
if qs.exists():
obj = qs.first()
ds_qs = obj.data_schemas.all()
data_list = [{
"type": item.type,
"content": item.content,
"fontnote": item.fontnote,
} for item in ds_qs]
return data_list
return None
# ~~~软件概述-获取到前端展示~~~ # ~~~软件概述-获取到前端展示~~~
@route.get("/get_soft_summary/", response=list[DataSchema]) @route.get("/get_soft_summary/", response=list[DataSchema])
@transaction.atomic @transaction.atomic
def get_soft_summary(self, id: int): def get_soft_summary(self, id: int):
project_obj = self.get_project_by_id(id) project_obj = self.get_project_by_id(id)
soft_summary_qs = ProjectSoftSummary.objects.filter(project=project_obj) data_list = self.get_res_from_info(project_obj, ProjectSoftSummary)
if soft_summary_qs.exists(): if data_list:
# 如果存在则返回数据 return ChenResponse(status=200, code=20000, data=data_list)
soft_summary = soft_summary_qs.first() return ChenResponse(status=200, code=20000, data=[])
dataSchem_qs = soft_summary.data_schemas.all()
return ChenResponse(status=200, code=25001, data=[{ # ~~~动态环境描述 - 获取展示~~~
"type": item.type, @route.get("/dynamic_des/", response=list[DataSchema])
"content": item.content, @transaction.atomic
"fontnote": item.fontnote, def get_dynamic_des(self, id: int):
} for item in dataSchem_qs]) project_obj = self.get_project_by_id(id)
return ChenResponse(status=200, code=25002, data=[]) data_list = self.get_res_from_info(project_obj, ProjectDynamicDescription)
if data_list:
return ChenResponse(status=200, code=20000, data=data_list)
return ChenResponse(status=200, code=20000, data=[])
# ~~~接口图新增或修改~~~ # ~~~接口图新增或修改~~~
@route.post("/interface_image/") @route.post("/interface_image/")
@@ -400,13 +441,15 @@ class ProjectController(ControllerBase):
}) })
return ChenResponse(status=200, code=25002, data=None) return ChenResponse(status=200, code=25002, data=None)
# 动态返回是哪个模型
@classmethod @classmethod
def get_model_from_category(cls, category: str): def get_model_from_category(cls, category: str):
mapDict = { mapDict = {
'静态软件项': StaticSoftItem, '静态软件项': StaticSoftItem,
'静态硬件项': StaticSoftHardware, '静态硬件项': StaticSoftHardware,
'动态软件项': DynamicSoftTable, '动态软件项': DynamicSoftTable,
'动态硬件项': DynamicHardwareTable '动态硬件项': DynamicHardwareTable,
'测评数据': EvaluateData
} }
return mapDict[category] return mapDict[category]
@@ -424,7 +467,6 @@ class ProjectController(ControllerBase):
@route.post("/post_static_dynamic_item/") @route.post("/post_static_dynamic_item/")
@transaction.atomic @transaction.atomic
def post_static_dynamic_item(self, data: StaticDynamicData): def post_static_dynamic_item(self, data: StaticDynamicData):
print(data)
project_obj = self.get_project_by_id(data.id) project_obj = self.get_project_by_id(data.id)
model = self.get_model_from_category(data.category) model = self.get_model_from_category(data.category)
item_qs = model.objects.filter(project=project_obj) item_qs = model.objects.filter(project=project_obj)
@@ -432,3 +474,24 @@ class ProjectController(ControllerBase):
# 如果存在则修改 # 如果存在则修改
item_qs.delete() item_qs.delete()
model.objects.create(project=project_obj, table=data.table, fontnote=data.fontnote) model.objects.create(project=project_obj, table=data.table, fontnote=data.fontnote)
# ~~~环境差异性分析 - 获取~~~
@route.get("/get_env_analysis/")
@transaction.atomic
def get_env_analysis(self, id: int):
project_obj = self.get_project_by_id(id)
qs = EnvAnalysis.objects.filter(project=project_obj)
if qs.exists():
obj = qs.first()
return ChenResponse(status=200, code=25001, data={"table": obj.table, "fontnote": obj.fontnote, "description": obj.description})
return ChenResponse(status=200, code=25002, data=None)
# ~~~环境差异性分析 - 新增和修改~~~
@route.post("/post_env_analysis/")
@transaction.atomic
def post_env_analysis(self, data: EnvAnalysisSchema):
project_obj = self.get_project_by_id(data.id)
qs = EnvAnalysis.objects.filter(project=project_obj)
if qs.exists():
qs.delete()
EnvAnalysis.objects.create(project=project_obj, table=data.table, fontnote=data.fontnote, description=data.description)

View File

@@ -2,9 +2,9 @@ from ninja_extra import api_controller, ControllerBase, route
from ninja_jwt.authentication import JWTAuth from ninja_jwt.authentication import JWTAuth
from ninja_extra.permissions import IsAuthenticated from ninja_extra.permissions import IsAuthenticated
from django.db import transaction from django.db import transaction
from apps.project.models import Round from apps.project.models import Round, InfluenceArea, InfluenceItem
from apps.project.schemas.round import TreeReturnRound, RoundInfoOutSchema, EditSchemaIn, DeleteSchema, \ from apps.project.schemas.round import TreeReturnRound, RoundInfoOutSchema, EditSchemaIn, DeleteSchema, \
CreateRoundOutSchema, CreateRoundInputSchema CreateRoundOutSchema, CreateRoundInputSchema, InfluenceItemOutSchema, InfluenceInputSchema
from typing import List from typing import List
from utils.chen_response import ChenResponse from utils.chen_response import ChenResponse
from apps.project.tools.delete_change_key import round_delete_sub_node_key from apps.project.tools.delete_change_key import round_delete_sub_node_key
@@ -81,3 +81,63 @@ class RoundController(ControllerBase):
return ChenResponse(code=400, status=400, message='标识和其他重复') return ChenResponse(code=400, status=400, message='标识和其他重复')
Round.objects.create(**asert_dict) Round.objects.create(**asert_dict)
return ChenResponse(message="新增轮次成功") return ChenResponse(message="新增轮次成功")
# ~~~影响域分析 - 获取数据和状态~~~
@route.get("/round/get_influence", response=List[InfluenceItemOutSchema], url_name="round-get-influence-items")
@transaction.atomic
def get_influence(self, id: int, round_key: str):
round_qs = Round.objects.filter(project__id=id, key=round_key)
round_obj = round_qs.first()
influence_qs = InfluenceArea.objects.filter(round=round_obj)
if influence_qs.exists():
influence = influence_qs.first()
items_qs = influence.influence_items.all()
if items_qs.exists():
return items_qs
return ChenResponse(status=200, code=25002, data=[])
# ~~~影响域分析是否有值~~~
@route.get("/round/get_status_influence", url_name="round-get-status-influence")
@transaction.atomic
def get_status_influence(self, id: int, round_key: str):
round_qs = Round.objects.filter(project__id=id, key=round_key)
round_obj = round_qs.first()
influence_qs = InfluenceArea.objects.filter(round=round_obj)
if influence_qs.exists():
influence = influence_qs.first()
items_qs = influence.influence_items.all()
if items_qs.exists():
return ChenResponse(status=200, code=25005, data=True)
return ChenResponse(status=200, code=25006, data=False)
# ~~~影响域分析 - 修改或新增~~~
@route.post("/round/create_influence", url_name="round-influence-create")
@transaction.atomic
def post_influence(self, data: InfluenceInputSchema):
print(data)
round_obj = Round.objects.filter(project_id=data.id, key=data.round_key).first()
influence_area_qs = InfluenceArea.objects.filter(round=round_obj)
if influence_area_qs.exists():
influence_area_obj = influence_area_qs.first()
influence_area_obj.influence_items.all().delete()
# 先删除再创建
data_list = []
for item in data.item_list:
new_item = InfluenceItem(influence=influence_area_obj,
change_type=item.change_type,
change_influ=item.change_influ,
change_des=item.change_des,
effect_cases=item.effect_cases)
data_list.append(new_item)
InfluenceItem.objects.bulk_create(data_list)
else:
parent_obj = InfluenceArea.objects.create(round=round_obj)
data_list = []
for item in data.item_list:
new_item = InfluenceItem(influence=parent_obj,
change_type=item.change_type,
change_influ=item.change_influ,
change_des=item.change_des,
effect_cases=item.effect_cases)
data_list.append(new_item)
InfluenceItem.objects.bulk_create(data_list)

View File

@@ -0,0 +1,28 @@
# Generated by Django 6.0.2 on 2026-02-06 10:56
import apps.project.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('project', '0024_projectdynamicdescription_and_more'),
]
operations = [
migrations.CreateModel(
name='EvaluateData',
fields=[
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='evaluate_data', serialize=False, to='project.project', verbose_name='关联项目')),
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
('fontnote', models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注')),
],
options={
'verbose_name': '测评数据',
'verbose_name_plural': '测评数据',
'db_table': 'project_evaluate_data',
},
),
]

View File

@@ -0,0 +1,29 @@
# Generated by Django 6.0.2 on 2026-02-06 13:56
import apps.project.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('project', '0025_evaluatedata'),
]
operations = [
migrations.CreateModel(
name='EnvAnalysis',
fields=[
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='env_analysis', serialize=False, to='project.project', verbose_name='关联项目')),
('table', models.JSONField(default=apps.project.models.default_json_value, help_text='储存表格二维数组', verbose_name='储存表格二维数组')),
('fontnote', models.CharField(default='', help_text='数据的题注说明', max_length=256, null=True, verbose_name='题注')),
('description', models.CharField(default='', max_length=1024, null=True, verbose_name='差异性分析文字')),
],
options={
'verbose_name': '环境差异性分析表',
'verbose_name_plural': '环境差异性分析表',
'db_table': 'project_env_analysis',
},
),
]

View File

@@ -0,0 +1,46 @@
# Generated by Django 6.0.2 on 2026-02-06 16:05
import apps.project.models
import django.db.models.deletion
import tinymce.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('project', '0026_envanalysis'),
]
operations = [
migrations.CreateModel(
name='InfluenceArea',
fields=[
('project', models.OneToOneField(db_constraint=False, help_text='关联项目', on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='influence', serialize=False, to='project.round', verbose_name='关联项目')),
],
options={
'verbose_name': '影响域分析',
'verbose_name_plural': '影响域分析',
'db_table': 'round_influence_area',
},
),
migrations.CreateModel(
name='InfluenceItem',
fields=[
('id', models.BigAutoField(help_text='Id', primary_key=True, serialize=False, verbose_name='Id')),
('remark', models.CharField(blank=True, help_text='描述', max_length=255, null=True, verbose_name='描述')),
('update_datetime', models.DateField(auto_now=True, help_text='修改时间', null=True, verbose_name='修改时间')),
('create_datetime', models.DateField(auto_now_add=True, help_text='创建时间', null=True, verbose_name='创建时间')),
('sort', models.IntegerField(blank=True, default=1, help_text='显示排序', null=True, verbose_name='显示排序')),
('change_type', models.CharField(default='', help_text='更改类型', max_length=256, null=True, verbose_name='更改类型')),
('change_des', tinymce.models.HTMLField(blank=True, null=True, verbose_name='更改内容描述')),
('effect_cases', models.JSONField(default=apps.project.models.create_list, verbose_name='影响的用例key数组')),
('influence', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='influence_items', to='project.influencearea', verbose_name='所属影响域分析')),
],
options={
'verbose_name': '影响域分析 - 行数据',
'verbose_name_plural': '影响域分析 - 行数据',
'db_table': 'influence_item',
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0.2 on 2026-02-07 13:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('project', '0027_influencearea_influenceitem'),
]
operations = [
migrations.RenameField(
model_name='influencearea',
old_name='project',
new_name='round',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 6.0.2 on 2026-02-07 16:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('project', '0028_rename_project_influencearea_round'),
]
operations = [
migrations.AddField(
model_name='influenceitem',
name='change_influ',
field=models.TextField(default='', help_text='影响域分析', max_length=2048, null=True, verbose_name='影响域分析'),
),
]

View File

@@ -542,7 +542,6 @@ class DynamicSoftTable(models.Model):
class DynamicHardwareTable(models.Model): class DynamicHardwareTable(models.Model):
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="dynamic_hardware", on_delete=models.CASCADE, project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="dynamic_hardware", on_delete=models.CASCADE,
verbose_name="关联项目", help_text="关联项目") verbose_name="关联项目", help_text="关联项目")
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value) table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明") fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
@@ -551,6 +550,32 @@ class DynamicHardwareTable(models.Model):
verbose_name = "动态硬件项表" verbose_name = "动态硬件项表"
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
# 一对一项目model动态环境 - 测评数据
class EvaluateData(models.Model):
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False, related_name="evaluate_data", on_delete=models.CASCADE,
verbose_name="关联项目", help_text="关联项目")
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
class Meta:
db_table = 'project_evaluate_data'
verbose_name = "测评数据"
verbose_name_plural = verbose_name
# 一对一项目model环境差异性分析
class EnvAnalysis(models.Model):
project = models.OneToOneField(to="Project", primary_key=True, db_constraint=False,
related_name="env_analysis", on_delete=models.CASCADE,
verbose_name="关联项目", help_text="关联项目")
table = models.JSONField(verbose_name="储存表格二维数组", help_text="储存表格二维数组", default=default_json_value)
fontnote = models.CharField(max_length=256, null=True, default="", verbose_name="题注", help_text="数据的题注说明")
description = models.CharField(max_length=1024, null=True, default="", verbose_name="差异性分析文字")
class Meta:
db_table = 'project_env_analysis'
verbose_name = "环境差异性分析表"
verbose_name_plural = verbose_name
# 结构化排序数据 # 结构化排序数据
class StuctSortData(CoreModel): class StuctSortData(CoreModel):
""" """
@@ -582,11 +607,7 @@ class StuctSortData(CoreModel):
help_text="数据的题注说明" help_text="数据的题注说明"
) )
# 内容字段 - 存储字符串、列表、字典 # 内容字段 - 存储字符串、列表、字典
content = models.JSONField( content = models.JSONField(verbose_name="内容", help_text="存储文本内容或二维表格数据或图片数据", default=default_json_value)
verbose_name="内容",
help_text="存储文本内容或二维表格数据或图片数据",
default=default_json_value
)
class Meta: class Meta:
db_table = 'data_schemas' db_table = 'data_schemas'
@@ -595,3 +616,28 @@ class StuctSortData(CoreModel):
def __str__(self): def __str__(self):
return f"结构排序化数据:({self.pk})" return f"结构排序化数据:({self.pk})"
# 影响域分析 - 隶属:轮次(不能是第一轮次)
class InfluenceArea(models.Model):
round = models.OneToOneField(to="Round", primary_key=True, db_constraint=False,
related_name="influence", on_delete=models.CASCADE,
verbose_name="关联项目", help_text="关联项目")
class Meta:
db_table = 'round_influence_area'
verbose_name = "影响域分析"
verbose_name_plural = verbose_name
class InfluenceItem(CoreModel):
# 外键:影响域分析
influence = models.ForeignKey(InfluenceArea, db_constraint=False, related_name="influence_items", verbose_name="所属影响域分析",
on_delete=models.CASCADE, null=True, blank=True)
change_type = models.CharField(max_length=256, null=True, default="", verbose_name="更改类型", help_text="更改类型")
change_influ = models.TextField(max_length=2048, null=True, default="", verbose_name="影响域分析", help_text="影响域分析")
change_des = HTMLField(blank=True, null=True, verbose_name="更改内容描述")
effect_cases = models.JSONField(default=create_list, verbose_name="影响的用例key数组")
class Meta:
db_table = 'influence_item'
verbose_name = "影响域分析 - 行数据"
verbose_name_plural = verbose_name

View File

@@ -62,3 +62,10 @@ class StaticDynamicData(Schema):
category: str category: str
table: list[list[str]] table: list[list[str]]
fontnote: Optional[str] = "" fontnote: Optional[str] = ""
# ~~~环境差异性分析~~~
class EnvAnalysisSchema(Schema):
id: int
table: list[list[str]]
fontnote: Optional[str] = ""
description: Optional[str] = ""

View File

@@ -1,7 +1,7 @@
from typing import Optional from typing import Optional
from ninja import Schema, ModelSchema from ninja import Schema, ModelSchema
from pydantic import Field from pydantic import Field
from apps.project.models import Round from apps.project.models import Round, InfluenceItem
# 输出树状信息的schema # 输出树状信息的schema
class TreeReturnRound(Schema): class TreeReturnRound(Schema):
@@ -55,3 +55,21 @@ class CreateRoundInputSchema(ModelSchema):
fields_optional = ['best_condition_tem', 'best_condition_voltage', fields_optional = ['best_condition_tem', 'best_condition_voltage',
'low_condition_tem', 'low_condition_voltage', 'typical_condition_tem', 'low_condition_tem', 'low_condition_voltage', 'typical_condition_tem',
'typical_condition_voltage' 'grade'] 'typical_condition_voltage' 'grade']
# influence_item return
class InfluenceItemOutSchema(ModelSchema):
class Meta:
model = InfluenceItem
fields = ['id', 'change_type', 'change_des', 'effect_cases', 'change_influ']
# influence input
class OneItemInputSchema(Schema):
change_type: str
change_des: Optional[str] = ""
effect_cases: Optional[list[str]] = []
change_influ: Optional[str] = ""
class InfluenceInputSchema(Schema):
id: int
round_key: str
item_list: list[OneItemInputSchema]

View File

@@ -292,3 +292,307 @@ FileNotFoundError: [Errno 2] No such file or directory: 'E:\\pycharmProjects\\cd
[WARNING][2026-02-05 18:19:31,559][log.py:249]Unprocessable Content: /api/testmanage/project/soft_summary/ [WARNING][2026-02-05 18:19:31,559][log.py:249]Unprocessable Content: /api/testmanage/project/soft_summary/
[WARNING][2026-02-05 18:20:01,886][operation.py:131]"POST - ProjectController[soft_summary] /api/testmanage/project/soft_summary/" ([{'type': 'missing', 'loc': ('body', 'payload', 'id'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'payload', 'data'), 'msg': 'Field required'}],) [WARNING][2026-02-05 18:20:01,886][operation.py:131]"POST - ProjectController[soft_summary] /api/testmanage/project/soft_summary/" ([{'type': 'missing', 'loc': ('body', 'payload', 'id'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'payload', 'data'), 'msg': 'Field required'}],)
[WARNING][2026-02-05 18:20:01,896][log.py:249]Unprocessable Content: /api/testmanage/project/soft_summary/ [WARNING][2026-02-05 18:20:01,896][log.py:249]Unprocessable Content: /api/testmanage/project/soft_summary/
[WARNING][2026-02-06 09:19:08,011][log.py:249]Unauthorized: /api/system/getInfo
[WARNING][2026-02-06 09:19:08,068][log.py:249]Unauthorized: /api/system/logout
[WARNING][2026-02-06 09:19:17,445][backend.py:91]Caught LDAPError looking up user: SERVER_DOWN({'result': -1, 'desc': "Can't contact LDAP server", 'ctrls': []})
[WARNING][2026-02-06 09:32:51,032][log.py:249]Not Found: /api/testmanage/project/dynamic_des/
[WARNING][2026-02-06 09:32:57,510][log.py:249]Not Found: /api/testmanage/project/dynamic_des/
[WARNING][2026-02-06 09:33:05,303][log.py:249]Method Not Allowed: /api/testmanage/project/dynamic_des/
[WARNING][2026-02-06 10:01:55,025][operation.py:131]"POST - ProjectController[dynamic_description] /api/testmanage/project/dynamic_description/" (400, '添加的数据未在系统内,请联系管理员')
[WARNING][2026-02-06 10:01:55,034][log.py:249]Bad Request: /api/testmanage/project/dynamic_description/
[WARNING][2026-02-06 10:02:03,145][operation.py:131]"POST - ProjectController[dynamic_description] /api/testmanage/project/dynamic_description/" (400, '添加的数据未在系统内,请联系管理员')
[WARNING][2026-02-06 10:02:03,155][log.py:249]Bad Request: /api/testmanage/project/dynamic_description/
[WARNING][2026-02-06 10:02:43,130][operation.py:131]"POST - ProjectController[dynamic_description] /api/testmanage/project/dynamic_description/" (400, '添加的数据未在系统内,请联系管理员')
[WARNING][2026-02-06 10:02:43,141][log.py:249]Bad Request: /api/testmanage/project/dynamic_description/
[WARNING][2026-02-06 10:39:53,890][operation.py:131]"GET - GenerateControllerDG[create_dynamic_env] /api/generate/create/dynamic_env" ("expected token 'end of statement block', got 'table'",)
[ERROR][2026-02-06 10:39:53,891][errors.py:131]expected token 'end of statement block', got 'table'
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\createDocument\controllers\dg.py", line 578, in create_dynamic_env
res = self.uniform_res_from_mul_data_schemas(id, '动态测试环境说明_2.docx',
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'动态测试环境说明.docx', ProjectDynamicDescription)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\apps\createDocument\controllers\dg.py", line 351, in uniform_res_from_mul_data_schemas
doc.render(context)
~~~~~~~~~~^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 489, in render
xml_src = self.build_xml(context, jinja_env)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 436, in build_xml
xml = self.render_xml_part(xml, self.docx._part, context, jinja_env)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 322, in render_xml_part
raise exc
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 312, in render_xml_part
template = Template(src_xml)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 1214, in __new__
return env.from_string(source, template_class=cls)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 1111, in from_string
return cls.from_code(self, self.compile(source), gs, None)
~~~~~~~~~~~~^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 771, in compile
self.handle_exception(source=source_hint)
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 942, in handle_exception
raise rewrite_traceback_stack(source=source)
File "<unknown>", line 4, in template
jinja2.exceptions.TemplateSyntaxError: expected token 'end of statement block', got 'table'
[ERROR][2026-02-06 10:39:53,923][log.py:249]Internal Server Error: /api/generate/create/dynamic_env
[WARNING][2026-02-06 10:40:33,124][operation.py:131]"GET - GenerateControllerDG[create_dynamic_env] /api/generate/create/dynamic_env" ('unexpected char "\'" at 5156',)
[ERROR][2026-02-06 10:40:33,127][errors.py:131]unexpected char "'" at 5156
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\createDocument\controllers\dg.py", line 578, in create_dynamic_env
res = self.uniform_res_from_mul_data_schemas(id, '动态测试环境说明_2.docx',
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
'动态测试环境说明.docx', ProjectDynamicDescription)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\apps\createDocument\controllers\dg.py", line 351, in uniform_res_from_mul_data_schemas
doc.render(context)
~~~~~~~~~~^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 489, in render
xml_src = self.build_xml(context, jinja_env)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 436, in build_xml
xml = self.render_xml_part(xml, self.docx._part, context, jinja_env)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 322, in render_xml_part
raise exc
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\docxtpl\template.py", line 312, in render_xml_part
template = Template(src_xml)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 1214, in __new__
return env.from_string(source, template_class=cls)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 1111, in from_string
return cls.from_code(self, self.compile(source), gs, None)
~~~~~~~~~~~~^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 771, in compile
self.handle_exception(source=source_hint)
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\jinja2\environment.py", line 942, in handle_exception
raise rewrite_traceback_stack(source=source)
File "<unknown>", line 4, in template
jinja2.exceptions.TemplateSyntaxError: unexpected char "'" at 5156
[ERROR][2026-02-06 10:40:33,143][log.py:249]Internal Server Error: /api/generate/create/dynamic_env
[WARNING][2026-02-06 10:52:55,927][operation.py:131]"GET - ProjectController[get_static_dynamic_items] /api/testmanage/project/get_static_dynamic_items/" ('测评数据',)
[ERROR][2026-02-06 10:52:55,927][errors.py:131]'测评数据'
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\project.py", line 454, in get_static_dynamic_items
item_qs = self.get_model_from_category(category).objects.filter(project=project_obj)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\project.py", line 448, in get_model_from_category
return mapDict[category]
~~~~~~~^^^^^^^^^^
KeyError: '测评数据'
[ERROR][2026-02-06 10:52:55,929][log.py:249]Internal Server Error: /api/testmanage/project/get_static_dynamic_items/
[WARNING][2026-02-06 13:56:16,158][operation.py:131]"GET - ProjectController[get_env_analysis] /api/testmanage/project/get_env_analysis/" (1146, "Table 'chengdu_test_plant_v1.project_env_analysis' doesn't exist")
[ERROR][2026-02-06 13:56:16,158][errors.py:131](1146, "Table 'chengdu_test_plant_v1.project_env_analysis' doesn't exist")
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\mysql\base.py", line 78, in execute
return self.cursor.execute(query, args)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\cursors.py", line 179, in execute
res = self._query(mogrified_query)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\cursors.py", line 330, in _query
db.query(q)
~~~~~~~~^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\connections.py", line 280, in query
_mysql.connection.query(self, query)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
MySQLdb.ProgrammingError: (1146, "Table 'chengdu_test_plant_v1.project_env_analysis' doesn't exist")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "D:\programs\uv\python\cpython-3.13.11-windows-x86_64-none\Lib\contextlib.py", line 85, in inner
return func(*args, **kwds)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\project.py", line 485, in get_env_analysis
if qs.exists():
~~~~~~~~~^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\query.py", line 1337, in exists
return self.query.has_results(using=self.db)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\sql\query.py", line 686, in has_results
return compiler.has_results()
~~~~~~~~~~~~~~~~~~~~^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 1592, in has_results
return bool(self.execute_sql(SINGLE))
~~~~~~~~~~~~~~~~^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\sql\compiler.py", line 1624, in execute_sql
cursor.execute(sql, params)
~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 122, in execute
return super().execute(sql, params)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 79, in execute
return self._execute_with_wrappers(
~~~~~~~~~~~~~~~~~~~~~~~~~~~^
sql, params, many=False, executor=self._execute
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 92, in _execute_with_wrappers
return executor(sql, params, many, context)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 100, in _execute
with self.db.wrap_database_errors:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\utils.py", line 94, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\backends\mysql\base.py", line 78, in execute
return self.cursor.execute(query, args)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\cursors.py", line 179, in execute
res = self._query(mogrified_query)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\cursors.py", line 330, in _query
db.query(q)
~~~~~~~~^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\MySQLdb\connections.py", line 280, in query
_mysql.connection.query(self, query)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
django.db.utils.ProgrammingError: (1146, "Table 'chengdu_test_plant_v1.project_env_analysis' doesn't exist")
[ERROR][2026-02-06 13:56:16,179][log.py:249]Internal Server Error: /api/testmanage/project/get_env_analysis/
[WARNING][2026-02-06 14:05:16,384][operation.py:131]"GET - ProjectController[get_env_analysis] /api/testmanage/project/get_env_analysis/" ([{'type': 'missing', 'loc': ('query', 'id'), 'msg': 'Field required'}],)
[WARNING][2026-02-06 14:05:16,392][log.py:249]Unprocessable Content: /api/testmanage/project/get_env_analysis/
[WARNING][2026-02-06 14:05:45,945][operation.py:131]"GET - ProjectController[get_env_analysis] /api/testmanage/project/get_env_analysis/" ([{'type': 'missing', 'loc': ('query', 'id'), 'msg': 'Field required'}],)
[WARNING][2026-02-06 14:05:45,955][log.py:249]Unprocessable Content: /api/testmanage/project/get_env_analysis/
[WARNING][2026-02-06 14:07:06,442][log.py:249]Method Not Allowed: /api/testmanage/project/post_env_analysis/
[WARNING][2026-02-06 17:54:26,693][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-06 17:55:19,893][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-06 17:55:54,935][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-06 17:55:57,677][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-06 17:56:36,885][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-06 17:56:39,905][log.py:249]Not Found: /api/project/case/getRelatedCase/
[WARNING][2026-02-07 12:50:41,012][log.py:249]Unauthorized: /api/system/getInfo
[WARNING][2026-02-07 12:50:41,075][log.py:249]Unauthorized: /api/system/logout
[WARNING][2026-02-07 12:50:44,998][backend.py:91]Caught LDAPError looking up user: SERVER_DOWN({'result': -1, 'desc': "Can't contact LDAP server", 'ctrls': []})
[WARNING][2026-02-07 13:37:04,069][operation.py:131]"GET - RoundController[get_influence] /api/project/round/get_influence" ("'QuerySet' object has no attribute 'influence'",)
[ERROR][2026-02-07 13:37:04,069][errors.py:131]'QuerySet' object has no attribute 'influence'
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\round.py", line 89, in get_influence
influence_qs = round_qs.influence.all()
^^^^^^^^^^^^^^^^^^
AttributeError: 'QuerySet' object has no attribute 'influence'
[ERROR][2026-02-07 13:37:04,071][log.py:249]Internal Server Error: /api/project/round/get_influence
[WARNING][2026-02-07 13:37:39,505][operation.py:131]"GET - RoundController[get_influence] /api/project/round/get_influence" ('Round has no influence.',)
[ERROR][2026-02-07 13:37:39,505][errors.py:131]Round has no influence.
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\round.py", line 89, in get_influence
influence_qs = round_qs.first().influence.all()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 520, in __get__
raise self.RelatedObjectDoesNotExist(
...<2 lines>...
)
apps.project.models.Round.influence.RelatedObjectDoesNotExist: Round has no influence.
[ERROR][2026-02-07 13:37:39,508][log.py:249]Internal Server Error: /api/project/round/get_influence
[WARNING][2026-02-07 13:38:15,267][operation.py:131]"GET - RoundController[get_influence] /api/project/round/get_influence" ('Round has no influence.',)
[ERROR][2026-02-07 13:38:15,267][errors.py:131]Round has no influence.
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\round.py", line 89, in get_influence
influence_qs = round_qs.first().influence.all()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 520, in __get__
raise self.RelatedObjectDoesNotExist(
...<2 lines>...
)
apps.project.models.Round.influence.RelatedObjectDoesNotExist: Round has no influence.
[ERROR][2026-02-07 13:38:15,269][log.py:249]Internal Server Error: /api/project/round/get_influence
[WARNING][2026-02-07 13:39:10,565][operation.py:131]"GET - RoundController[get_influence] /api/project/round/get_influence" ('Round has no influence.',)
[ERROR][2026-02-07 13:39:10,565][errors.py:131]Round has no influence.
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\round.py", line 89, in get_influence
influence_qs = round_qs.first().influence.all()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 520, in __get__
raise self.RelatedObjectDoesNotExist(
...<2 lines>...
)
apps.project.models.Round.influence.RelatedObjectDoesNotExist: Round has no influence.
[ERROR][2026-02-07 13:39:10,568][log.py:249]Internal Server Error: /api/project/round/get_influence
[WARNING][2026-02-07 14:25:27,963][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'missing', 'loc': ('body', 'data', 'id'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'data', 'round_key'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'data', 'item_list'), 'msg': 'Field required'}],)
[WARNING][2026-02-07 14:25:27,972][log.py:249]Unprocessable Content: /api/project/round/create_influence
[WARNING][2026-02-07 14:25:49,141][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'missing', 'loc': ('body', 'data', 'id'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'data', 'round_key'), 'msg': 'Field required'}, {'type': 'missing', 'loc': ('body', 'data', 'item_list'), 'msg': 'Field required'}],)
[WARNING][2026-02-07 14:25:49,154][log.py:249]Unprocessable Content: /api/project/round/create_influence
[WARNING][2026-02-07 15:01:21,250][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ('Cannot assign "<QuerySet [<InfluenceArea: InfluenceArea object (195)>]>": "InfluenceItem.influence" must be a "InfluenceArea" instance.',)
[ERROR][2026-02-07 15:01:21,250][errors.py:131]Cannot assign "<QuerySet [<InfluenceArea: InfluenceArea object (195)>]>": "InfluenceItem.influence" must be a "InfluenceArea" instance.
Traceback (most recent call last):
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\operation.py", line 212, in run
result = self.view_func(request, **ctx.kwargs["view_func_kwargs"])
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\ninja_extra\controllers\route\route_functions.py", line 108, in as_view
result = self.route.view_func(
ctx.controller_instance, *args, **ctx.view_func_kwargs
)
File "D:\programs\uv\python\cpython-3.13.11-windows-x86_64-none\Lib\contextlib.py", line 85, in inner
return func(*args, **kwds)
File "E:\pycharmProjects\cdtestplant_v1\apps\project\controllers\round.py", line 110, in post_influence
new_item = InfluenceItem(influence=influence_area_qs,
change_type=item.change_type,
change_des=item.change_des,
effect_cases=item.effect_cases)
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\base.py", line 566, in __init__
_setattr(self, field.name, rel_obj)
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\pycharmProjects\cdtestplant_v1\.venv\Lib\site-packages\django\db\models\fields\related_descriptors.py", line 287, in __set__
raise ValueError(
...<7 lines>...
)
ValueError: Cannot assign "<QuerySet [<InfluenceArea: InfluenceArea object (195)>]>": "InfluenceItem.influence" must be a "InfluenceArea" instance.
[ERROR][2026-02-07 15:01:21,258][log.py:249]Internal Server Error: /api/project/round/create_influence
[WARNING][2026-02-07 15:19:38,249][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 0), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 1), 'msg': 'Input should be a valid string'}],)
[WARNING][2026-02-07 15:19:38,257][log.py:249]Unprocessable Content: /api/project/round/create_influence
[WARNING][2026-02-07 15:19:43,626][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 0), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 1), 'msg': 'Input should be a valid string'}],)
[WARNING][2026-02-07 15:19:43,635][log.py:249]Unprocessable Content: /api/project/round/create_influence
[WARNING][2026-02-07 15:20:44,492][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 0), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 1), 'msg': 'Input should be a valid string'}],)
[WARNING][2026-02-07 15:20:44,503][log.py:249]Unprocessable Content: /api/project/round/create_influence
[WARNING][2026-02-07 15:21:39,506][operation.py:131]"POST - RoundController[post_influence] /api/project/round/create_influence" ([{'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 0), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 1), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 2), 'msg': 'Input should be a valid string'}, {'type': 'string_type', 'loc': ('body', 'data', 'item_list', 0, 'effect_cases', 3), 'msg': 'Input should be a valid string'}],)
[WARNING][2026-02-07 15:21:39,515][log.py:249]Unprocessable Content: /api/project/round/create_influence

Binary file not shown.

Binary file not shown.