initial commit
This commit is contained in:
0
apps/project/__init__.py
Normal file
0
apps/project/__init__.py
Normal file
BIN
apps/project/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/project/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/project/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/admin.cpython-313.pyc
Normal file
BIN
apps/project/__pycache__/admin.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/admin.cpython-38.pyc
Normal file
BIN
apps/project/__pycache__/admin.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/apps.cpython-313.pyc
Normal file
BIN
apps/project/__pycache__/apps.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/apps.cpython-38.pyc
Normal file
BIN
apps/project/__pycache__/apps.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/models.cpython-313.pyc
Normal file
BIN
apps/project/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/models.cpython-38.pyc
Normal file
BIN
apps/project/__pycache__/models.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/signals.cpython-313.pyc
Normal file
BIN
apps/project/__pycache__/signals.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/__pycache__/signals.cpython-38.pyc
Normal file
BIN
apps/project/__pycache__/signals.cpython-38.pyc
Normal file
Binary file not shown.
3
apps/project/admin.py
Normal file
3
apps/project/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
11
apps/project/apps.py
Normal file
11
apps/project/apps.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class ProjectConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.project'
|
||||
|
||||
def ready(self) -> None:
|
||||
from apps.project.signals import set_request_locals, clear_request_locals
|
||||
from django.core.signals import request_finished, request_started
|
||||
request_started.connect(set_request_locals)
|
||||
request_finished.connect(clear_request_locals)
|
||||
13
apps/project/controllers/__init__.py
Normal file
13
apps/project/controllers/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# 导入所有控制器
|
||||
from apps.project.controllers.project import ProjectController
|
||||
from apps.project.controllers.round import RoundController
|
||||
from apps.project.controllers.dut import DutController
|
||||
from apps.project.controllers.design import DesignController
|
||||
from apps.project.controllers.testDemand import TestDemandController
|
||||
from apps.project.controllers.case import CaseController
|
||||
from apps.project.controllers.problem import ProblemController
|
||||
from apps.project.controllers.treeOperation import TreeController
|
||||
|
||||
# 将导入的控制器以列表方式放入下面数组
|
||||
__all__ = ['ProjectController', 'RoundController', 'DutController', 'DesignController', 'TestDemandController',
|
||||
'CaseController', 'ProblemController', 'TreeController']
|
||||
BIN
apps/project/controllers/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/case.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/case.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/case.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/case.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/design.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/design.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/design.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/design.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/dut.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/dut.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/dut.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/dut.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/problem.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/problem.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/problem.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/problem.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/project.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/project.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/project.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/project.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/round.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/round.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/round.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/round.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/testDemand.cpython-313.pyc
Normal file
BIN
apps/project/controllers/__pycache__/testDemand.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/controllers/__pycache__/testDemand.cpython-38.pyc
Normal file
BIN
apps/project/controllers/__pycache__/testDemand.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
245
apps/project/controllers/case.py
Normal file
245
apps/project/controllers/case.py
Normal file
@@ -0,0 +1,245 @@
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja import Query
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja.pagination import paginate
|
||||
from ninja.errors import HttpError
|
||||
from utils.chen_pagination import MyPagination
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import multi_delete_case
|
||||
from apps.project.models import Design, Dut, Round, TestDemand, Case, CaseStep, Project, Problem
|
||||
from apps.project.schemas.case import DeleteSchema, CaseModelOutSchema, CaseFilterSchema, CaseTreeReturnSchema, \
|
||||
CaseTreeInputSchema, CaseCreateOutSchema, CaseCreateInputSchema, DemandNodeSchema
|
||||
from utils.util import get_testType
|
||||
from utils.codes import HTTP_INDEX_ERROR, HTTP_EXISTS_CASES
|
||||
from apps.project.tools.copyCase import case_move_to_test, case_copy_to_test, case_to_case_copy_or_move
|
||||
from utils.smallTools.interfaceTools import conditionNoneToBlank
|
||||
# 导入case的schema
|
||||
from apps.project.schemas.case import CaseModelOutSchemaWithoutProblem
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['测试用例接口'])
|
||||
class CaseController(ControllerBase):
|
||||
@route.get("/getCaseList", response=List[CaseModelOutSchema], exclude_none=True,
|
||||
url_name="case-list")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_case_list(self, data: CaseFilterSchema = Query(...)):
|
||||
"""有id则查询一个case,无id则查询多个"""
|
||||
data_dict = data.dict()
|
||||
case_id = data_dict.pop('id')
|
||||
if case_id:
|
||||
# 当传入了id,则查询单个
|
||||
qs = Case.objects.filter(id=case_id) # type:ignore
|
||||
else:
|
||||
conditionNoneToBlank(data)
|
||||
test_key = "".join([data.round_id, '-', data.dut_id, '-', data.design_id, '-', data.test_id])
|
||||
qs = Case.objects.filter(project__id=data.project_id, test__key=test_key, # type:ignore
|
||||
ident__icontains=data.ident,
|
||||
name__icontains=data.name,
|
||||
designPerson__icontains=data.designPerson,
|
||||
testPerson__icontains=data.testPerson,
|
||||
monitorPerson__icontains=data.monitorPerson,
|
||||
summarize__icontains=data.summarize,
|
||||
).order_by("key")
|
||||
# 由于有嵌套query_set存在,把每个用例的schema加上一个字段
|
||||
query_list = []
|
||||
for query_single in qs:
|
||||
setattr(query_single, "testStep", query_single.step.all().values())
|
||||
# 增加一个字段,测试类型例如:FT
|
||||
setattr(query_single, 'testType', get_testType(query_single.test.testType, dict_code='testType'))
|
||||
# 如果有问题单字段则添加上
|
||||
related_problem: Problem = query_single.caseField.first()
|
||||
if query_single.caseField.all():
|
||||
setattr(query_single, 'problem', related_problem)
|
||||
query_list.append(query_single)
|
||||
return query_list
|
||||
|
||||
@route.get("/getCaseOne", response=CaseModelOutSchemaWithoutProblem, url_name='case-one')
|
||||
@transaction.atomic
|
||||
def get_case_one(self, key: str, projectId: int):
|
||||
"""用于在用例树状页面,获取promblem信息,这里根据key获取信息"""
|
||||
project_obj = get_object_or_404(Project, id=projectId)
|
||||
case = project_obj.pcField.filter(key=key).first()
|
||||
if case:
|
||||
setattr(case, "testStep", case.step.all().values())
|
||||
setattr(case, 'testType', get_testType(case.test.testType, dict_code='testType'))
|
||||
return case
|
||||
raise HttpError(500, "您获取的数据不存在")
|
||||
|
||||
# 处理树状数据
|
||||
@route.get("/getCaseInfo", response=List[CaseTreeReturnSchema], url_name="case-info")
|
||||
@transaction.atomic
|
||||
def get_case_tree(self, payload: CaseTreeInputSchema = Query(...)):
|
||||
qs = Case.objects.filter(project__id=payload.project_id, test__key=payload.key) # type:ignore
|
||||
for q in qs:
|
||||
# 遍历每个用例节点,查看是否有关联问题单
|
||||
if q.caseField.count() > 0:
|
||||
q.isRelatedProblem = True
|
||||
# 遍历用例的step查看是否有未通过
|
||||
q.isNotPassed = False
|
||||
for step in q.step.all():
|
||||
if step.passed == '2':
|
||||
q.isNotPassed = True
|
||||
return qs
|
||||
|
||||
# 添加测试用例
|
||||
@route.post("/case/save", response=CaseCreateOutSchema, url_name="case-create")
|
||||
@transaction.atomic
|
||||
def create_case(self, payload: CaseCreateInputSchema):
|
||||
asert_dict = payload.dict(exclude_none=True)
|
||||
# 构造design_key
|
||||
test_whole_key = "".join(
|
||||
[payload.round_key, "-", payload.dut_key, '-', payload.design_key, '-', payload.test_key])
|
||||
# 查询当前key应该为多少
|
||||
case_count = Case.objects.filter(project__id=payload.project_id, # type:ignore
|
||||
test__key=test_whole_key).count()
|
||||
key_string = ''.join([test_whole_key, "-", str(case_count)])
|
||||
# 查询当前各个前面节点的instance
|
||||
round_instance = Round.objects.get(project__id=payload.project_id, key=payload.round_key)
|
||||
dut_instance = Dut.objects.get(project__id=payload.project_id, # type:ignore
|
||||
key="".join([payload.round_key, "-", payload.dut_key]))
|
||||
design_instance = Design.objects.get(project__id=payload.project_id, key="".join( # type:ignore
|
||||
[payload.round_key, "-", payload.dut_key, '-', payload.design_key]))
|
||||
test_instance = TestDemand.objects.get(project__id=payload.project_id, key="".join( # type:ignore
|
||||
[payload.round_key, "-", payload.dut_key, '-', payload.design_key, '-', payload.test_key]))
|
||||
# 直接把测试项的标识给前端处理显示
|
||||
asert_dict['ident'] = test_instance.ident
|
||||
# ~~~~~~~~~end~~~~~~~~~
|
||||
asert_dict.update({'key': key_string, 'round': round_instance, 'dut': dut_instance, 'design': design_instance,
|
||||
"test": test_instance, 'title': payload.name})
|
||||
asert_dict.pop("round_key")
|
||||
asert_dict.pop("dut_key")
|
||||
asert_dict.pop("design_key")
|
||||
asert_dict.pop("test_key")
|
||||
asert_dict.pop("testStep")
|
||||
qs = Case.objects.create(**asert_dict) # type:ignore
|
||||
# 对testStep单独处理
|
||||
data_list = []
|
||||
for item in payload.dict()["testStep"]:
|
||||
if not isinstance(item, dict):
|
||||
item = item.dict()
|
||||
item["case"] = qs
|
||||
data_list.append(CaseStep(**item))
|
||||
CaseStep.objects.bulk_create(data_list) # type:ignore
|
||||
return qs
|
||||
|
||||
# 更新测试用例
|
||||
@route.put("/case/update/{id}", response=CaseCreateOutSchema, url_name="case-update")
|
||||
@transaction.atomic
|
||||
def update_case(self, id: int, payload: CaseCreateInputSchema):
|
||||
# 查到当前
|
||||
case_qs = Case.objects.get(id=id) # type:ignore
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'project_id' or attr == 'round_key' or attr == 'dut_key' or attr == 'design_key' or attr == 'test_key':
|
||||
continue
|
||||
if attr == 'name':
|
||||
setattr(case_qs, "title", value)
|
||||
# testStep处理
|
||||
if attr == 'testStep':
|
||||
content_list = case_qs.step.all()
|
||||
for content_single in content_list:
|
||||
content_single.delete()
|
||||
data_list = []
|
||||
for item in value:
|
||||
if item['operation'] or item['expect'] or item['result'] or item['passed'] or item['status']:
|
||||
item["case"] = case_qs
|
||||
data_list.append(CaseStep(**item))
|
||||
CaseStep.objects.bulk_create(data_list) # type:ignore
|
||||
|
||||
setattr(case_qs, attr, value)
|
||||
# 处理标识-统一设置为YL
|
||||
case_qs.ident = case_qs.test.ident
|
||||
# ~~~~~~~~~end~~~~~~~~~
|
||||
case_qs.save()
|
||||
return case_qs
|
||||
|
||||
# 删除测试用例
|
||||
@route.delete("/case/delete", url_name="case-delete")
|
||||
@transaction.atomic
|
||||
def delete_case(self, data: DeleteSchema):
|
||||
# 根据其中一个id查询出dut_id,注意这里解决前端框架问题:删除后还报错选择的行id
|
||||
try:
|
||||
case_single = Case.objects.filter(id=data.ids[0])[0] # type:ignore
|
||||
except IndexError:
|
||||
return ChenResponse(status=500, code=HTTP_INDEX_ERROR, message='您未选择需要删除的内容')
|
||||
test_id = case_single.test.id
|
||||
test_key = case_single.test.key
|
||||
multi_delete_case(data.ids, Case)
|
||||
index = 0
|
||||
case_all_qs = Case.objects.filter(test__id=test_id).order_by('id') # type:ignore
|
||||
for single_qs in case_all_qs:
|
||||
case_key = "".join([test_key, '-', str(index)])
|
||||
single_qs.key = case_key
|
||||
index = index + 1
|
||||
single_qs.save()
|
||||
return ChenResponse(message="测试用例删除成功!")
|
||||
|
||||
# 右键测试项,根据测试子项生成用例
|
||||
@route.post("/case/create_by_demand", url_name='case-create-by-demand')
|
||||
def create_case_by_demand(self, demand_node: DemandNodeSchema):
|
||||
project_qs = get_object_or_404(Project, id=demand_node.project_id)
|
||||
if demand_node.key and demand_node.key != '':
|
||||
demand = get_object_or_404(TestDemand, key=demand_node.key, project=project_qs)
|
||||
# 先查询当前测试项下面有无case
|
||||
case_exists = demand.tcField.exists()
|
||||
if case_exists:
|
||||
return ChenResponse(status=500, code=HTTP_EXISTS_CASES, message='测试项下面有用例,请删除后生成')
|
||||
# 查询所有测试子项
|
||||
sub_items = demand.testQField.all()
|
||||
# 每一个子项都创建一个用例,先声明一个列表,后面可以bulk_create
|
||||
index = 0
|
||||
for sub in sub_items:
|
||||
user_name = self.context.request.user.name # type:ignore
|
||||
case_dict = {
|
||||
'ident': demand.ident,
|
||||
'name': sub.subName,
|
||||
'initialization': '软件正常启动,正常运行',
|
||||
'premise': '软件正常启动,外部接口运行正常',
|
||||
'summarize': demand.testDesciption,
|
||||
'designPerson': user_name,
|
||||
'testPerson': user_name,
|
||||
'monitorPerson': user_name,
|
||||
'project': project_qs,
|
||||
'round': demand.round,
|
||||
'dut': demand.dut,
|
||||
'design': demand.design,
|
||||
'test': demand,
|
||||
'title': sub.subName,
|
||||
'key': ''.join([demand_node.key, '-', str(index)]),
|
||||
'level': '4',
|
||||
}
|
||||
case_model = Case.objects.create(**case_dict) # type:ignore
|
||||
# 创建用例步骤
|
||||
for demand_step_obj in sub.testStepField.all():
|
||||
operation = demand_step_obj.operation
|
||||
case_step_dict = {
|
||||
'operation': "".join([operation if operation is not None else ""]),
|
||||
'expect': demand_step_obj.expect,
|
||||
'result': '', # 暂时为空
|
||||
'case': case_model, # 指定父级Case模型
|
||||
}
|
||||
CaseStep.objects.create(**case_step_dict) # type:ignore
|
||||
index += 1
|
||||
# 这里返回一个demand的key用于前端刷新树状图
|
||||
return ChenResponse(data={'key': demand_node.key}, status=200, code=200, message='测试项自动生成用例成功')
|
||||
|
||||
# 测试用例复制/移动到测试项上
|
||||
@route.get("/case/copy_or_move_to_demand", url_name='case-copy-move-demand')
|
||||
@transaction.atomic
|
||||
def copy_move_case_to_demand(self, project_id: int, case_key: str, demand_key: str, move: bool):
|
||||
if move: # 移动
|
||||
old_key, new_key = case_move_to_test(project_id, case_key, demand_key)
|
||||
else: # 复制
|
||||
old_key, new_key = case_copy_to_test(project_id, case_key, demand_key)
|
||||
# 返回刷新树状信息-需要刷新2个,原来的case_key和现在的case_key
|
||||
return ChenResponse(data={'oldCaseKey': {'key': old_key}, 'newCaseKey': {'key': new_key}})
|
||||
|
||||
# 测试用例复制/移动到用例
|
||||
@route.get("/case/copy_or_move_by_case", url_name='case-copy-move-case')
|
||||
@transaction.atomic
|
||||
def copy_move_case_by_case(self, project_id: int, drag_key: str, drop_key: str, move: bool, position: int):
|
||||
case_to_case_copy_or_move(project_id, drag_key, drop_key, move, position)
|
||||
return ChenResponse(data={'old': {'key': drag_key}, 'new': {'key': drop_key}})
|
||||
158
apps/project/controllers/design.py
Normal file
158
apps/project/controllers/design.py
Normal file
@@ -0,0 +1,158 @@
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja import Query
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja.pagination import paginate
|
||||
from ninja.errors import HttpError
|
||||
from utils.chen_pagination import MyPagination
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import multi_delete_design
|
||||
from utils.codes import HTTP_INDEX_ERROR
|
||||
from apps.project.models import Design, Dut, Round, Project
|
||||
from apps.project.schemas.design import DeleteSchema, DesignFilterSchema, DesignModelOutSchema, DesignTreeReturnSchema, \
|
||||
DesignTreeInputSchema, DesignCreateOutSchema, DesignCreateInputSchema, MultiDesignCreateInputSchema
|
||||
from apps.project.tools.delete_change_key import design_delete_sub_node_key
|
||||
from utils.smallTools.interfaceTools import conditionNoneToBlank
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['设计需求数据'])
|
||||
class DesignController(ControllerBase):
|
||||
@route.get("/getDesignDemandList", response=List[DesignModelOutSchema], exclude_none=True, url_name="design-list")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_design_list(self, datafilter: DesignFilterSchema = Query(...)):
|
||||
conditionNoneToBlank(datafilter)
|
||||
dut_key = "".join([datafilter.round_id, '-', datafilter.dut_id])
|
||||
qs = Design.objects.filter(project__id=datafilter.project_id, dut__key=dut_key,
|
||||
ident__icontains=datafilter.ident,
|
||||
name__icontains=datafilter.name,
|
||||
demandType__contains=datafilter.demandType,
|
||||
chapter__icontains=datafilter.chapter).order_by('id')
|
||||
return qs
|
||||
|
||||
@route.get("/getDesignOne", response=DesignModelOutSchema, url_name='design-one')
|
||||
def get_dut(self, project_id: int, key: str):
|
||||
design_qs = Design.objects.filter(project_id=project_id, key=key).first()
|
||||
if design_qs:
|
||||
return design_qs
|
||||
raise HttpError(500, "未找到相应的数据")
|
||||
|
||||
# 处理树状数据
|
||||
@route.get("/getDesignDemandInfo", response=List[DesignTreeReturnSchema], url_name="design-info")
|
||||
def get_design_tree(self, payload: DesignTreeInputSchema = Query(...)):
|
||||
qs = Design.objects.filter(project__id=payload.project_id, dut__key=payload.key).order_by('id')
|
||||
return qs
|
||||
|
||||
# 添加设计需求
|
||||
@route.post("/designDemand/save", response=DesignCreateOutSchema, url_name="design-create")
|
||||
@transaction.atomic
|
||||
def create_design(self, payload: DesignCreateInputSchema):
|
||||
asert_dict = payload.dict(exclude_none=True)
|
||||
# 如果识别description为None变为空字符串
|
||||
description = asert_dict.get('description')
|
||||
# 构造dut_key
|
||||
dut_key = "".join([payload.round_key, "-", payload.dut_key])
|
||||
# 判重标识-不需要再查询round以后的
|
||||
if Design.objects.filter(project__id=payload.project_id, round__key=payload.round_key, dut__key=dut_key,
|
||||
ident=payload.ident).exists() and asert_dict['ident'] != "":
|
||||
return ChenResponse(code=400, status=400, message='研制需求的标识重复,请检查')
|
||||
# 查询当前key应该为多少
|
||||
design_count = Design.objects.filter(project__id=payload.project_id, dut__key=dut_key).count()
|
||||
key_string = ''.join([dut_key, "-", str(design_count)])
|
||||
# 查询当前的round_id
|
||||
round_instance = Round.objects.get(project__id=payload.project_id, key=payload.round_key)
|
||||
dut_instance = Dut.objects.get(project__id=payload.project_id, key=dut_key)
|
||||
asert_dict.update({'key': key_string, 'round': round_instance, 'dut': dut_instance, 'title': payload.name})
|
||||
asert_dict.pop("round_key")
|
||||
asert_dict.pop("dut_key")
|
||||
qs = Design.objects.create(**asert_dict)
|
||||
return qs
|
||||
|
||||
# 批量增加设计需求,对应前端批量增加页面modal
|
||||
@route.post('/designDemand/multi_save', url_name='design-multi-create')
|
||||
@transaction.atomic
|
||||
def multi_create_design(self, payload: MultiDesignCreateInputSchema):
|
||||
project_obj = get_object_or_404(Project, id=payload.project_id)
|
||||
dut_obj = project_obj.pdField.filter(key=payload.dut_key).first()
|
||||
round_obj = dut_obj.round
|
||||
# 当前dut下的design个数
|
||||
design_count = Design.objects.filter(project=project_obj, dut=dut_obj).count()
|
||||
key_index = design_count
|
||||
# 这里根据payload.data批量增加
|
||||
bulk_list = []
|
||||
for desgin_obj in payload.data:
|
||||
design_one = Design(**desgin_obj.model_dump())
|
||||
design_one.title = design_one.name
|
||||
# 计算出当前key应该为多少
|
||||
design_one.key = ''.join([dut_obj.key, "-", str(key_index)])
|
||||
key_index += 1
|
||||
design_one.level = '2'
|
||||
design_one.project = project_obj
|
||||
design_one.round = round_obj
|
||||
design_one.dut = dut_obj
|
||||
bulk_list.append(design_one)
|
||||
Design.objects.bulk_create(bulk_list)
|
||||
# 为了前端更新,需要返回一个dut_key
|
||||
return ChenResponse(status=200, code=200, data={'key': dut_obj.key + '-1'})
|
||||
|
||||
# 更新设计需求
|
||||
@route.put("/editDesignDemand/{id}", response=DesignCreateOutSchema, url_name="design-update")
|
||||
@transaction.atomic
|
||||
def update_design(self, id: int, payload: DesignCreateInputSchema):
|
||||
design_search = Design.objects.filter(project__id=payload.project_id, ident=payload.ident,
|
||||
round__key=payload.round_key)
|
||||
# 判断是否和同项目同轮次的标识重复
|
||||
if len(design_search) > 1 and payload.ident != '':
|
||||
return ChenResponse(code=400, status=400, message='研制需求的标识重复,请检查')
|
||||
# 查到当前
|
||||
design_qs = Design.objects.get(id=id)
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'project_id' or attr == 'round_key' or attr == 'dut_key':
|
||||
continue
|
||||
if attr == 'name':
|
||||
setattr(design_qs, "title", value)
|
||||
setattr(design_qs, attr, value)
|
||||
design_qs.save()
|
||||
return design_qs
|
||||
|
||||
# 删除设计需求
|
||||
@route.delete("/designDemand/delete", url_name="design-delete")
|
||||
@transaction.atomic
|
||||
def delete_design(self, data: DeleteSchema):
|
||||
# 根据其中一个id查询出dut_id
|
||||
try:
|
||||
design_single = Design.objects.filter(id=data.ids[0])[0]
|
||||
except IndexError:
|
||||
return ChenResponse(status=500, code=HTTP_INDEX_ERROR, message='您未选择需要删除的内容')
|
||||
dut_id = design_single.dut.id
|
||||
dut_key = design_single.dut.key
|
||||
multi_delete_design(data.ids, Design)
|
||||
index = 0
|
||||
design_all_qs = Design.objects.filter(dut__id=dut_id).order_by('id')
|
||||
for single_qs in design_all_qs:
|
||||
design_key = "".join([dut_key, '-', str(index)])
|
||||
single_qs.key = design_key
|
||||
index = index + 1
|
||||
single_qs.save()
|
||||
design_delete_sub_node_key(single_qs)
|
||||
return ChenResponse(message="设计需求删除成功!")
|
||||
|
||||
# 给复制功能级联选择器查询所有的设计需求
|
||||
@route.get("/designDemand/getRelatedDesign", url_name='dut-relatedDesign')
|
||||
def getRelatedDesign(self, id: int):
|
||||
project_qs = get_object_or_404(Project, id=id)
|
||||
# 依次找出round -> dut -> design
|
||||
round_qs = project_qs.pField.all()
|
||||
data_list = []
|
||||
for round in round_qs:
|
||||
round_dict = {'label': round.name, 'value': round.id, 'children': []}
|
||||
for dut in round.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}
|
||||
dut_dict['children'].append(design_dict)
|
||||
round_dict['children'].append(dut_dict)
|
||||
data_list.append(round_dict)
|
||||
return ChenResponse(message='获取成功', data=data_list)
|
||||
246
apps/project/controllers/dut.py
Normal file
246
apps/project/controllers/dut.py
Normal file
@@ -0,0 +1,246 @@
|
||||
import os
|
||||
import tempfile
|
||||
from copy import deepcopy
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja import Query, File, UploadedFile
|
||||
from ninja.errors import HttpError
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja.pagination import paginate
|
||||
from utils.chen_pagination import MyPagination
|
||||
from django.db import transaction
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import multi_delete_dut
|
||||
from utils.codes import HTTP_INDEX_ERROR
|
||||
from apps.project.models import Dut, Round, Project, DutMetrics
|
||||
from django.shortcuts import get_object_or_404
|
||||
from apps.project.schemas.dut import DutModelOutSchema, DutFilterSchema, DutTreeReturnSchema, DutTreeInputSchema, \
|
||||
DutCreateInputSchema, DutCreateOutSchema, DeleteSchema, DutCreateR1SoDutSchema
|
||||
# 导入自动生成design、demand、case的辅助函数
|
||||
from apps.project.tools.auto_create_data import auto_create_jt_and_dm, auto_create_wd
|
||||
from apps.project.tools.delete_change_key import dut_delete_sub_node_key
|
||||
from utils.smallTools.interfaceTools import model_retrieve
|
||||
# 导入代码统计函数
|
||||
from apps.project.tool.source_counter import analyze_code_directory, extract_and_get_paths
|
||||
# 导入需求解析类
|
||||
from apps.project.tool.xq_parse import DocxChapterExtractor
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['被测件数据'])
|
||||
class DutController(ControllerBase):
|
||||
@route.get("/getDutList", response=List[DutModelOutSchema], exclude_none=True, url_name="dut-list")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_dut_list(self, filters: DutFilterSchema = Query(...)):
|
||||
qs = model_retrieve(filters, Dut.objects, ['project_id', 'round_id']).order_by("-create_datetime")
|
||||
qs = qs.filter(project__id=filters.project_id, round__key=filters.round_id)
|
||||
return qs
|
||||
|
||||
# 处理树状数据
|
||||
@route.get("/getDutInfo", response=List[DutTreeReturnSchema], url_name="dut-info")
|
||||
def get_round_tree(self, payload: DutTreeInputSchema = Query(...)):
|
||||
qs = Dut.objects.filter(project__id=payload.project_id, round__key=payload.key)
|
||||
return qs
|
||||
|
||||
# 获取单个dut
|
||||
@route.get("/getDutOne", response=DutModelOutSchema, url_name="dut-one")
|
||||
@transaction.atomic
|
||||
def get_dut(self, project_id: int, key: str):
|
||||
dut_qs = Dut.objects.filter(project_id=project_id, key=key).first()
|
||||
if dut_qs:
|
||||
return dut_qs
|
||||
raise HttpError(500, "未找到相应的数据")
|
||||
|
||||
# 添加被测件
|
||||
@route.post("/dut/save", url_name="dut-create", response=DutCreateOutSchema)
|
||||
@transaction.atomic
|
||||
def create_dut(self, payload: DutCreateInputSchema):
|
||||
asert_dict = payload.dict(exclude_none=True)
|
||||
# 当被测件为SO时,一个轮次只运行有一个
|
||||
if payload.type == 'SO':
|
||||
if Dut.objects.filter(project__id=payload.project_id, round__key=payload.round_key, type='SO').exists():
|
||||
return ChenResponse(code=400, status=400, message='源代码被测件一个轮次只能添加一个')
|
||||
# 判重标识
|
||||
if Dut.objects.filter(project__id=payload.project_id, round__key=payload.round_key,
|
||||
ident=payload.ident).exists():
|
||||
return ChenResponse(code=400, status=400, message='被测件的标识重复,请检查')
|
||||
# 查询当前key应该为多少
|
||||
dut_count = Dut.objects.filter(project__id=payload.project_id, round__key=payload.round_key).count()
|
||||
key_string = ''.join([payload.round_key, "-", str(dut_count)])
|
||||
# 然后在标识后面加上UT+KEY -> 注意删除时也改了key要对应修改blink1->>>>>>
|
||||
asert_dict['ident'] = ''.join([asert_dict['ident'], str(dut_count + 1)])
|
||||
# 查询当前的round_id
|
||||
round_instance = Round.objects.get(project__id=payload.project_id, key=payload.round_key)
|
||||
asert_dict.update({'key': key_string, 'round': round_instance, 'title': payload.name})
|
||||
asert_dict.pop("round_key")
|
||||
qs = Dut.objects.create(**asert_dict)
|
||||
return qs
|
||||
|
||||
# 更新被测件
|
||||
@route.put("/dut/update/{id}", url_name="dut-update", response=DutCreateOutSchema)
|
||||
@transaction.atomic
|
||||
def update_dut(self, id: int, payload: DutCreateInputSchema):
|
||||
dut_search = Dut.objects.filter(project__id=payload.project_id, ident=payload.ident)
|
||||
# 判断是否和同项目同轮次的标识重复
|
||||
if len(dut_search) > 1:
|
||||
return ChenResponse(code=400, status=400, message='被测件的标识重复,请检查')
|
||||
# 查到当前
|
||||
if payload.type == 'SO':
|
||||
dut_qs = Dut.objects.get(id=id)
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'project_id' or attr == 'round_key':
|
||||
continue
|
||||
if attr == 'name':
|
||||
setattr(dut_qs, "title", value)
|
||||
setattr(dut_qs, attr, value)
|
||||
dut_qs.save()
|
||||
return dut_qs
|
||||
else:
|
||||
dut_qs = Dut.objects.get(id=id)
|
||||
for attr, value in payload.dict().items():
|
||||
if attr == 'project_id' or attr == 'round_key':
|
||||
continue
|
||||
if attr == 'total_lines' or attr == 'effective_lines' or attr == 'comment_lines':
|
||||
setattr(dut_qs, attr, "")
|
||||
continue
|
||||
if attr == 'name':
|
||||
setattr(dut_qs, "title", value)
|
||||
setattr(dut_qs, attr, value)
|
||||
dut_qs.save()
|
||||
return dut_qs
|
||||
|
||||
# 删除被测件 - 1.重新对key排序 2.重新对表示尾号排序
|
||||
@route.delete("/dut/delete", url_name="dut-delete")
|
||||
@transaction.atomic
|
||||
def delete_dut(self, data: DeleteSchema):
|
||||
# 查询某一个dut对象
|
||||
try:
|
||||
dut_single = Dut.objects.filter(id=data.ids[0])[0]
|
||||
except IndexError:
|
||||
return ChenResponse(status=500, code=HTTP_INDEX_ERROR, message='您未选择需要删除的内容')
|
||||
# 查询出dut所属的轮次id、key
|
||||
round_id = dut_single.round.id
|
||||
round_key = dut_single.round.key
|
||||
# blink1->>>>>> 这里不仅重排key,还要重排ident中编号,先取出前面的RXXXX-RX等信息,这里必须要在删除之前
|
||||
# 查询出当前轮次所有dut
|
||||
ids = deepcopy(data.ids)
|
||||
message = '被测件删除成功'
|
||||
for id in data.ids:
|
||||
dut_obj = Dut.objects.filter(type='SO', id=id).first()
|
||||
if dut_obj:
|
||||
ids.remove(id)
|
||||
message = '源代码被测件不能删除'
|
||||
multi_delete_dut(ids, Dut)
|
||||
dut_all_qs = Dut.objects.filter(round__id=round_id).order_by('id')
|
||||
ident_before_string = dut_all_qs[0].ident.split("UT")[0] # 输出类似于“R2233-R1-”
|
||||
index = 0
|
||||
for single_qs in dut_all_qs:
|
||||
dut_key = "".join([round_key, '-', str(index)]) # 重排现有的dut的key
|
||||
single_qs.key = dut_key
|
||||
single_qs.ident = ident_before_string + "UT" + str(index + 1)
|
||||
index = index + 1
|
||||
single_qs.save()
|
||||
# 不仅重排自己的还要改所有子类的key,因为还是之前的key
|
||||
dut_delete_sub_node_key(single_qs)
|
||||
|
||||
return ChenResponse(message=message)
|
||||
|
||||
# 查询项目中第一轮次是否存在源代码的被测件 -> 5月16日更改:查每一轮是否有源代码被测件
|
||||
@route.get("/dut/soExist", url_name="dut-soExist")
|
||||
@transaction.atomic
|
||||
def delete_soExist(self, id: int):
|
||||
project_obj = get_object_or_404(Project, id=id)
|
||||
# 先查询项目的所有轮次
|
||||
round_qs = project_obj.pField.all()
|
||||
data = {
|
||||
'round_count': round_qs.count(),
|
||||
'round_list': []
|
||||
}
|
||||
for round_obj in round_qs:
|
||||
so_dut_exists = round_obj.rdField.filter(type='SO').exists()
|
||||
round_dict = {
|
||||
'key': round_obj.key,
|
||||
'isExists': so_dut_exists
|
||||
}
|
||||
data['round_list'].append(round_dict)
|
||||
return ChenResponse(code=200, status=200, message='在data展示轮次是否有源代码信息', data=data)
|
||||
|
||||
# 弹窗添加第一轮被测件源代码信息,另外创建测试项(静态分析、代码审查),测试用例(静态分析、代码审查)
|
||||
@route.post("/dut/createR1Sodut", response=DutCreateOutSchema, url_name='dut-r1SoDut')
|
||||
@transaction.atomic
|
||||
def create_r1_so_dut(self, data: DutCreateR1SoDutSchema):
|
||||
asert_dict = data.dict(exclude_none=True) # asert_dict['round_key']可以获取是第几轮次
|
||||
round_key = asert_dict.pop('round_key')
|
||||
project_obj = get_object_or_404(Project, id=data.project_id)
|
||||
if Dut.objects.filter(project__id=data.project_id, round__key=round_key, type='SO').exists():
|
||||
return ChenResponse(code=400, status=400, message='源代码被测件一个轮次只能添加一个')
|
||||
# 查询当前key应该为多少
|
||||
dut_count = Dut.objects.filter(project__id=data.project_id, round__key=round_key).count()
|
||||
key_string = ''.join([round_key, "-", str(dut_count)])
|
||||
asert_dict['ident'] = "-".join(
|
||||
[project_obj.ident, ''.join(['R', str(int(round_key) + 1)]), 'UT', str(dut_count + 1)]).replace("UT-", "UT")
|
||||
# 查询round_id
|
||||
round_id = project_obj.pField.filter(key=round_key).first().id
|
||||
asert_dict['round_id'] = round_id
|
||||
asert_dict.update({'key': key_string, 'title': '软件源代码', 'type': 'SO', 'name': '软件源代码', 'level': '1'})
|
||||
dut_qs: Dut = Dut.objects.create(**asert_dict)
|
||||
# 到这里就自动生成了第一轮的源代码dut,下面使用辅助函数自动生成(静态分析、代码审查)
|
||||
user_name = self.context.request.user.name
|
||||
# 注意判断如果非第一轮次
|
||||
# 1.自动生成静态分析、代码审查
|
||||
auto_create_jt_and_dm(user_name, dut_qs, project_obj)
|
||||
# 2.自动生成文档审查在源代码被测件中
|
||||
auto_create_wd(user_name, dut_qs, project_obj)
|
||||
return dut_qs
|
||||
|
||||
# 进入dut页面,返回dut的类型,例如XQ/XY/SO
|
||||
@route.get('/dut/dut_type', url_name='testDemand-type')
|
||||
@transaction.atomic
|
||||
def get_dut_type(self, project_id: int, key: str):
|
||||
project_qs = get_object_or_404(Project, id=project_id)
|
||||
dut = project_qs.pdField.filter(key=key).first()
|
||||
return ChenResponse(code=200, status=200, data={'dut_type': dut.type})
|
||||
|
||||
@api_controller("/dut_upload", tags=['上传源代码/上传需求规格说明解析'])
|
||||
class UploadController(ControllerBase):
|
||||
# 上传zip、7z、rar压缩文件然后计算圈复杂度等信息
|
||||
@route.post("/upload_file", url_name='dut-upload-file')
|
||||
def upload_code_lines(self, dut_id: int, file: File[UploadedFile]):
|
||||
# 获取dut对象
|
||||
dut_qs: Dut = get_object_or_404(Dut, id=dut_id)
|
||||
# 创建临时目录
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# 保存上传的ZIP文件
|
||||
zip_path = os.path.join(tmp_dir, file.name)
|
||||
with open(zip_path, 'wb') as f:
|
||||
for chunk in file.chunks():
|
||||
f.write(chunk)
|
||||
# 解压并获取文件路径
|
||||
source_root = extract_and_get_paths(zip_path, tmp_dir)
|
||||
results = analyze_code_directory(source_root)
|
||||
# 判断是否录入了metrics,并且去掉ORM不需要字段
|
||||
key_to_remove = {'comment_rate', 'total_lines', 'effective_lines', 'comment_lines', 'code_ratio'}
|
||||
create_results = {k: v for k, v in results.items() if k not in key_to_remove}
|
||||
# 这是判断反向外键是否存在的关键
|
||||
if not hasattr(dut_qs, 'metrics'):
|
||||
DutMetrics.objects.create(**create_results, dut=dut_qs)
|
||||
DutMetrics.objects.filter(dut=dut_qs).update(**create_results)
|
||||
# 进行储存
|
||||
dut_qs.total_lines = results['total_lines']
|
||||
dut_qs.effective_lines = results['effective_lines']
|
||||
dut_qs.comment_lines = results['comment_lines']
|
||||
dut_qs.save()
|
||||
return results
|
||||
|
||||
# 上传需求规格说明.docx进行解析
|
||||
@route.post("/upload_xq_docx/", url_name='dut-xq-docx')
|
||||
def upload_xq_docx(self, dut_key: str, project_id: int, file: File[UploadedFile]):
|
||||
# 构建临时目录
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
# 保存到临时目录
|
||||
docx_path = os.path.join(tmp_dir, file.name)
|
||||
with open(docx_path, 'wb') as f:
|
||||
for chunk in file.chunks():
|
||||
f.write(chunk)
|
||||
extractor = DocxChapterExtractor(docx_path)
|
||||
extractor.main('需求')
|
||||
332
apps/project/controllers/problem.py
Normal file
332
apps/project/controllers/problem.py
Normal file
@@ -0,0 +1,332 @@
|
||||
import datetime
|
||||
import numpy as np
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja import Query
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja.pagination import paginate
|
||||
from apps.dict.models import DictItem
|
||||
from utils.chen_pagination import MyPagination
|
||||
from django.db import transaction
|
||||
from typing import List, Optional
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.codes import HTTP_INDEX_ERROR
|
||||
from django.shortcuts import get_object_or_404
|
||||
from apps.project.models import Case, Problem, Project, TestDemand
|
||||
from apps.project.schemas.problem import (
|
||||
DeleteSchema,
|
||||
ProblemModelOutSchema,
|
||||
ProblemFilterSchema,
|
||||
ProblemCreateOutSchema,
|
||||
ProblemCreateInputSchema,
|
||||
ProblemSingleInputSchema,
|
||||
ProblemUpdateInputSchema,
|
||||
ProblemFilterWithHangSchema
|
||||
)
|
||||
from utils.util import get_str_abbr
|
||||
from utils.smallTools.interfaceTools import conditionNoneToBlank
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['问题单系列'])
|
||||
class ProblemController(ControllerBase):
|
||||
@route.get("/getProblemList", response=List[ProblemModelOutSchema], exclude_none=True,
|
||||
url_name="problem-list")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_problem_list(self, data: ProblemFilterSchema = Query(...)):
|
||||
project_id = data.project_id
|
||||
conditionNoneToBlank(data)
|
||||
case_key = "".join([data.round_id, '-', data.dut_id, '-', data.design_id, '-', data.test_id, '-', data.case_id])
|
||||
# 先查询出对应的case
|
||||
case_obj = Case.objects.filter(project_id=project_id, key=case_key).first()
|
||||
# 然后进行过滤
|
||||
qs = case_obj.caseField.filter(project__id=data.project_id,
|
||||
ident__icontains=data.ident,
|
||||
name__icontains=data.name,
|
||||
status__icontains=data.status,
|
||||
type__icontains=data.type,
|
||||
grade__icontains=data.grade,
|
||||
operation__icontains=data.operation,
|
||||
postPerson__icontains=data.postPerson,
|
||||
).order_by("id")
|
||||
|
||||
# 遍历通过代码不通过ORM查询闭环方式-巧妙使用numpy中array对象的in方法来判断
|
||||
closeMethod1 = self.context.request.GET.get("closeMethod[0]")
|
||||
closeMethod2 = self.context.request.GET.get("closeMethod[1]")
|
||||
query_add_closeMethod = []
|
||||
for query in qs:
|
||||
arr = np.array(query.closeMethod)
|
||||
if closeMethod1 is None and closeMethod2 is None:
|
||||
query_add_closeMethod.append(query)
|
||||
continue
|
||||
if closeMethod1 in arr:
|
||||
query_add_closeMethod.append(query)
|
||||
continue
|
||||
if closeMethod2 in arr:
|
||||
query_add_closeMethod.append(query)
|
||||
continue
|
||||
return query_add_closeMethod
|
||||
|
||||
# 搜索全部问题单/或查询轮次下的问题单
|
||||
@route.get('/problem/searchAllProblem', response=List[ProblemModelOutSchema], exclude_none=True,
|
||||
url_name="problem-allList")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_all_problems(self, round_key: Optional[str] = False, data: ProblemFilterWithHangSchema = Query(...)):
|
||||
project_id = data.project_id
|
||||
for attr, value in data.__dict__.items():
|
||||
if getattr(data, attr) is None:
|
||||
setattr(data, attr, '')
|
||||
# 先查询当前项目
|
||||
qs = Problem.objects.filter(project__id=data.project_id,
|
||||
ident__icontains=data.ident,
|
||||
name__icontains=data.name,
|
||||
status__icontains=data.status,
|
||||
type__icontains=data.type,
|
||||
grade__icontains=data.grade,
|
||||
operation__icontains=data.operation,
|
||||
postPerson__icontains=data.postPerson,
|
||||
).order_by("id")
|
||||
closeMethod1 = self.context.request.GET.get("closeMethod[0]")
|
||||
closeMethod2 = self.context.request.GET.get("closeMethod[1]")
|
||||
query_final = []
|
||||
for query in qs:
|
||||
arr = np.array(query.closeMethod)
|
||||
if closeMethod1 is None and closeMethod2 is None:
|
||||
query_final.append(query)
|
||||
continue
|
||||
if closeMethod1 in arr:
|
||||
query_final.append(query)
|
||||
continue
|
||||
if closeMethod2 in arr:
|
||||
query_final.append(query)
|
||||
continue
|
||||
# 遍历所有problem,查询是有否有关联case,如果有则设置hang为True,否则False
|
||||
hang = True
|
||||
# 过滤不是该轮次的问题单对象列表
|
||||
deleted_problem_list = []
|
||||
for pro_obj in query_final:
|
||||
case_exists = pro_obj.case.exists()
|
||||
if not case_exists:
|
||||
setattr(pro_obj, "hang", hang)
|
||||
# 如果有关联用例还要看是否是查询轮次的问题单,过滤出去
|
||||
elif case_exists:
|
||||
hang = False
|
||||
setattr(pro_obj, "hang", hang)
|
||||
hang = True
|
||||
if round_key:
|
||||
if not pro_obj.case.filter(round__key=round_key).exists():
|
||||
deleted_problem_list.append(pro_obj)
|
||||
for dq in deleted_problem_list:
|
||||
query_final.remove(dq)
|
||||
# !!!如果是轮次查询则返回轮次,如果是关联查询则查询关联当前的case
|
||||
if round_key:
|
||||
pass
|
||||
else:
|
||||
case_obj = Case.objects.filter(project_id=project_id, key=data.key).first()
|
||||
if case_obj:
|
||||
for pro_obj in query_final:
|
||||
# 查询关联的case
|
||||
related = False
|
||||
for re_case in pro_obj.case.all():
|
||||
if case_obj.id == re_case.id:
|
||||
related = True
|
||||
setattr(pro_obj, "related", related)
|
||||
# 过滤查询悬挂逻辑
|
||||
query_last = []
|
||||
if data.hang == '3' or data.hang == '': # 疑问:为什么会是空字符串
|
||||
query_last = query_final
|
||||
if data.hang == '2':
|
||||
for pp in query_final:
|
||||
if not pp.hang:
|
||||
query_last.append(pp)
|
||||
if data.hang == '1':
|
||||
for pp in query_final:
|
||||
if pp.hang is True:
|
||||
query_last.append(pp)
|
||||
return query_last
|
||||
|
||||
@staticmethod
|
||||
def __date_solve(payload: ProblemCreateInputSchema):
|
||||
"""辅助函数:
|
||||
1.设置问题单时间,而不是默认进入时间,传入schema对象,返回schema对象,只对里面时间进行处理
|
||||
"""
|
||||
project_obj = get_object_or_404(Project, id=payload.project_id)
|
||||
round_obj = project_obj.pField.filter(key=payload.round_key).first()
|
||||
if round_obj:
|
||||
if payload.postDate is None:
|
||||
payload.postDate = round_obj.beginTime + datetime.timedelta(days=1)
|
||||
if payload.designDate is None:
|
||||
payload.designDate = round_obj.beginTime + datetime.timedelta(days=2)
|
||||
return payload
|
||||
|
||||
# 添加问题单
|
||||
@route.post("/problem/save", response=ProblemCreateOutSchema, url_name="problem-create")
|
||||
@transaction.atomic
|
||||
def create_case_demand(self, payload: ProblemCreateInputSchema):
|
||||
payload = self.__date_solve(payload)
|
||||
asert_dict = payload.dict()
|
||||
project_id = payload.project_id
|
||||
# 查询problem的总数
|
||||
problem_count = Problem.objects.filter(project_id=project_id).count()
|
||||
# 查询当前各个前面节点的instance
|
||||
pop_keys: List[str] = ["round_key", "dut_key", "design_key", "test_key", "case_key"]
|
||||
for pkey in pop_keys:
|
||||
asert_dict.pop(pkey)
|
||||
# 处理问题单标识PT_项目ident_数目依次增加
|
||||
asert_dict["ident"] = str(problem_count + 1)
|
||||
qs = Problem.objects.create(**asert_dict)
|
||||
# 处理时间
|
||||
qs.postDate = payload.postDate
|
||||
qs.designDate = payload.designDate
|
||||
qs.save()
|
||||
# 分两个逻辑处理,无关联创建问题单/case下面创建问题单
|
||||
if payload.case_key:
|
||||
# 构造case_key
|
||||
case_key = "".join(
|
||||
[payload.round_key, "-", payload.dut_key, '-', payload.design_key, '-', payload.test_key, '-',
|
||||
payload.case_key])
|
||||
# 查询出所属的case
|
||||
case_obj = Case.objects.filter(project_id=project_id, key=case_key).first()
|
||||
qs.case.add(case_obj)
|
||||
qs.save()
|
||||
# 对problem的ident排序
|
||||
self.reset_problem_ident(project_id)
|
||||
return qs
|
||||
|
||||
# 更新问题单
|
||||
@route.put("/problem/update/{id}", response=ProblemCreateOutSchema, url_name="problem-update")
|
||||
@transaction.atomic
|
||||
def update_problem(self, id: int, payload: ProblemCreateInputSchema):
|
||||
# 查到当前
|
||||
problem_qs = Problem.objects.get(id=id)
|
||||
for attr, value in payload.dict().items():
|
||||
setattr(problem_qs, attr, value)
|
||||
problem_qs.save()
|
||||
return ChenResponse(message="问题单更新成功")
|
||||
|
||||
# 弹窗的-更新问题单
|
||||
@route.put("/problem/modalupdate/{id}", response=ProblemCreateOutSchema, url_name="problem-update")
|
||||
@transaction.atomic
|
||||
def update_modal_problem(self, id: int, payload: ProblemUpdateInputSchema):
|
||||
# 查到当前
|
||||
problem_qs = Problem.objects.get(id=id)
|
||||
for attr, value in payload.dict().items():
|
||||
setattr(problem_qs, attr, value)
|
||||
problem_qs.save()
|
||||
return ChenResponse(message="问题单更新成功")
|
||||
|
||||
# 删除问题单
|
||||
@route.delete("/problem/delete", url_name="problem-delete")
|
||||
@transaction.atomic
|
||||
def delete_problem(self, data: DeleteSchema):
|
||||
# 1.查询出所有被删除id
|
||||
problems = Problem.objects.filter(id__in=data.ids)
|
||||
if not problems.exists():
|
||||
return ChenResponse(status=500, code=HTTP_INDEX_ERROR, message='您未选取删除内容')
|
||||
# 4.查询出当前项目id
|
||||
project_id = None
|
||||
# 2.循环该取出problem
|
||||
for problem in problems:
|
||||
project_id = problem.project_id
|
||||
# 3. 直接删除case关联,然后删除自己
|
||||
problem.case.clear()
|
||||
problem.delete()
|
||||
# 4.找到对应项目的所有problems进行排序
|
||||
if project_id is not None:
|
||||
self.reset_problem_ident(project_id)
|
||||
return ChenResponse(message="问题单删除成功!")
|
||||
|
||||
# 根据问题单id,返回关联的用例s
|
||||
@route.get('/getRelativeCases', url_name='problem-relative-case')
|
||||
@transaction.atomic
|
||||
def get_relative_cases(self, id: int):
|
||||
problem_qs = get_object_or_404(Problem, id=id)
|
||||
cases = problem_qs.case.all()
|
||||
case_list = []
|
||||
for case in cases:
|
||||
case_dict = {
|
||||
'id': case.id,
|
||||
'case': case.title,
|
||||
'round': case.round.title,
|
||||
'dut': case.dut.title,
|
||||
'design': case.design.title,
|
||||
}
|
||||
demand = case.test
|
||||
case_dict['demand'] = demand.title
|
||||
demand_testType_showtitle = get_str_abbr(demand.testType, 'testType')
|
||||
case_dict['demand_ident'] = "-".join(['XQ', demand_testType_showtitle, demand.ident])
|
||||
case_list.append(case_dict)
|
||||
return case_list
|
||||
|
||||
# 单独显示问题单页面需要数据
|
||||
@route.get("/getSingleProblem", url_name="problem-single", response=ProblemCreateOutSchema)
|
||||
@transaction.atomic
|
||||
def search_single_problem(self, data: ProblemSingleInputSchema = Query(...)):
|
||||
key_string = "".join(
|
||||
[data.round_id, '-', data.dut_id, '-', data.design_id, '-', data.test_id, '-', data.case_id, '-',
|
||||
data.problem_id])
|
||||
qs = Problem.objects.get(project__id=data.project_id, key=key_string)
|
||||
return qs
|
||||
|
||||
# 让测试用例关联/取消问题单
|
||||
@route.get('/problem/relateProblem', exclude_none=True, url_name="problem-allList")
|
||||
@transaction.atomic
|
||||
def relate_problem(self, case_key: str, problem_id: int, val: bool): # val是将要变成的值
|
||||
# 先判断将要变成的值是否为True
|
||||
problem_obj: Problem = Problem.objects.filter(id=problem_id).first()
|
||||
project_id = problem_obj.project_id # 根据问题单反推项目id
|
||||
case_obj = Case.objects.filter(project_id=project_id, key=case_key).first()
|
||||
flag = False # 是否操作成功的标志
|
||||
if val:
|
||||
# 这分支是进行关联操作
|
||||
# 5月15日新需求:一个用例只能关联一个问题单
|
||||
if case_obj.caseField.count() >= 1:
|
||||
return ChenResponse(code=400, status=400, message='请注意:一个用例只允许关联一个问题单',
|
||||
data={'isOK': False})
|
||||
case_obj.caseField.add(problem_obj)
|
||||
flag = True
|
||||
else:
|
||||
case_obj.caseField.remove(problem_obj)
|
||||
flag = True
|
||||
# 排序ident
|
||||
if project_id:
|
||||
self.reset_problem_ident(project_id)
|
||||
return ChenResponse(code=200, status=200, message='关联或取消关联成功...',
|
||||
data={'isOK': flag, 'key': case_obj.key})
|
||||
|
||||
# 类方法:操作后对problem的ident排序:先基于轮次排序,然后基于测试项类型排序
|
||||
@classmethod
|
||||
def reset_problem_ident(cls, project_id: int):
|
||||
project_obj: Project = get_object_or_404(Project, id=project_id)
|
||||
# 获取所有问题单
|
||||
problem_qs = project_obj.projField.prefetch_related('case').prefetch_related('case__test')
|
||||
# 待排序列表
|
||||
not_sorted_problems = []
|
||||
# 处理为List[Dict]以便后续排序修改ident
|
||||
for problem in problem_qs:
|
||||
cases = problem.case.all()
|
||||
if len(cases):
|
||||
# 如果关联了case
|
||||
belong_demand: TestDemand = cases[0].test
|
||||
# 找到对应测试类型
|
||||
test_type = DictItem.objects.get(dict__code='testType', key=belong_demand.testType)
|
||||
# 找到测试类型的sort/找到轮次key
|
||||
not_sorted_problems.append({
|
||||
'problem': problem,
|
||||
'sort': test_type.sort,
|
||||
'round_key': belong_demand.round.key,
|
||||
})
|
||||
else:
|
||||
# 如果没有关联case
|
||||
not_sorted_problems.append({
|
||||
'problem': problem,
|
||||
'sort': 1024,
|
||||
'round_key': 1024,
|
||||
})
|
||||
# 排序后修改ident
|
||||
round_sorted_problems = sorted(not_sorted_problems, key=lambda x: int(x['round_key']))
|
||||
last_sorted_problems = sorted(round_sorted_problems, key=lambda x: int(x['sort']))
|
||||
# 根据排序修改problem的ident
|
||||
for index, problem_dict in enumerate(last_sorted_problems):
|
||||
problem_dict['problem'].ident = str(index + 1)
|
||||
problem_dict['problem'].save()
|
||||
266
apps/project/controllers/project.py
Normal file
266
apps/project/controllers/project.py
Normal file
@@ -0,0 +1,266 @@
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
from typing import List
|
||||
from shutil import copytree, rmtree
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db import transaction
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from apps.user.models import Users
|
||||
from utils.chen_pagination import MyPagination
|
||||
from ninja.pagination import paginate
|
||||
from ninja import Query
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import create, multi_delete_project
|
||||
from apps.project.models import Project, Round
|
||||
from apps.project.schemas.project import ProjectRetrieveSchema, ProjectFilterSchema, ProjectCreateInput, DeleteSchema
|
||||
from utils.util import get_str_dict
|
||||
# 时间处理模块
|
||||
from apps.project.tool.timeList import time_return_to
|
||||
# 反射工具
|
||||
from utils.smallTools.interfaceTools import conditionNoneToBlank
|
||||
|
||||
media_path = Path.cwd() / 'media'
|
||||
base_document_path = Path.cwd() / 'conf/base_document'
|
||||
|
||||
@api_controller("/testmanage/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['项目表相关'])
|
||||
class ProjectController(ControllerBase):
|
||||
@route.get("/index", response=List[ProjectRetrieveSchema])
|
||||
@paginate(MyPagination)
|
||||
def list_project(self, filters: ProjectFilterSchema = Query(...)):
|
||||
conditionNoneToBlank(filters)
|
||||
# 处理时间范围
|
||||
start_time = self.context.request.GET.get('searchOnlyTimeRange[0]')
|
||||
if start_time is None:
|
||||
start_time = "2000-01-01"
|
||||
end_time = self.context.request.GET.get('searchOnlyTimeRange[1]')
|
||||
if end_time is None:
|
||||
end_time = '9999-01-01'
|
||||
date_list = [start_time, end_time]
|
||||
# 前端返回的member
|
||||
member_list = []
|
||||
for key, value in self.context.request.GET.items():
|
||||
if key.find('member') != -1:
|
||||
member_list.append(self.context.request.GET[key])
|
||||
qs = Project.objects.filter(
|
||||
ident__icontains=filters.ident, name__icontains=filters.name,
|
||||
beginTime__range=date_list, duty_person__icontains=filters.duty_person,
|
||||
security_level__icontains=filters.security_level,
|
||||
report_type__icontains=filters.report_type, step__icontains=filters.step,
|
||||
member__contains=member_list, secret__icontains=filters.secret).order_by(
|
||||
"-create_datetime")
|
||||
# 对软件类型进行处理
|
||||
if filters.soft_type != '':
|
||||
qs = qs.filter(soft_type=filters.soft_type)
|
||||
|
||||
# ~~role:查询项目的负责人和成员:普通用户只能看到自己参加的项目~~
|
||||
final_qs = []
|
||||
auth_info: Users = self.context.request.auth
|
||||
if auth_info:
|
||||
if auth_info.role != 'admin':
|
||||
for proj in qs:
|
||||
if proj.duty_person == auth_info.name or auth_info.name in proj.member:
|
||||
final_qs.append(proj)
|
||||
return final_qs
|
||||
return qs
|
||||
|
||||
@route.get("/findOneById/{int:project_id}", response=ProjectRetrieveSchema)
|
||||
@transaction.atomic
|
||||
def get_project_by_id(self, project_id: int):
|
||||
project_obj = get_object_or_404(Project, id=project_id)
|
||||
return project_obj
|
||||
|
||||
@route.post("/save")
|
||||
@transaction.atomic
|
||||
def create_project(self, data: ProjectCreateInput):
|
||||
data_dict = data.dict()
|
||||
ident_qucover = Project.objects.filter(ident=data.dict()['ident'])
|
||||
if ident_qucover:
|
||||
return ChenResponse(code=400, status=400, message="项目标识重复,请重新设置")
|
||||
qs = create(self.context.request, data_dict, Project)
|
||||
# 创建项目时候自动添加第一轮测试
|
||||
if qs:
|
||||
Round.objects.create(project_id=qs.id, key='0', level='0', title='第1轮测试', name='第1轮测试',
|
||||
remark='第一轮测试', ident=''.join([qs.ident, '-R1']))
|
||||
# 在新增项目时,将/conf/base_document 移动到 /media/{项目ident}/下面
|
||||
src_dir = base_document_path
|
||||
dist_dir = media_path / qs.ident
|
||||
try:
|
||||
copytree(src_dir, dist_dir) # shutil模块直接是复制并命名,如果命名文件存在则抛出FileExists异常
|
||||
except PermissionError:
|
||||
return ChenResponse(code=500, status=500, message="错误,检查是否打开了服务器的conf中的文档,关闭后重试")
|
||||
except FileExistsError:
|
||||
return ChenResponse(code=500, status=500, message='文件标识已存在或输入为空格,请修改')
|
||||
except FileNotFoundError:
|
||||
return ChenResponse(code=500, status=500, message='文件不存在,请检查')
|
||||
return ChenResponse(code=200, status=200, message="添加项目成功,并添加第一轮测试")
|
||||
|
||||
@route.put("/update/{project_id}")
|
||||
@transaction.atomic
|
||||
def update_project(self, project_id: int, payload: ProjectCreateInput):
|
||||
# 判断标识是否是被允许的字符串
|
||||
project = self.get_object_or_exception(Project, id=project_id)
|
||||
old_ident = project.ident
|
||||
# 更新操作
|
||||
for attr, value in payload.dict().items():
|
||||
setattr(project, attr, value)
|
||||
project.save()
|
||||
new_ident = project.ident
|
||||
# 如果新ident不等于老ident,则做 1.更新文件夹名称 2.更新所有轮次中的ident
|
||||
if new_ident != old_ident:
|
||||
try:
|
||||
Path(media_path / old_ident).rename(media_path / project.ident)
|
||||
# 同时要更改round和dut的标识
|
||||
for r in project.pField.all():
|
||||
r.ident = r.ident.replace(old_ident, new_ident)
|
||||
r.save()
|
||||
for d in project.pdField.all():
|
||||
d.ident = d.ident.replace(old_ident, new_ident)
|
||||
d.save()
|
||||
except PermissionError:
|
||||
return ChenResponse(code=500, status=500, message="错误,请关闭文件资源管理器再试")
|
||||
except FileExistsError:
|
||||
return ChenResponse(code=500, status=500, message='文件标识已存在或输入为空格,请修改')
|
||||
except FileNotFoundError:
|
||||
return ChenResponse(code=500, status=500, message='文件不存在,请检查')
|
||||
return ChenResponse(code=200, status=200, message="项目更新成功")
|
||||
|
||||
@route.delete("/delete")
|
||||
@transaction.atomic
|
||||
def delete(self, data: DeleteSchema):
|
||||
idents = multi_delete_project(data.ids, Project)
|
||||
# 查询media所属项目文件夹,并删除
|
||||
for ident in idents:
|
||||
project_media_path = media_path / ident
|
||||
try:
|
||||
rmtree(project_media_path)
|
||||
except FileNotFoundError as e:
|
||||
return ChenResponse(status=400, code=400, message='项目模版目录可能不存在,可能之前已删除')
|
||||
return ChenResponse(message="删除成功!")
|
||||
|
||||
# 看板页面接口
|
||||
@route.get('/board')
|
||||
@transaction.atomic
|
||||
def board(self, id: int):
|
||||
project_obj = get_object_or_404(Project, id=id)
|
||||
# 1.项目阶段直接转字符串
|
||||
step_str = get_str_dict(project_obj.step, 'step')
|
||||
# 2.返回时间信息
|
||||
# 3.返回人员信息
|
||||
# 4.返回研制方信息
|
||||
# 5.返回用例信息
|
||||
case_qs = project_obj.pcField.all()
|
||||
exe_count = 0 # 已执行数量
|
||||
noexe_count = 0 # 未执行数量
|
||||
partexe_count = 0 # 部分执行数量
|
||||
## 5.1 计算已执行的用例数 -> 所以的都通过/未通过才算执行,否则部分执行
|
||||
for case in case_qs:
|
||||
steps = case.step.all()
|
||||
steps_count = steps.count() # 步骤总数
|
||||
passed_steps_count = steps.filter(passed='1').count()
|
||||
notPassed_steps_count = steps.filter(passed='2').count()
|
||||
notExe_steps_count = steps_count - passed_steps_count - notPassed_steps_count
|
||||
if notExe_steps_count > 0:
|
||||
# 步骤全是未执行,则用例未执行
|
||||
if notExe_steps_count == steps_count:
|
||||
noexe_count += 1
|
||||
else:
|
||||
partexe_count += 1
|
||||
else:
|
||||
exe_count += 1
|
||||
|
||||
# 6.计算问题单数
|
||||
problems = project_obj.projField.all()
|
||||
close_count = 0
|
||||
open_count = 0
|
||||
for problem in problems:
|
||||
if problem.status != '1':
|
||||
open_count += 1
|
||||
else:
|
||||
close_count += 1
|
||||
|
||||
# 7.将时间提取 todo:后续将计算的事件放入该页面
|
||||
timers = {'round_time': []}
|
||||
rounds = project_obj.pField.all()
|
||||
timers['start_time'] = project_obj.beginTime
|
||||
timers['end_time'] = project_obj.endTime
|
||||
for round in rounds:
|
||||
round_number = int(round.key) + 1
|
||||
timers['round_time'].append({
|
||||
'name': f'第{round_number}轮次',
|
||||
'start': round.beginTime,
|
||||
'end': round.endTime
|
||||
})
|
||||
|
||||
# 8.提取所有需求下面测试项、用例数量
|
||||
# 9.提取测试类型下面测试项数量、用例数量
|
||||
data_list = []
|
||||
for round in rounds:
|
||||
round_dict = {'name': f'第{int(round.key) + 1}轮次', 'desings': [], 'method_demand': {}, 'method_case': {}}
|
||||
designs = round.dsField.all()
|
||||
for design in designs:
|
||||
design_dict = {
|
||||
'name': design.name,
|
||||
'demand_count': design.dtField.count(),
|
||||
'case_count': design.dcField.count()
|
||||
}
|
||||
round_dict['desings'].append(design_dict)
|
||||
demands = round.rtField.all()
|
||||
for demand in demands:
|
||||
test_type = get_str_dict(demand.testType, 'testType')
|
||||
if test_type not in round_dict['method_demand']:
|
||||
round_dict['method_demand'][test_type] = 1
|
||||
else:
|
||||
round_dict['method_demand'][test_type] += 1
|
||||
cases = round.rcField.all()
|
||||
for case in cases:
|
||||
testDemand = case.test
|
||||
case_type = get_str_dict(testDemand.testType, 'testType')
|
||||
if case_type not in round_dict['method_case']:
|
||||
round_dict['method_case'][case_type] = 1
|
||||
else:
|
||||
round_dict['method_case'][case_type] += 1
|
||||
data_list.append(round_dict)
|
||||
|
||||
return {
|
||||
'ident': project_obj.ident,
|
||||
'name': project_obj.name,
|
||||
'step': step_str,
|
||||
'title_info': {
|
||||
'时间': {
|
||||
'开始时间': project_obj.beginTime,
|
||||
'结束时间': project_obj.endTime,
|
||||
'到现在时间': f"{(date.today() - project_obj.beginTime).days}天",
|
||||
},
|
||||
'人员': {
|
||||
'负责人': project_obj.duty_person,
|
||||
'成员数': len(project_obj.member),
|
||||
},
|
||||
'开发方信息': {
|
||||
'联系人': project_obj.dev_contact,
|
||||
'电话': project_obj.dev_contact_phone,
|
||||
'邮箱': project_obj.dev_email
|
||||
},
|
||||
'用例数': {
|
||||
'总数': case_qs.count(),
|
||||
'已执行': exe_count,
|
||||
'未执行': noexe_count,
|
||||
'部分执行': partexe_count,
|
||||
},
|
||||
'问题数': {
|
||||
'总数': problems.count(),
|
||||
'已闭环': close_count,
|
||||
'未闭环': open_count,
|
||||
}
|
||||
},
|
||||
'time_line': timers,
|
||||
'statistics': data_list,
|
||||
}
|
||||
|
||||
# 看板页面的生成文档时间接口
|
||||
@route.get('/document_time_show')
|
||||
@transaction.atomic
|
||||
def document_time_show(self, id: int):
|
||||
time = time_return_to(id)
|
||||
return time
|
||||
83
apps/project/controllers/round.py
Normal file
83
apps/project/controllers/round.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from django.db import transaction
|
||||
from apps.project.models import Round
|
||||
from apps.project.schemas.round import TreeReturnRound, RoundInfoOutSchema, EditSchemaIn, DeleteSchema, \
|
||||
CreateRoundOutSchema, CreateRoundInputSchema
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from apps.project.tools.delete_change_key import round_delete_sub_node_key
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['轮次数据'])
|
||||
class RoundController(ControllerBase):
|
||||
@route.get("/getRoundInfo/{project_id}", response=List[TreeReturnRound], url_name="round-info")
|
||||
def get_round_tree(self, project_id):
|
||||
qs = Round.objects.filter(project__id=project_id).order_by('key')
|
||||
return qs
|
||||
|
||||
@route.get("/getOneRoundInfo", response=RoundInfoOutSchema, url_name="round-one-info")
|
||||
def get_round_info(self, projectId: str, round: str):
|
||||
qs = Round.objects.filter(project__id=projectId).order_by('id')
|
||||
# 这里问题是如果删除中间轮次会出现问题
|
||||
qs = qs.get(key=round)
|
||||
return qs
|
||||
|
||||
# 更新轮次信息
|
||||
@route.put("/round/update/{id}", response=RoundInfoOutSchema, url_name="round-update")
|
||||
def update_round(self, id, payload: EditSchemaIn):
|
||||
round = self.get_object_or_exception(Round, project__id=payload.project, id=id)
|
||||
# 去重功能
|
||||
exist_round = Round.objects.filter(project__id=payload.project)
|
||||
for exist_r in exist_round:
|
||||
if exist_r.id != int(id):
|
||||
if exist_r.ident == payload.ident:
|
||||
return ChenResponse(code=400, status=400, message='标识和其他重复')
|
||||
for attr, value in payload.dict().items():
|
||||
# 不知道为什么多个project
|
||||
if attr != "project":
|
||||
setattr(round, attr, value)
|
||||
round.save()
|
||||
return ChenResponse(message="轮次信息更新成功")
|
||||
|
||||
@route.delete("/round/delete", url_name="round-delete")
|
||||
@transaction.atomic
|
||||
def delete_round(self, project_id: str, data: DeleteSchema):
|
||||
# 先查询该project下面的值
|
||||
instance = self.get_object_or_exception(Round, project__id=project_id, key=data.key)
|
||||
if instance.key == '0':
|
||||
return ChenResponse(code=400, status=400, message="无法删除第一轮次数据")
|
||||
# (多对多)删除下面case关联的problem关系
|
||||
cases = instance.rcField.all()
|
||||
for case in cases:
|
||||
case.caseField.clear()
|
||||
instance.delete()
|
||||
# 注意:删除中间key必须发生变化,重写key
|
||||
## 先查询出当前有多少轮次
|
||||
round_all_qs = Round.objects.filter(project__id=project_id).order_by('id')
|
||||
## 1.按顺序将轮次的key从1~N排序 2.并且将ident改为key值一样 3.将名称改为对应
|
||||
index = 0
|
||||
for single_qs in round_all_qs:
|
||||
old_key = single_qs.key
|
||||
single_qs.key = str(index)
|
||||
single_qs.ident = single_qs.ident.replace(f'R{int(old_key) + 1}', f'R{index + 1}')
|
||||
single_qs.name = single_qs.name.replace(str(int(old_key) + 1), str(index + 1))
|
||||
single_qs.title = single_qs.name
|
||||
index = index + 1
|
||||
single_qs.save()
|
||||
round_delete_sub_node_key(single_qs)
|
||||
return ChenResponse(message="删除成功")
|
||||
|
||||
@route.post("/round/save", response=CreateRoundOutSchema, url_name="round-create")
|
||||
def create_round(self, project_id: str, data: CreateRoundInputSchema):
|
||||
asert_dict = data.dict()
|
||||
asert_dict['project_id'] = int(project_id)
|
||||
asert_dict['title'] = asert_dict['name']
|
||||
# 标识去重
|
||||
exist_round = Round.objects.filter(project__id=project_id)
|
||||
for exist_r in exist_round:
|
||||
if exist_r.id != int(project_id):
|
||||
if exist_r.ident == asert_dict['ident']:
|
||||
return ChenResponse(code=400, status=400, message='标识和其他重复')
|
||||
Round.objects.create(**asert_dict)
|
||||
return ChenResponse(message="新增轮次成功")
|
||||
253
apps/project/controllers/testDemand.py
Normal file
253
apps/project/controllers/testDemand.py
Normal file
@@ -0,0 +1,253 @@
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja import Query
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from ninja.pagination import paginate
|
||||
from ninja.errors import HttpError
|
||||
from utils.chen_pagination import MyPagination
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from typing import List
|
||||
from utils.chen_response import ChenResponse
|
||||
from utils.chen_crud import multi_delete_testDemand
|
||||
from utils.codes import HTTP_INDEX_ERROR
|
||||
from apps.project.models import Design, Dut, Round, TestDemand, TestDemandContent, TestDemandContentStep
|
||||
from apps.project.schemas.testDemand import DeleteSchema, TestDemandModelOutSchema, TestDemandFilterSchema, \
|
||||
TestDemandTreeReturnSchema, TestDemandTreeInputSchema, TestDemandCreateOutSchema, TestDemandCreateInputSchema, \
|
||||
TestDemandRelatedSchema, TestDemandExistRelatedSchema, DemandCopyToDesignSchema
|
||||
# 导入ORM
|
||||
from apps.project.models import Project
|
||||
# 导入工具
|
||||
from apps.project.tools.copyDemand import demand_copy_to_design
|
||||
from apps.project.tools.delete_change_key import demand_delete_sub_node_key
|
||||
from utils.smallTools.interfaceTools import conditionNoneToBlank
|
||||
|
||||
@api_controller("/project", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['测试项接口'])
|
||||
class TestDemandController(ControllerBase):
|
||||
@route.get("/getTestDemandList", response=List[TestDemandModelOutSchema], exclude_none=True,
|
||||
url_name="testDemand-list")
|
||||
@transaction.atomic
|
||||
@paginate(MyPagination)
|
||||
def get_test_demand_list(self, datafilter: TestDemandFilterSchema = Query(...)):
|
||||
conditionNoneToBlank(datafilter)
|
||||
design_key = "".join([datafilter.round_id, '-', datafilter.dut_id, '-', datafilter.design_id])
|
||||
qs = TestDemand.objects.filter(project__id=datafilter.project_id, design__key=design_key,
|
||||
ident__icontains=datafilter.ident,
|
||||
name__icontains=datafilter.name,
|
||||
testType__contains=datafilter.testType,
|
||||
priority__icontains=datafilter.priority).order_by("key")
|
||||
# 由于有嵌套query_set存在,把每个测试需求的schema加上一个字段
|
||||
query_list = []
|
||||
for query_single in qs:
|
||||
# 遍历每一个测试子项
|
||||
sub_list = []
|
||||
for step_obj in query_single.testQField.all():
|
||||
setattr(step_obj, "subStep", step_obj.testStepField.all().values())
|
||||
sub_list.append(step_obj)
|
||||
setattr(query_single, "testContent", sub_list)
|
||||
query_list.append(query_single)
|
||||
return query_list
|
||||
|
||||
@route.get("/getTestDemandOne", response=TestDemandModelOutSchema, url_name='testDemand-one')
|
||||
@transaction.atomic
|
||||
def get_test_demand_one(self, project_id: int, key: str):
|
||||
demand_qs = TestDemand.objects.filter(project_id=project_id, key=key).first()
|
||||
if demand_qs:
|
||||
sub_list = []
|
||||
for step_obj in demand_qs.testQField.all():
|
||||
setattr(step_obj, "subStep", step_obj.testStepField.all().values())
|
||||
sub_list.append(step_obj)
|
||||
setattr(demand_qs, "testContent", sub_list)
|
||||
return demand_qs
|
||||
raise HttpError(500, "未找到相应的数据")
|
||||
|
||||
# 处理树状数据
|
||||
@route.get("/getTestdemandInfo", response=List[TestDemandTreeReturnSchema], url_name="testDemand-info")
|
||||
@transaction.atomic
|
||||
def get_testDemand_tree(self, payload: TestDemandTreeInputSchema = Query(...)):
|
||||
qs = TestDemand.objects.filter(project__id=payload.project_id, design__key=payload.key)
|
||||
return qs
|
||||
|
||||
# 添加测试项
|
||||
@route.post("/testDemand/save", response=TestDemandCreateOutSchema, url_name="testDemand-create")
|
||||
@transaction.atomic
|
||||
def create_test_demand(self, payload: TestDemandCreateInputSchema):
|
||||
asert_dict = payload.dict(exclude_none=True)
|
||||
# ident判重
|
||||
project_qs = Project.objects.filter(id=payload.project_id).first()
|
||||
if payload.ident and project_qs:
|
||||
exists = project_qs.ptField.filter(ident=payload.ident).exists()
|
||||
if exists:
|
||||
return ChenResponse(code=500, status=500, message='测试项标识和其他测试项重复,请更换测试项标识!!!')
|
||||
# 构造design_key
|
||||
design_key = "".join([payload.round_key, "-", payload.dut_key, '-', payload.design_key])
|
||||
# 查询当前key应该为多少
|
||||
test_demand_count = TestDemand.objects.filter(project__id=payload.project_id, design__key=design_key).count()
|
||||
key_string = ''.join([design_key, "-", str(test_demand_count)])
|
||||
# 查询当前各个前面节点的instance
|
||||
round_instance = Round.objects.get(project__id=payload.project_id, key=payload.round_key)
|
||||
dut_instance = Dut.objects.get(project__id=payload.project_id,
|
||||
key="".join([payload.round_key, "-", payload.dut_key]))
|
||||
design_instance = Design.objects.get(project__id=payload.project_id, key="".join(
|
||||
[payload.round_key, "-", payload.dut_key, '-', payload.design_key]))
|
||||
asert_dict.update({'key': key_string, 'round': round_instance, 'dut': dut_instance, 'design': design_instance,
|
||||
'title': payload.name})
|
||||
asert_dict.pop("round_key")
|
||||
asert_dict.pop("dut_key")
|
||||
asert_dict.pop("design_key")
|
||||
asert_dict.pop("testContent")
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# 创建测试项 - 以及子项/子项步骤
|
||||
qs = TestDemand.objects.create(**asert_dict)
|
||||
for item in payload.dict()['testContent']:
|
||||
content_obj = TestDemandContent.objects.create(
|
||||
testDemand=qs,
|
||||
subName=item['subName']
|
||||
)
|
||||
TestDemandContentStep.objects.bulk_create([
|
||||
TestDemandContentStep(
|
||||
testDemandContent=content_obj,
|
||||
**step.dict() if not isinstance(step, dict) else step
|
||||
)
|
||||
for step in item['subStep']
|
||||
])
|
||||
return qs
|
||||
|
||||
# 更新测试项
|
||||
@route.put("/testDemand/update/{id}", response=TestDemandCreateOutSchema, url_name="testDemand-update")
|
||||
@transaction.atomic
|
||||
def update_testDemand(self, id: int, payload: TestDemandCreateInputSchema):
|
||||
project_qs = get_object_or_404(Project, id=payload.project_id)
|
||||
# 查到当前
|
||||
testDemand_qs = TestDemand.objects.get(id=id)
|
||||
old_ident = testDemand_qs.ident # 用于判断是否要集体修改case的ident
|
||||
for attr, value in payload.dict().items():
|
||||
# 判重复
|
||||
if attr == 'ident':
|
||||
if testDemand_qs.ident != value: # 如果ident不和原来相等,则要判重复
|
||||
exists = project_qs.ptField.filter(ident=payload.ident).exists()
|
||||
if exists:
|
||||
return ChenResponse(code=500, status=500, message='更换的标识和其他测试项重复')
|
||||
if attr == 'project_id' or attr == 'round_key' or attr == 'dut_key' or attr == 'design_key':
|
||||
continue # 如果发现是key则不处理
|
||||
if attr == 'name':
|
||||
setattr(testDemand_qs, "title", value)
|
||||
# 找到attr为testContent的
|
||||
if attr == 'testContent':
|
||||
content_list = testDemand_qs.testQField.all()
|
||||
for content_single in content_list:
|
||||
# 删除TestDemandContent,会把CASCADE的TestDemandContentStep也删除
|
||||
content_single.delete()
|
||||
# 添加测试项步骤
|
||||
for item in value: # 遍历的是testContent字段,所以每个item是TestDemandContent的数据
|
||||
# 存在subName就添加一个测试子项
|
||||
if item['subName']:
|
||||
content_obj = TestDemandContent.objects.create(
|
||||
testDemand=testDemand_qs,
|
||||
subName=item["subName"]
|
||||
)
|
||||
TestDemandContentStep.objects.bulk_create([
|
||||
TestDemandContentStep(
|
||||
testDemandContent=content_obj,
|
||||
**step.dict() if not isinstance(step, dict) else step
|
||||
)
|
||||
for step in item["subStep"]
|
||||
])
|
||||
# ~~~2024年5月9日:测试项更新标识后还要更新下面用例的标识~~~
|
||||
if testDemand_qs.ident != old_ident:
|
||||
for case in testDemand_qs.tcField.all():
|
||||
case.ident = testDemand_qs.ident
|
||||
case.save()
|
||||
return testDemand_qs
|
||||
|
||||
# 删除测试项
|
||||
@route.delete("/testDemand/delete", url_name="testDemand-delete")
|
||||
@transaction.atomic
|
||||
def delete_testDemand(self, data: DeleteSchema):
|
||||
# 根据其中一个id查询出dut_id
|
||||
try:
|
||||
test_demand_single = TestDemand.objects.filter(id=data.ids[0])[0]
|
||||
except IndexError:
|
||||
return ChenResponse(status=500, code=HTTP_INDEX_ERROR, message='您未选择需要删除的内容')
|
||||
design_id = test_demand_single.design.id
|
||||
design_key = test_demand_single.design.key
|
||||
multi_delete_testDemand(data.ids, TestDemand)
|
||||
index = 0
|
||||
test_demand_all_qs = TestDemand.objects.filter(design__id=design_id).order_by('id')
|
||||
for single_qs in test_demand_all_qs:
|
||||
test_demand_key = "".join([design_key, '-', str(index)])
|
||||
single_qs.key = test_demand_key
|
||||
index = index + 1
|
||||
single_qs.save()
|
||||
demand_delete_sub_node_key(single_qs) # 删除后需重排子节点
|
||||
return ChenResponse(message="测试需求删除成功!")
|
||||
|
||||
# 查询一个项目的所有测试项
|
||||
@route.get("/testDemand/getRelatedTestDemand", url_name="testDemand-getRelatedTestDemand")
|
||||
@transaction.atomic
|
||||
def getRelatedTestDemand(self, id: int, round: str):
|
||||
project_qs = get_object_or_404(Project, id=id)
|
||||
# 找出属于该轮次的所有测试项
|
||||
round_qs = project_qs.pField.filter(key=round).first()
|
||||
designs = round_qs.dsField.all()
|
||||
data_list = []
|
||||
for design in designs:
|
||||
design_dict = {'label': design.name, 'value': design.id, 'children': []}
|
||||
for test_item in design.dtField.all():
|
||||
test_item_dict = {'label': test_item.name, 'value': test_item.id}
|
||||
design_dict['children'].append(test_item_dict)
|
||||
data_list.append(design_dict)
|
||||
return ChenResponse(message='获取成功', data=data_list)
|
||||
|
||||
# 处理desgin关联testDemand接口
|
||||
@route.post('/testDemand/solveRelatedTestDemand', url_name="testDemand-solveRelatedTestDemand")
|
||||
@transaction.atomic
|
||||
def solveRelatedTestDemand(self, data: TestDemandRelatedSchema):
|
||||
test_item_ids = data.data
|
||||
non_exist_ids = [x for x in test_item_ids]
|
||||
project_qs = get_object_or_404(Project, id=data.project_id)
|
||||
key_str = "-".join([data.round_key, data.dut_key, data.design_key])
|
||||
design_item = project_qs.psField.filter(key=key_str).first()
|
||||
if design_item:
|
||||
# 将test_item_ids中本身具有的测试项从id数组中移除
|
||||
for test_id in test_item_ids:
|
||||
for ti in design_item.dtField.all():
|
||||
if ti.pk == test_id:
|
||||
non_exist_ids.remove(test_id)
|
||||
if len(non_exist_ids) <= 0 < len(test_item_ids):
|
||||
return ChenResponse(status=400, code=200, message='选择的测试项全部存在于当前设计需求中,请重新选择...')
|
||||
# 先查询现在有的关联测试项
|
||||
for item in design_item.odField.values('id'):
|
||||
item_id = item.get('id', None)
|
||||
if not item_id in test_item_ids:
|
||||
test_item_obj = TestDemand.objects.filter(id=item_id).first()
|
||||
design_item.odField.remove(test_item_obj)
|
||||
for test_item_id in non_exist_ids:
|
||||
test_items = design_item.odField.filter(id=test_item_id)
|
||||
if len(test_items) <= 0:
|
||||
# 查询testDemand
|
||||
design_item.odField.add(TestDemand.objects.filter(id=test_item_id).first())
|
||||
else:
|
||||
return ChenResponse(status=400, code=400, message='设计需求不存在,请检查...')
|
||||
return ChenResponse(status=200, code=200, message='添加关联测试项成功...')
|
||||
|
||||
# 找出已关联的测试项给前端的cascader
|
||||
@route.post('/testDemand/getExistRelatedTestDemand', url_name="testDemand-getExistRelatedTestDemand")
|
||||
@transaction.atomic
|
||||
def getExistRelatedTestDemand(self, data: TestDemandExistRelatedSchema):
|
||||
project_qs = get_object_or_404(Project, id=data.project_id)
|
||||
key_str = "-".join([data.round_key, data.dut_key, data.design_key])
|
||||
design_item = project_qs.psField.filter(key=key_str).first()
|
||||
ids = []
|
||||
if design_item:
|
||||
for item in design_item.odField.all():
|
||||
ids.append(item.id)
|
||||
return ids
|
||||
|
||||
# 前端测试项右键复制到某个设计需求下面
|
||||
@route.post('/testDemand/copy_to_design', url_name='testDemand-copy')
|
||||
@transaction.atomic
|
||||
def copy_to_design(self, data: DemandCopyToDesignSchema):
|
||||
"""前端测试项右键复制到某个设计需求下面"""
|
||||
new_demand_key = demand_copy_to_design(data.project_id, data.demand_key, data.design_id, data.depth)
|
||||
return ChenResponse(data={'key': new_demand_key})
|
||||
27
apps/project/controllers/treeOperation.py
Normal file
27
apps/project/controllers/treeOperation.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from ninja_extra import api_controller, ControllerBase, route
|
||||
from ninja_jwt.authentication import JWTAuth
|
||||
from ninja_extra.permissions import IsAuthenticated
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
# 导入schema
|
||||
from apps.project.schemas.treeOperation import CopySchema
|
||||
# 导入模型
|
||||
from apps.project.models import Project
|
||||
# 导入本app工具
|
||||
from apps.project.tools.keyTools import TreeKey
|
||||
# 导入项目工具
|
||||
from utils.chen_response import ChenResponse
|
||||
|
||||
@api_controller("/treeOperation", auth=JWTAuth(), permissions=[IsAuthenticated], tags=['树的操作'])
|
||||
class TreeController(ControllerBase):
|
||||
@route.post("/copy", url_name="tree-copy")
|
||||
@transaction.atomic
|
||||
def tree_copy(self, data: CopySchema):
|
||||
"""新建下一个轮次,并复制选中的节点"""
|
||||
project_obj = get_object_or_404(Project, id=data.pid)
|
||||
round_count = project_obj.pField.count()
|
||||
tree_keys = data.data
|
||||
# 逻辑是:如果大节点有值,则复制整个大节点而不关心其子节点
|
||||
key_tree = TreeKey(tree_keys)
|
||||
key_tree.copy_tree(round_count, project_obj)
|
||||
return ChenResponse(code=200, status=200, message='生成轮次成功')
|
||||
381
apps/project/migrations/0001_initial.py
Normal file
381
apps/project/migrations/0001_initial.py
Normal file
@@ -0,0 +1,381 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-03 10:38
|
||||
|
||||
import apps.project.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import tinymce.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Abbreviation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(help_text='缩略语', max_length=64, verbose_name='缩略语')),
|
||||
('des', models.CharField(help_text='描述', max_length=256, verbose_name='描述')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '缩略语和行业词汇',
|
||||
'verbose_name_plural': '缩略语和行业词汇',
|
||||
'db_table': 'project_abbreviation',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Case',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='用例标识', max_length=64, null=True, verbose_name='用例标识')),
|
||||
('name', models.CharField(blank=True, help_text='用例名称', max_length=64, null=True, verbose_name='用例名称')),
|
||||
('initialization', models.CharField(blank=True, help_text='初始化条件', max_length=128, null=True, verbose_name='初始条件')),
|
||||
('premise', models.CharField(blank=True, help_text='前提和约束', max_length=128, null=True, verbose_name='前提和约束')),
|
||||
('summarize', models.CharField(blank=True, help_text='用例综述', max_length=256, null=True, verbose_name='用例综述')),
|
||||
('designPerson', models.CharField(blank=True, help_text='设计人员', max_length=16, null=True, verbose_name='设计人员')),
|
||||
('testPerson', models.CharField(blank=True, help_text='测试人员', max_length=16, null=True, verbose_name='测试人员')),
|
||||
('monitorPerson', models.CharField(blank=True, help_text='审核人员', max_length=16, null=True, verbose_name='审核人员')),
|
||||
('isLeaf', models.BooleanField(default=True, help_text='树状图最后一个节点', verbose_name='树状图最后一个节点')),
|
||||
('title', models.CharField(blank=True, help_text='树-名称', max_length=64, null=True, verbose_name='树-名称')),
|
||||
('key', models.CharField(blank=True, help_text='round-dut-designkey-testdemand-case', max_length=64, null=True, verbose_name='round-dut-designkey-testdemand-case')),
|
||||
('level', models.CharField(blank=True, default=4, help_text='树-level', max_length=64, null=True, verbose_name='树-level')),
|
||||
('exe_time', models.DateField(blank=True, help_text='执行时间', null=True, verbose_name='执行时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '测试用例',
|
||||
'verbose_name_plural': '测试用例',
|
||||
'db_table': 'project_case',
|
||||
'ordering': ('key',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Contact',
|
||||
fields=[
|
||||
('key', models.IntegerField(auto_created=True, help_text='公司编号', verbose_name='公司编号')),
|
||||
('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='显示排序')),
|
||||
('entrust_person', models.CharField(blank=True, help_text='法人', max_length=16, verbose_name='法人')),
|
||||
('name', models.CharField(blank=True, help_text='公司名称', max_length=64, verbose_name='公司名称')),
|
||||
('addr', models.CharField(blank=True, help_text='公司地址', max_length=64, verbose_name='公司地址')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '委托方、研制方、测试方信息',
|
||||
'verbose_name_plural': '委托方、研制方、测试方信息',
|
||||
'db_table': 'contact_gongsi',
|
||||
'ordering': ('create_datetime',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Design',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='设计需求标识', max_length=64, null=True, verbose_name='设计需求标识')),
|
||||
('name', models.CharField(blank=True, help_text='设计需求名称', max_length=64, null=True, verbose_name='设计需求名称')),
|
||||
('demandType', models.CharField(blank=True, help_text='设计需求类型', max_length=8, null=True, verbose_name='设计需求类型')),
|
||||
('description', tinymce.models.HTMLField(blank=True, help_text='设计需求描述', null=True, verbose_name='设计需求描述')),
|
||||
('title', models.CharField(blank=True, help_text='树-名称', max_length=64, null=True, verbose_name='树-名称')),
|
||||
('key', models.CharField(blank=True, help_text='round-dut-designkey', max_length=64, null=True, verbose_name='round-dut-designkey')),
|
||||
('level', models.CharField(blank=True, default=2, help_text='树-level', max_length=64, null=True, verbose_name='树-level')),
|
||||
('chapter', models.CharField(blank=True, help_text='设计需求章节号', max_length=64, verbose_name='设计需求章节号')),
|
||||
('source', models.CharField(blank=True, default='', help_text='接口来源', max_length=64, null=True, verbose_name='接口来源')),
|
||||
('to', models.CharField(blank=True, default='', help_text='接口目的地', max_length=64, null=True, verbose_name='接口目的地')),
|
||||
('type', models.CharField(blank=True, default='', help_text='接口类型', max_length=64, null=True, verbose_name='接口类型')),
|
||||
('protocal', models.CharField(blank=True, default='', help_text='接口协议', max_length=64, null=True, verbose_name='接口协议')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '测试需求',
|
||||
'verbose_name_plural': '测试需求',
|
||||
'db_table': 'project_design',
|
||||
'ordering': ('key',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Dut',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='被测件标识', max_length=64, null=True, verbose_name='被测件标识')),
|
||||
('type', models.CharField(blank=True, help_text='被测件类型', max_length=16, null=True, verbose_name='被测件类型')),
|
||||
('name', models.CharField(blank=True, help_text='被测件名称', max_length=64, null=True, verbose_name='被测件名称')),
|
||||
('black_line', models.CharField(blank=True, help_text='空行代码数', max_length=64, null=True, verbose_name='空行代码数')),
|
||||
('code_line', models.CharField(blank=True, help_text='纯代码行数', max_length=64, null=True, verbose_name='纯代码行数')),
|
||||
('mix_line', models.CharField(blank=True, help_text='混合行数', max_length=64, null=True, verbose_name='混合行数')),
|
||||
('comment_line', models.CharField(blank=True, help_text='纯注释行', max_length=64, null=True, verbose_name='纯注释行')),
|
||||
('title', models.CharField(blank=True, help_text='树-名称', max_length=64, null=True, verbose_name='树-名称')),
|
||||
('key', models.CharField(blank=True, help_text='树-key', max_length=64, null=True, verbose_name='树-key')),
|
||||
('version', models.CharField(blank=True, help_text='发布版本', max_length=64, null=True, verbose_name='发布版本')),
|
||||
('release_union', models.CharField(blank=True, help_text='发布版本', max_length=64, null=True, verbose_name='发布版本')),
|
||||
('release_date', models.DateField(auto_now_add=True, help_text='发布时间', null=True, verbose_name='发布时间')),
|
||||
('ref', models.CharField(blank=True, help_text='文档编号', max_length=32, null=True, verbose_name='文档编号')),
|
||||
('level', models.CharField(blank=True, default=1, help_text='树-level', max_length=64, null=True, verbose_name='树-level')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '被测件信息',
|
||||
'verbose_name_plural': '被测件信息',
|
||||
'db_table': 'project_dut',
|
||||
'ordering': ('key',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Project',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='项目标识', max_length=64, null=True, verbose_name='项目标识')),
|
||||
('name', models.CharField(blank=True, help_text='项目名称', max_length=100, null=True, verbose_name='项目名称')),
|
||||
('engin_model', models.CharField(blank=True, help_text='工程型号', max_length=64, null=True, verbose_name='工程型号')),
|
||||
('section_system', models.CharField(blank=True, help_text='分系统', max_length=64, null=True, verbose_name='分系统')),
|
||||
('sub_system', models.CharField(blank=True, help_text='子系统', max_length=64, null=True, verbose_name='子系统')),
|
||||
('device', models.CharField(blank=True, help_text='设备', max_length=64, null=True, verbose_name='设备')),
|
||||
('beginTime', models.DateField(auto_now_add=True, help_text='开始时间', null=True, verbose_name='开始时间')),
|
||||
('endTime', models.DateField(auto_now_add=True, help_text='结束时间', null=True, verbose_name='结束时间')),
|
||||
('duty_person', models.CharField(help_text='负责人', max_length=64, verbose_name='负责人')),
|
||||
('member', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='项目成员', null=True, verbose_name='项目成员')),
|
||||
('quality_person', models.CharField(help_text='质量保证员', max_length=64, verbose_name='质量保证员')),
|
||||
('vise_person', models.CharField(help_text='质量监督员', max_length=64, verbose_name='质量监督员')),
|
||||
('config_person', models.CharField(help_text='配置管理员', max_length=64, verbose_name='配置管理员')),
|
||||
('security_level', models.CharField(blank=True, help_text='安全等级', max_length=8, null=True, verbose_name='安全等级')),
|
||||
('test_level', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='测试级别', null=True, verbose_name='测试级别')),
|
||||
('plant_type', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='平台类型', null=True, verbose_name='平台类型')),
|
||||
('report_type', models.CharField(blank=True, help_text='报告类型', max_length=64, null=True, verbose_name='报告类型')),
|
||||
('language', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='被测语言', null=True, verbose_name='被测语言')),
|
||||
('standard', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='依据标准', null=True, verbose_name='依据标准')),
|
||||
('entrust_unit', models.CharField(help_text='委托方单位', max_length=64, verbose_name='委托方单位')),
|
||||
('entrust_contact', models.CharField(blank=True, help_text='委托方联系人', max_length=64, null=True, verbose_name='委托方联系人')),
|
||||
('entrust_contact_phone', models.CharField(blank=True, help_text='委托方电话', max_length=64, null=True, verbose_name='委托方电话')),
|
||||
('entrust_email', models.CharField(blank=True, help_text='委托方邮箱', max_length=64, null=True, verbose_name='委托方邮箱')),
|
||||
('dev_unit', models.CharField(help_text='开发方单位', max_length=64, verbose_name='开发方单位')),
|
||||
('dev_contact', models.CharField(blank=True, help_text='研制方联系人', max_length=64, null=True, verbose_name='研制方联系人')),
|
||||
('dev_contact_phone', models.CharField(blank=True, help_text='研制方电话', max_length=64, null=True, verbose_name='研制方电话')),
|
||||
('dev_email', models.CharField(blank=True, help_text='研制方邮箱', max_length=64, null=True, verbose_name='研制方邮箱')),
|
||||
('test_unit', models.CharField(help_text='测试方单位', max_length=64, verbose_name='测试方单位')),
|
||||
('test_contact', models.CharField(blank=True, help_text='测评中心联系人', max_length=64, null=True, verbose_name='测评中心联系人')),
|
||||
('test_contact_phone', models.CharField(blank=True, help_text='测评中心电话', max_length=64, null=True, verbose_name='测评中心电话')),
|
||||
('test_email', models.CharField(blank=True, help_text='测评中心邮箱', max_length=64, null=True, verbose_name='测评中心邮箱')),
|
||||
('step', models.CharField(blank=True, help_text='项目阶段', max_length=8, null=True, verbose_name='项目阶段')),
|
||||
('abbreviation', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='缩略语', null=True, verbose_name='缩略语')),
|
||||
('soft_type', models.SmallIntegerField(choices=[(1, '新研'), (2, '改造'), (3, '沿用')], default=1, verbose_name='软件类型')),
|
||||
('runtime', models.CharField(blank=True, help_text='运行环境', max_length=8, null=True, verbose_name='运行环境')),
|
||||
('devplant', models.CharField(blank=True, help_text='开发环境', max_length=8, null=True, verbose_name='开发环境')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '项目信息',
|
||||
'verbose_name_plural': '项目信息',
|
||||
'db_table': 'project_project',
|
||||
'ordering': ('-create_datetime',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Round',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='轮次标识', max_length=64, null=True, verbose_name='轮次标识')),
|
||||
('name', models.CharField(blank=True, help_text='轮次名称', max_length=64, null=True, verbose_name='轮次名称')),
|
||||
('beginTime', models.DateField(auto_now_add=True, help_text='开始时间', null=True, verbose_name='开始时间')),
|
||||
('endTime', models.DateField(auto_now_add=True, help_text='结束时间', null=True, verbose_name='结束时间')),
|
||||
('speedGrade', models.CharField(blank=True, help_text='速度等级', max_length=64, null=True, verbose_name='速度等级')),
|
||||
('package', models.CharField(blank=True, help_text='封装', max_length=64, null=True, verbose_name='封装')),
|
||||
('grade', models.CharField(blank=True, help_text='等级', max_length=64, null=True, verbose_name='等级')),
|
||||
('best_condition_voltage', models.CharField(blank=True, help_text='最优工况电压', max_length=64, null=True, verbose_name='最优工况电压')),
|
||||
('best_condition_tem', models.CharField(blank=True, help_text='最优工况温度', max_length=64, null=True, verbose_name='最优工况温度')),
|
||||
('typical_condition_voltage', models.CharField(blank=True, help_text='典型工况电压', max_length=64, null=True, verbose_name='典型工况电压')),
|
||||
('typical_condition_tem', models.CharField(blank=True, help_text='典型工况温度', max_length=64, null=True, verbose_name='典型工况温度')),
|
||||
('low_condition_voltage', models.CharField(blank=True, help_text='最低工况电压', max_length=64, null=True, verbose_name='最低工况电压')),
|
||||
('low_condition_tem', models.CharField(blank=True, help_text='最低工况温度', max_length=64, null=True, verbose_name='最低工况温度')),
|
||||
('level', models.CharField(default='0', help_text='树状级别第一级', max_length=15, verbose_name='树状级别第一级')),
|
||||
('key', models.CharField(help_text='给前端的树状级别', max_length=15, verbose_name='给前端的树状级别')),
|
||||
('title', models.CharField(help_text='给前端的name', max_length=15, verbose_name='给前端的name')),
|
||||
('location', models.CharField(help_text='测评执行地点', max_length=30, verbose_name='测评执行地点')),
|
||||
('project', models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='pField', related_query_name='pQuery', to='project.project', verbose_name='归属项目')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '轮次信息',
|
||||
'verbose_name_plural': '轮次信息',
|
||||
'db_table': 'project_round',
|
||||
'ordering': ('key',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TestDemand',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='测试需求标识', max_length=64, null=True, verbose_name='测试需求标识')),
|
||||
('name', models.CharField(blank=True, help_text='测试需求名称', max_length=64, null=True, verbose_name='测试需求名称')),
|
||||
('adequacy', models.CharField(blank=True, help_text='充分条件', max_length=256, null=True, verbose_name='充分条件')),
|
||||
('priority', models.CharField(blank=True, help_text='优先级', max_length=8, null=True, verbose_name='优先级')),
|
||||
('testType', models.CharField(blank=True, default='1', help_text='测试类型', max_length=8, null=True, verbose_name='测试类型')),
|
||||
('testMethod', models.JSONField(blank=True, default=apps.project.models.create_list, help_text='测试方法', verbose_name='测试方法')),
|
||||
('title', models.CharField(blank=True, help_text='树-名称', max_length=64, null=True, verbose_name='树-名称')),
|
||||
('key', models.CharField(blank=True, help_text='round-dut-designkey-testdemand', max_length=64, null=True, verbose_name='round-dut-designkey-testdemand')),
|
||||
('level', models.CharField(blank=True, default=3, help_text='树-level', max_length=64, null=True, verbose_name='树-level')),
|
||||
('design', models.ForeignKey(db_constraint=False, help_text='归属设计需求', on_delete=django.db.models.deletion.CASCADE, related_name='dtField', related_query_name='dtQuery', to='project.design', verbose_name='归属设计需求')),
|
||||
('dut', models.ForeignKey(db_constraint=False, help_text='归属被测件', on_delete=django.db.models.deletion.CASCADE, related_name='dutField', related_query_name='dtQuery', to='project.dut', verbose_name='归属被测件')),
|
||||
('otherDesign', models.ManyToManyField(blank=True, db_constraint=False, related_name='odField', related_query_name='odQuery', to='project.design')),
|
||||
('project', models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='ptField', related_query_name='ptQuery', to='project.project', verbose_name='归属项目')),
|
||||
('round', models.ForeignKey(db_constraint=False, help_text='归属轮次', on_delete=django.db.models.deletion.CASCADE, related_name='rtField', related_query_name='dutQuery', to='project.round', verbose_name='归属轮次')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '核心模型',
|
||||
'verbose_name_plural': '核心模型',
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TestDemandContent',
|
||||
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='显示排序')),
|
||||
('subName', models.CharField(blank=True, max_length=1024, null=True, verbose_name='测试子项名称')),
|
||||
('subDesc', models.CharField(blank=True, max_length=1024, null=True, verbose_name='测试子项描述-对应表格测试项描述')),
|
||||
('condition', models.CharField(blank=True, max_length=1024, null=True, verbose_name='测试子项具体条件')),
|
||||
('operation', models.CharField(blank=True, max_length=3072, null=True, verbose_name='测试子项操作')),
|
||||
('observe', models.CharField(blank=True, max_length=1024, null=True, verbose_name='测试子项观察')),
|
||||
('expect', models.CharField(blank=True, max_length=1024, null=True, verbose_name='期望')),
|
||||
('testDemand', models.ForeignKey(db_constraint=False, help_text='归属的测试项', on_delete=django.db.models.deletion.CASCADE, related_name='testQField', related_query_name='testQField', to='project.testdemand', verbose_name='归属的测试项')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '核心模型',
|
||||
'verbose_name_plural': '核心模型',
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Problem',
|
||||
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='显示排序')),
|
||||
('ident', models.CharField(blank=True, help_text='问题单标识', max_length=64, null=True, verbose_name='问题单标识')),
|
||||
('name', models.CharField(blank=True, help_text='问题单名称', max_length=64, null=True, verbose_name='问题单名称')),
|
||||
('status', models.CharField(blank=True, help_text='缺陷状态', max_length=8, null=True, verbose_name='缺陷状态')),
|
||||
('grade', models.CharField(blank=True, help_text='缺陷等级', max_length=8, null=True, verbose_name='缺陷等级')),
|
||||
('type', models.CharField(blank=True, help_text='缺陷类型', max_length=8, null=True, verbose_name='缺陷类型')),
|
||||
('closeMethod', models.JSONField(blank=True, default=apps.project.models.create_list_1, help_text='闭环方式', null=True, verbose_name='闭环方式')),
|
||||
('operation', tinymce.models.HTMLField(blank=True, help_text='问题描述', null=True, verbose_name='问题描述')),
|
||||
('result', tinymce.models.HTMLField(blank=True, help_text='问题结果/影响', null=True, verbose_name='问题结果/影响')),
|
||||
('postPerson', models.CharField(blank=True, help_text='提出人员', max_length=16, null=True, verbose_name='提出人员')),
|
||||
('postDate', models.DateField(auto_now_add=True, help_text='提单日期', null=True, verbose_name='提单日期')),
|
||||
('designerPerson', models.CharField(blank=True, help_text='开发人员', max_length=16, null=True, verbose_name='开发人员')),
|
||||
('designDate', models.DateField(auto_now_add=True, help_text='确认日期', null=True, verbose_name='确认日期')),
|
||||
('verifyPerson', models.CharField(blank=True, help_text='验证人员', max_length=16, null=True, verbose_name='验证人员')),
|
||||
('verifyDate', models.DateField(auto_now_add=True, help_text='验证日期', null=True, verbose_name='验证日期')),
|
||||
('solve', models.TextField(blank=True, help_text='开发人员填写-改正措施,该字段需要关联“status=1”', null=True, verbose_name='开发人员填写-改正措施')),
|
||||
('analysis', tinymce.models.HTMLField(blank=True, help_text='开发人员填写-原因分析', null=True, verbose_name='开发人员填写-原因分析')),
|
||||
('effect_scope', tinymce.models.HTMLField(blank=True, help_text='开发人员填写-影响域分析', null=True, verbose_name='开发人员填写-影响域分析')),
|
||||
('verify_result', tinymce.models.HTMLField(blank=True, help_text='回归结果', null=True, verbose_name='回归结果')),
|
||||
('case', models.ManyToManyField(db_constraint=False, help_text='归属测试用例-多对多', related_name='caseField', related_query_name='caseQuery', to='project.case', verbose_name='归属测试用例')),
|
||||
('project', models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='projField', related_query_name='projQuery', to='project.project', verbose_name='归属项目')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '问题单',
|
||||
'verbose_name_plural': '问题单',
|
||||
'db_table': 'project_problem',
|
||||
'ordering': ('id',),
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dut',
|
||||
name='project',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='pdField', related_query_name='pdQuery', to='project.project', verbose_name='归属项目'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dut',
|
||||
name='round',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属轮次', on_delete=django.db.models.deletion.CASCADE, related_name='rdField', related_query_name='rdQuery', to='project.round', verbose_name='归属轮次'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='design',
|
||||
name='dut',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属轮次', on_delete=django.db.models.deletion.CASCADE, related_name='rsField', related_query_name='rsQuery', to='project.dut', verbose_name='归属轮次'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='design',
|
||||
name='project',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='psField', related_query_name='psQuery', to='project.project', verbose_name='归属项目'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='design',
|
||||
name='round',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属轮次', on_delete=django.db.models.deletion.CASCADE, related_name='dsField', related_query_name='rsQuery', to='project.round', verbose_name='归属轮次'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CaseStep',
|
||||
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='显示排序')),
|
||||
('operation', tinymce.models.HTMLField(blank=True, help_text='测试步骤-操作', null=True, verbose_name='测试步骤-操作')),
|
||||
('expect', models.CharField(blank=True, help_text='用例预期', max_length=64, null=True, verbose_name='用例预期')),
|
||||
('result', tinymce.models.HTMLField(blank=True, help_text='测试步骤-结果', null=True, verbose_name='测试步骤-结果')),
|
||||
('passed', models.CharField(blank=True, default='3', help_text='是否通过', max_length=8, null=True, verbose_name='是否通过')),
|
||||
('status', models.CharField(blank=True, default='3', help_text='执行状态', max_length=8, null=True, verbose_name='执行状态')),
|
||||
('case', models.ForeignKey(db_constraint=False, help_text='归属的测试用例', on_delete=django.db.models.deletion.CASCADE, related_name='step', related_query_name='stepQ', to='project.case', verbose_name='归属的测试用例')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '核心模型',
|
||||
'verbose_name_plural': '核心模型',
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='design',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属设计需求', on_delete=django.db.models.deletion.CASCADE, related_name='dcField', related_query_name='dcQuery', to='project.design', verbose_name='归属设计需求'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='dut',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属被测件', on_delete=django.db.models.deletion.CASCADE, related_name='ducField', related_query_name='ducQuery', to='project.dut', verbose_name='归属被测件'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='project',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属项目', on_delete=django.db.models.deletion.CASCADE, related_name='pcField', related_query_name='pcQuery', to='project.project', verbose_name='归属项目'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='round',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属轮次', on_delete=django.db.models.deletion.CASCADE, related_name='rcField', related_query_name='rcQuery', to='project.round', verbose_name='归属轮次'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='test',
|
||||
field=models.ForeignKey(db_constraint=False, help_text='归属测试需求', on_delete=django.db.models.deletion.CASCADE, related_name='tcField', related_query_name='tcQuery', to='project.testdemand', verbose_name='归属测试需求'),
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0002_contact_refer_name.py
Normal file
18
apps/project/migrations/0002_contact_refer_name.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-15 16:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contact',
|
||||
name='refer_name',
|
||||
field=models.CharField(blank=True, help_text='公司简称', max_length=32, verbose_name='公司简称'),
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0003_alter_design_protocal.py
Normal file
18
apps/project/migrations/0003_alter_design_protocal.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.13 on 2024-07-23 11:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0002_contact_refer_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='design',
|
||||
name='protocal',
|
||||
field=models.CharField(blank=True, default='', help_text='接口数据', max_length=64, null=True, verbose_name='接口数据'),
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0004_project_secret.py
Normal file
18
apps/project/migrations/0004_project_secret.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.15 on 2024-09-02 14:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0003_alter_design_protocal'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='secret',
|
||||
field=models.CharField(default='1', help_text='密级', max_length=30, verbose_name='密级'),
|
||||
),
|
||||
]
|
||||
17
apps/project/migrations/0005_remove_casestep_status.py
Normal file
17
apps/project/migrations/0005_remove_casestep_status.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.15 on 2024-09-04 17:40
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0004_project_secret'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='casestep',
|
||||
name='status',
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0006_testdemand_testdesciption.py
Normal file
18
apps/project/migrations/0006_testdemand_testdesciption.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.19 on 2025-03-12 13:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0005_remove_casestep_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='testdemand',
|
||||
name='testDesciption',
|
||||
field=models.CharField(blank=True, default='', help_text='老版本-测试项描述', max_length=1024, null=True, verbose_name='测试项描述'),
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0007_alter_round_grade.py
Normal file
18
apps/project/migrations/0007_alter_round_grade.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2 on 2025-04-16 10:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0006_testdemand_testdesciption'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='round',
|
||||
name='grade',
|
||||
field=models.CharField(blank=True, default='1', help_text='等级', max_length=64, null=True, verbose_name='等级'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.2 on 2025-04-16 13:27
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0007_alter_round_grade'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='project',
|
||||
name='device',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='project',
|
||||
name='engin_model',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='project',
|
||||
name='section_system',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='project',
|
||||
name='sub_system',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.2 on 2025-04-16 15:34
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0008_remove_project_device_remove_project_engin_model_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='round',
|
||||
name='package',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='round',
|
||||
name='speedGrade',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,30 @@
|
||||
# Generated by Django 5.2 on 2025-04-16 15:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0009_remove_round_package_remove_round_speedgrade'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='testdemandcontent',
|
||||
name='condition',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='testdemandcontent',
|
||||
name='observe',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='testdemandcontent',
|
||||
name='subDesc',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='testdemand',
|
||||
name='testDesciption',
|
||||
field=models.CharField(blank=True, default='', help_text='测试项描述', max_length=1024, null=True, verbose_name='测试项描述'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
# Generated by Django 5.2 on 2025-04-17 14:57
|
||||
|
||||
import django.db.models.deletion
|
||||
import shortuuidfield.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0010_remove_testdemandcontent_condition_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='testdemandcontent',
|
||||
name='expect',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='testdemandcontent',
|
||||
name='operation',
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TestDemandContentStep',
|
||||
fields=[
|
||||
('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='显示排序')),
|
||||
('id', shortuuidfield.fields.ShortUUIDField(blank=True, editable=False, help_text='Id', max_length=22, primary_key=True, serialize=False, verbose_name='Id')),
|
||||
('operation', models.CharField(blank=True, max_length=3072, null=True, verbose_name='测试子项操作')),
|
||||
('expect', models.CharField(blank=True, max_length=1024, null=True, verbose_name='期望')),
|
||||
('testDemandContent', models.ForeignKey(db_constraint=False, help_text='归属的测试项', on_delete=django.db.models.deletion.CASCADE, related_name='testStepField', related_query_name='testStepField', to='project.testdemandcontent', verbose_name='归属的测试项')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '核心模型',
|
||||
'verbose_name_plural': '核心模型',
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0012_alter_project_ident.py
Normal file
18
apps/project/migrations/0012_alter_project_ident.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2 on 2025-04-23 09:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0011_remove_testdemandcontent_expect_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='ident',
|
||||
field=models.CharField(blank=True, help_text='项目标识', max_length=64, null=True, unique=True, verbose_name='项目标识'),
|
||||
),
|
||||
]
|
||||
19
apps/project/migrations/0013_case_timing_diagram.py
Normal file
19
apps/project/migrations/0013_case_timing_diagram.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.2 on 2025-04-24 11:01
|
||||
|
||||
import tinymce.models
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0012_alter_project_ident'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='case',
|
||||
name='timing_diagram',
|
||||
field=tinymce.models.HTMLField(blank=True, help_text='FPGA时序图', null=True, verbose_name='FPGA时序图'),
|
||||
),
|
||||
]
|
||||
18
apps/project/migrations/0014_alter_casestep_expect.py
Normal file
18
apps/project/migrations/0014_alter_casestep_expect.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2 on 2025-04-27 10:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0013_case_timing_diagram'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='casestep',
|
||||
name='expect',
|
||||
field=models.CharField(blank=True, help_text='用例预期', max_length=3072, null=True, verbose_name='用例预期'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,44 @@
|
||||
# Generated by Django 5.2 on 2025-04-28 10:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0014_alter_casestep_expect'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='dut',
|
||||
name='black_line',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dut',
|
||||
name='code_line',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dut',
|
||||
name='comment_line',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='dut',
|
||||
name='mix_line',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dut',
|
||||
name='comment_lines',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='注释行数'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dut',
|
||||
name='effective_lines',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='有效代码行数'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='dut',
|
||||
name='total_lines',
|
||||
field=models.CharField(blank=True, max_length=64, null=True, verbose_name='总行数'),
|
||||
),
|
||||
]
|
||||
29
apps/project/migrations/0016_dutmetrics.py
Normal file
29
apps/project/migrations/0016_dutmetrics.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 5.2 on 2025-04-28 15:25
|
||||
|
||||
import django.db.models.deletion
|
||||
import shortuuidfield.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('project', '0015_remove_dut_black_line_remove_dut_code_line_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DutMetrics',
|
||||
fields=[
|
||||
('id', shortuuidfield.fields.ShortUUIDField(blank=True, editable=False, help_text='id', max_length=22, primary_key=True, serialize=False, verbose_name='id')),
|
||||
('avg_function_lines', models.IntegerField(verbose_name='平均模块大小')),
|
||||
('avg_cyclomatic', models.IntegerField(verbose_name='平均圈复杂度')),
|
||||
('avg_fan_out', models.IntegerField(verbose_name='平均扇出数')),
|
||||
('function_count', models.IntegerField(verbose_name='模块数量')),
|
||||
('max_cyclomatic', models.IntegerField(verbose_name='最大圈复杂度')),
|
||||
('high_cyclomatic_ratio', models.IntegerField(verbose_name='圈复杂度>20模块占比')),
|
||||
('total_blanks', models.IntegerField(verbose_name='空行数')),
|
||||
('dut', models.OneToOneField(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, related_name='metrics', related_query_name='metrics', to='project.dut', verbose_name='归属源代码被测件')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
apps/project/migrations/__init__.py
Normal file
0
apps/project/migrations/__init__.py
Normal file
BIN
apps/project/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
BIN
apps/project/migrations/__pycache__/0001_initial.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
BIN
apps/project/migrations/__pycache__/0001_initial.cpython-38.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
apps/project/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/project/migrations/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/project/migrations/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
402
apps/project/models.py
Normal file
402
apps/project/models.py
Normal file
@@ -0,0 +1,402 @@
|
||||
from django.db import models
|
||||
from utils.models import CoreModel
|
||||
from tinymce.models import HTMLField
|
||||
from shortuuidfield import ShortUUIDField
|
||||
|
||||
def create_list():
|
||||
return []
|
||||
|
||||
def create_list_1():
|
||||
return ['1']
|
||||
|
||||
class Project(CoreModel):
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="项目标识",
|
||||
help_text="项目标识", unique=True) # 唯一
|
||||
name = models.CharField(max_length=100, blank=True, null=True, verbose_name="项目名称", help_text="项目名称")
|
||||
beginTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="开始时间",
|
||||
verbose_name="开始时间")
|
||||
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间", verbose_name="结束时间")
|
||||
duty_person = models.CharField(max_length=64, verbose_name="负责人", help_text="负责人")
|
||||
member = models.JSONField(null=True, blank=True, help_text="项目成员", verbose_name="项目成员", default=create_list)
|
||||
# 8月新增字段
|
||||
quality_person = models.CharField(max_length=64, verbose_name="质量保证员", help_text="质量保证员")
|
||||
vise_person = models.CharField(max_length=64, verbose_name="质量监督员", help_text="质量监督员")
|
||||
config_person = models.CharField(max_length=64, verbose_name="配置管理员", help_text="配置管理员")
|
||||
# ~~~~~~~~~~~
|
||||
security_level = models.CharField(max_length=8, blank=True, null=True, verbose_name="安全等级",
|
||||
help_text="安全等级")
|
||||
test_level = models.JSONField(null=True, blank=True, help_text="测试级别", verbose_name="测试级别",
|
||||
default=create_list)
|
||||
plant_type = models.JSONField(null=True, blank=True, help_text="平台类型", verbose_name="平台类型",
|
||||
default=create_list)
|
||||
report_type = models.CharField(max_length=64, blank=True, null=True, verbose_name="报告类型", help_text="报告类型")
|
||||
language = models.JSONField(null=True, blank=True, help_text="被测语言", verbose_name="被测语言",
|
||||
default=create_list)
|
||||
standard = models.JSONField(null=True, blank=True, help_text="依据标准", verbose_name="依据标准",
|
||||
default=create_list)
|
||||
entrust_unit = models.CharField(max_length=64, verbose_name="委托方单位", help_text="委托方单位")
|
||||
entrust_contact = models.CharField(max_length=64, blank=True, null=True, verbose_name="委托方联系人",
|
||||
help_text="委托方联系人")
|
||||
entrust_contact_phone = models.CharField(max_length=64, blank=True, null=True, verbose_name="委托方电话",
|
||||
help_text="委托方电话")
|
||||
entrust_email = models.CharField(max_length=64, blank=True, null=True, verbose_name="委托方邮箱",
|
||||
help_text="委托方邮箱")
|
||||
dev_unit = models.CharField(max_length=64, verbose_name="开发方单位", help_text="开发方单位")
|
||||
dev_contact = models.CharField(max_length=64, blank=True, null=True, verbose_name="研制方联系人",
|
||||
help_text="研制方联系人")
|
||||
dev_contact_phone = models.CharField(max_length=64, blank=True, null=True, verbose_name="研制方电话",
|
||||
help_text="研制方电话")
|
||||
dev_email = models.CharField(max_length=64, blank=True, null=True, verbose_name="研制方邮箱",
|
||||
help_text="研制方邮箱")
|
||||
test_unit = models.CharField(max_length=64, verbose_name="测试方单位", help_text="测试方单位")
|
||||
test_contact = models.CharField(max_length=64, blank=True, null=True, verbose_name="测评中心联系人",
|
||||
help_text="测评中心联系人")
|
||||
test_contact_phone = models.CharField(max_length=64, blank=True, null=True, verbose_name="测评中心电话",
|
||||
help_text="测评中心电话")
|
||||
test_email = models.CharField(max_length=64, blank=True, null=True, verbose_name="测评中心邮箱",
|
||||
help_text="测评中心邮箱")
|
||||
step = models.CharField(max_length=8, blank=True, null=True, verbose_name="项目阶段", help_text="项目阶段")
|
||||
abbreviation = models.JSONField(null=True, blank=True, help_text="缩略语", verbose_name="缩略语",
|
||||
default=create_list)
|
||||
soft_type = models.SmallIntegerField(verbose_name='软件类型', choices=((1, '新研'), (2, '改造'), (3, '沿用')),
|
||||
default=1)
|
||||
runtime = models.CharField(max_length=8, blank=True, null=True, verbose_name="运行环境",
|
||||
help_text="运行环境")
|
||||
devplant = models.CharField(max_length=8, blank=True, null=True, verbose_name="开发环境",
|
||||
help_text="开发环境")
|
||||
# 9月2日新增字段:密级
|
||||
secret = models.CharField(max_length=30, default='1', verbose_name='密级', help_text='密级')
|
||||
|
||||
def __str__(self):
|
||||
return f'项目{self.ident}-{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_project'
|
||||
verbose_name = "项目信息"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('-create_datetime',)
|
||||
|
||||
class Round(CoreModel):
|
||||
objects = models.Manager()
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="轮次标识",
|
||||
help_text="轮次标识") # 后面加上unique=True
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="轮次名称",
|
||||
help_text="轮次名称")
|
||||
beginTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="开始时间",
|
||||
verbose_name="开始时间")
|
||||
endTime = models.DateField(auto_now_add=True, null=True, blank=True, help_text="结束时间", verbose_name="结束时间")
|
||||
grade = models.CharField(max_length=64, blank=True, null=True, verbose_name="等级", help_text="等级", default='1')
|
||||
best_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="最优工况电压",
|
||||
help_text="最优工况电压")
|
||||
best_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="最优工况温度",
|
||||
help_text="最优工况温度")
|
||||
typical_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="典型工况电压",
|
||||
help_text="典型工况电压")
|
||||
typical_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="典型工况温度",
|
||||
help_text="典型工况温度")
|
||||
low_condition_voltage = models.CharField(max_length=64, blank=True, null=True, verbose_name="最低工况电压",
|
||||
help_text="最低工况电压")
|
||||
low_condition_tem = models.CharField(max_length=64, blank=True, null=True, verbose_name="最低工况温度",
|
||||
help_text="最低工况温度")
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='pQuery')
|
||||
level = models.CharField(max_length=15, verbose_name='树状级别第一级', help_text="树状级别第一级", default='0')
|
||||
key = models.CharField(max_length=15, verbose_name='给前端的树状级别', help_text="给前端的树状级别")
|
||||
title = models.CharField(max_length=15, verbose_name='给前端的name', help_text="给前端的name")
|
||||
# 新增执行地点
|
||||
location = models.CharField(max_length=30, verbose_name='测评执行地点', help_text='测评执行地点')
|
||||
|
||||
def __str__(self):
|
||||
return f'第{str(int(self.key) + 1)}轮次'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_round'
|
||||
verbose_name = "轮次信息"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('key',)
|
||||
|
||||
class Dut(CoreModel):
|
||||
objects = models.Manager()
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="被测件标识",
|
||||
help_text="被测件标识") # 后面加上unique=True
|
||||
type = models.CharField(max_length=16, blank=True, null=True, verbose_name="被测件类型", help_text="被测件类型")
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="被测件名称", help_text="被测件名称")
|
||||
# 2025年4月28日更新,分为总函数、有效代码行数、注释行数
|
||||
total_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='总行数')
|
||||
effective_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='有效代码行数')
|
||||
comment_lines = models.CharField(max_length=64, blank=True, null=True, verbose_name='注释行数')
|
||||
|
||||
# 更新结束
|
||||
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
|
||||
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-key", help_text="树-key")
|
||||
# 被测件添加版本、发布单位、发布时间
|
||||
version = models.CharField(max_length=64, blank=True, null=True, verbose_name="发布版本", help_text="发布版本")
|
||||
release_union = models.CharField(max_length=64, blank=True, null=True, verbose_name="发布版本",
|
||||
help_text="发布版本")
|
||||
release_date = models.DateField(auto_now_add=True, null=True, blank=True, help_text="发布时间",
|
||||
verbose_name="发布时间")
|
||||
# 新增用户文档的编号
|
||||
ref = models.CharField(max_length=32, blank=True, null=True, verbose_name="文档编号", help_text="文档编号")
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
|
||||
default=1) # 默认为1
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pdField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='pdQuery')
|
||||
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rdField", on_delete=models.CASCADE,
|
||||
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rdQuery')
|
||||
|
||||
def __str__(self):
|
||||
return f'被测件:{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_dut'
|
||||
verbose_name = "被测件信息"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('key',)
|
||||
|
||||
class DutMetrics(models.Model):
|
||||
objects = models.Manager()
|
||||
id = ShortUUIDField(primary_key=True, help_text="id", verbose_name="id")
|
||||
# 外键Dut,一个Dut储存一个指标
|
||||
dut = models.OneToOneField(Dut, on_delete=models.CASCADE, related_name='metrics', related_query_name='metrics',
|
||||
db_constraint=False, verbose_name='归属源代码被测件')
|
||||
avg_function_lines = models.IntegerField(verbose_name='平均模块大小')
|
||||
avg_cyclomatic = models.IntegerField(verbose_name='平均圈复杂度')
|
||||
avg_fan_out = models.IntegerField(verbose_name='平均扇出数')
|
||||
function_count = models.IntegerField(verbose_name='模块数量')
|
||||
max_cyclomatic = models.IntegerField(verbose_name='最大圈复杂度')
|
||||
high_cyclomatic_ratio = models.IntegerField(verbose_name='圈复杂度>20模块占比')
|
||||
total_blanks = models.IntegerField(verbose_name='空行数')
|
||||
|
||||
class Design(CoreModel):
|
||||
objects = models.Manager()
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="设计需求标识",
|
||||
help_text="设计需求标识")
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="设计需求名称", help_text="设计需求名称")
|
||||
demandType = models.CharField(max_length=8, blank=True, null=True, verbose_name="设计需求类型",
|
||||
help_text="设计需求类型")
|
||||
description = HTMLField(blank=True, null=True, verbose_name="设计需求描述", help_text="设计需求描述")
|
||||
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
|
||||
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey",
|
||||
help_text="round-dut-designkey")
|
||||
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
|
||||
default=2) # 默认为2
|
||||
chapter = models.CharField(max_length=64, blank=True, verbose_name="设计需求章节号", help_text="设计需求章节号")
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="psField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='psQuery')
|
||||
round = models.ForeignKey(to="Round", db_constraint=False, related_name="dsField", on_delete=models.CASCADE,
|
||||
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rsQuery')
|
||||
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="rsField", on_delete=models.CASCADE,
|
||||
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rsQuery')
|
||||
# 如果是demandTye='3'则加上如下字段
|
||||
source = models.CharField(max_length=64, blank=True, null=True, default='', verbose_name='接口来源',
|
||||
help_text='接口来源')
|
||||
to = models.CharField(max_length=64, blank=True, null=True, default='', verbose_name='接口目的地',
|
||||
help_text='接口目的地')
|
||||
type = models.CharField(max_length=64, blank=True, null=True, default='', verbose_name='接口类型',
|
||||
help_text='接口类型')
|
||||
# 注意:该字段改为接口数据
|
||||
protocal = models.CharField(max_length=64, blank=True, null=True, default='', verbose_name='接口数据',
|
||||
help_text='接口数据')
|
||||
|
||||
def __str__(self):
|
||||
return f'设计需求:{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_design'
|
||||
verbose_name = "测试需求"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('key',)
|
||||
|
||||
class TestDemand(CoreModel):
|
||||
objects = models.Manager()
|
||||
"""测试项"""
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="测试需求标识",
|
||||
help_text="测试需求标识")
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="测试需求名称", help_text="测试需求名称")
|
||||
adequacy = models.CharField(max_length=256, blank=True, null=True, verbose_name="充分条件", help_text="充分条件")
|
||||
priority = models.CharField(max_length=8, blank=True, null=True, verbose_name="优先级", help_text="优先级")
|
||||
testType = models.CharField(max_length=8, null=True, blank=True, help_text="测试类型", verbose_name="测试类型",
|
||||
default="1")
|
||||
testMethod = models.JSONField(blank=True, help_text="测试方法", verbose_name="测试方法", default=create_list)
|
||||
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
|
||||
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey-testdemand",
|
||||
help_text="round-dut-designkey-testdemand")
|
||||
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
|
||||
default=3) # 默认为3
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="ptField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='ptQuery')
|
||||
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rtField", on_delete=models.CASCADE,
|
||||
verbose_name='归属轮次', help_text='归属轮次', related_query_name='dutQuery')
|
||||
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="dutField", on_delete=models.CASCADE,
|
||||
verbose_name='归属被测件', help_text='归属被测件', related_query_name='dtQuery')
|
||||
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dtField", on_delete=models.CASCADE,
|
||||
verbose_name='归属设计需求', help_text='归属设计需求', related_query_name='dtQuery')
|
||||
otherDesign = models.ManyToManyField(to="Design", db_constraint=False, related_name="odField",
|
||||
related_query_name='odQuery', blank=True)
|
||||
# 新模版要求:测试项描述对整个测试项进行描述
|
||||
testDesciption = models.CharField(max_length=1024, blank=True, null=True, verbose_name='测试项描述', default="",
|
||||
help_text='测试项描述')
|
||||
|
||||
def __str__(self):
|
||||
return f'测试项:{self.name}'
|
||||
|
||||
class TestDemandContent(CoreModel):
|
||||
objects = models.Manager()
|
||||
"""测试方法中的测试子项内容"""
|
||||
testDemand = models.ForeignKey(to="TestDemand", db_constraint=False, related_name="testQField",
|
||||
on_delete=models.CASCADE, verbose_name='归属的测试项', help_text='归属的测试项',
|
||||
related_query_name='testQField')
|
||||
# 2025年4月16日去掉subDesc、condition、observe
|
||||
# 4月17日修改:因为新增步骤所以把operation和expect弄到下面Model里面了,新增字段
|
||||
subName = models.CharField(max_length=1024, blank=True, null=True, verbose_name='测试子项名称')
|
||||
|
||||
def __str__(self):
|
||||
return f'测试子项:{self.subName}'
|
||||
|
||||
# 4月17日新增:因为测试项需要测试子项step
|
||||
class TestDemandContentStep(CoreModel):
|
||||
objects = models.Manager()
|
||||
id = ShortUUIDField(primary_key=True, help_text="Id", verbose_name="Id")
|
||||
operation = models.CharField(max_length=3072, blank=True, null=True, verbose_name='测试子项操作')
|
||||
expect = models.CharField(max_length=1024, blank=True, null=True, verbose_name='期望')
|
||||
testDemandContent = models.ForeignKey(to="TestDemandContent", db_constraint=False, related_name="testStepField",
|
||||
on_delete=models.CASCADE, verbose_name='归属的测试项',
|
||||
help_text='归属的测试项',
|
||||
related_query_name='testStepField')
|
||||
|
||||
class Case(CoreModel):
|
||||
objects = models.Manager()
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例标识", help_text="用例标识")
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="用例名称", help_text="用例名称")
|
||||
initialization = models.CharField(max_length=128, blank=True, null=True, verbose_name="初始条件",
|
||||
help_text="初始化条件")
|
||||
premise = models.CharField(max_length=128, blank=True, null=True, verbose_name="前提和约束", help_text="前提和约束")
|
||||
summarize = models.CharField(max_length=256, blank=True, null=True, verbose_name="用例综述", help_text="用例综述")
|
||||
designPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="设计人员", help_text="设计人员")
|
||||
testPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="测试人员", help_text="测试人员")
|
||||
monitorPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="审核人员",
|
||||
help_text="审核人员")
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="pcField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='pcQuery')
|
||||
isLeaf = models.BooleanField(default=True, verbose_name="树状图最后一个节点", help_text="树状图最后一个节点")
|
||||
round = models.ForeignKey(to="Round", db_constraint=False, related_name="rcField", on_delete=models.CASCADE,
|
||||
verbose_name='归属轮次', help_text='归属轮次', related_query_name='rcQuery')
|
||||
dut = models.ForeignKey(to="Dut", db_constraint=False, related_name="ducField", on_delete=models.CASCADE,
|
||||
verbose_name='归属被测件', help_text='归属被测件', related_query_name='ducQuery')
|
||||
design = models.ForeignKey(to="Design", db_constraint=False, related_name="dcField", on_delete=models.CASCADE,
|
||||
verbose_name='归属设计需求', help_text='归属设计需求', related_query_name='dcQuery')
|
||||
test = models.ForeignKey(to="TestDemand", db_constraint=False, related_name="tcField", on_delete=models.CASCADE,
|
||||
verbose_name='归属测试需求', help_text='归属测试需求', related_query_name='tcQuery')
|
||||
title = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-名称", help_text="树-名称")
|
||||
key = models.CharField(max_length=64, blank=True, null=True, verbose_name="round-dut-designkey-testdemand-case",
|
||||
help_text="round-dut-designkey-testdemand-case")
|
||||
level = models.CharField(max_length=64, blank=True, null=True, verbose_name="树-level", help_text="树-level",
|
||||
default=4) # 默认为4
|
||||
# 2024年5月31日新增属性:执行时间
|
||||
exe_time = models.DateField(blank=True, null=True, verbose_name='执行时间', help_text='执行时间')
|
||||
# 2025年4月24日新增属性:FPGA的时序图
|
||||
timing_diagram = HTMLField(blank=True, null=True, verbose_name="FPGA时序图", help_text="FPGA时序图")
|
||||
|
||||
def __str__(self):
|
||||
return f'测试用例:{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_case'
|
||||
verbose_name = "测试用例"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('key',)
|
||||
|
||||
class CaseStep(CoreModel):
|
||||
objects = models.Manager()
|
||||
operation = HTMLField(blank=True, null=True, verbose_name="测试步骤-操作", help_text="测试步骤-操作")
|
||||
expect = models.CharField(max_length=3072, blank=True, null=True, verbose_name="用例预期", help_text="用例预期")
|
||||
result = HTMLField(blank=True, null=True, verbose_name="测试步骤-结果", help_text="测试步骤-结果")
|
||||
passed = models.CharField(max_length=8, null=True, blank=True, help_text="是否通过", verbose_name="是否通过",
|
||||
default="3")
|
||||
# status = models.CharField(max_length=8, null=True, blank=True, help_text="执行状态", verbose_name="执行状态",
|
||||
# default="3")
|
||||
case = models.ForeignKey(to="Case", db_constraint=False, related_name="step",
|
||||
on_delete=models.CASCADE, verbose_name='归属的测试用例', help_text='归属的测试用例',
|
||||
related_query_name='stepQ')
|
||||
|
||||
def __str__(self):
|
||||
return f'测试用例步骤'
|
||||
|
||||
class Problem(CoreModel):
|
||||
objects = models.Manager()
|
||||
# ident为PT_RXXXX_ident,这里需要根据测试项类型进行排序处理
|
||||
ident = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单标识", help_text="问题单标识")
|
||||
name = models.CharField(max_length=64, blank=True, null=True, verbose_name="问题单名称", help_text="问题单名称")
|
||||
# 问题状态1-已闭环 2-开放 3-推迟 4-撤销
|
||||
status = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷状态", help_text="缺陷状态")
|
||||
# 问题等级1-一般 2-严重 3-建议 4-重大
|
||||
grade = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷等级", help_text="缺陷等级")
|
||||
# 问题类型1-其他问题 2-文档问题 3-程序问题 4-设计问题 5-需求问题 6-数据问题
|
||||
type = models.CharField(max_length=8, blank=True, null=True, verbose_name="缺陷类型", help_text="缺陷类型")
|
||||
closeMethod = models.JSONField(null=True, blank=True, help_text="闭环方式", verbose_name="闭环方式",
|
||||
default=create_list_1)
|
||||
operation = HTMLField(blank=True, null=True, verbose_name="问题描述", help_text="问题描述")
|
||||
result = HTMLField(blank=True, null=True, verbose_name="问题结果/影响", help_text="问题结果/影响")
|
||||
postPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="提出人员", help_text="提出人员")
|
||||
postDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="提单日期", verbose_name="提单日期")
|
||||
designerPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="开发人员",
|
||||
help_text="开发人员")
|
||||
designDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="确认日期",
|
||||
verbose_name="确认日期")
|
||||
verifyPerson = models.CharField(max_length=16, blank=True, null=True, verbose_name="验证人员", help_text="验证人员")
|
||||
verifyDate = models.DateField(auto_now_add=True, null=True, blank=True, help_text="验证日期",
|
||||
verbose_name="验证日期")
|
||||
project = models.ForeignKey(to="Project", db_constraint=False, related_name="projField", on_delete=models.CASCADE,
|
||||
verbose_name='归属项目', help_text='归属项目', related_query_name='projQuery')
|
||||
case = models.ManyToManyField(to="Case", db_constraint=False, related_name="caseField", verbose_name='归属测试用例',
|
||||
help_text='归属测试用例-多对多', related_query_name='caseQuery')
|
||||
solve = models.TextField(verbose_name='开发人员填写-改正措施',
|
||||
help_text='开发人员填写-改正措施,该字段需要关联“status=1”', blank=True, null=True)
|
||||
analysis = HTMLField(blank=True, null=True, verbose_name="开发人员填写-原因分析", help_text="开发人员填写-原因分析")
|
||||
effect_scope = HTMLField(blank=True, null=True, verbose_name="开发人员填写-影响域分析",
|
||||
help_text="开发人员填写-影响域分析")
|
||||
verify_result = HTMLField(blank=True, null=True, verbose_name="回归结果", help_text="回归结果")
|
||||
|
||||
def __str__(self):
|
||||
return f'问题单:{self.ident}-{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_problem'
|
||||
verbose_name = "问题单"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('id',)
|
||||
|
||||
# 单位信息
|
||||
class Contact(CoreModel):
|
||||
objects = models.Manager()
|
||||
entrust_person = models.CharField(max_length=16, blank=True, verbose_name="法人", help_text="法人")
|
||||
name = models.CharField(max_length=64, blank=True, verbose_name="公司名称", help_text="公司名称")
|
||||
key = models.IntegerField(auto_created=True, verbose_name="公司编号", help_text="公司编号")
|
||||
# 新增地址
|
||||
addr = models.CharField(max_length=64, blank=True, verbose_name="公司地址", help_text="公司地址")
|
||||
# 新增简称
|
||||
refer_name = models.CharField(max_length=32, blank=True, verbose_name='公司简称', help_text='公司简称')
|
||||
|
||||
def __str__(self):
|
||||
return f'联系方式:{self.name}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'contact_gongsi'
|
||||
verbose_name = '委托方、研制方、测试方信息'
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ('create_datetime',)
|
||||
|
||||
# ~~~~~2024年2月27日新增~~~~~
|
||||
class Abbreviation(models.Model):
|
||||
objects = models.Manager()
|
||||
title = models.CharField(max_length=64, verbose_name="缩略语", help_text="缩略语")
|
||||
des = models.CharField(max_length=256, verbose_name="描述", help_text="描述")
|
||||
|
||||
def __str__(self):
|
||||
return f'缩略语:{self.title}'
|
||||
|
||||
class Meta:
|
||||
db_table = 'project_abbreviation'
|
||||
verbose_name = '缩略语和行业词汇'
|
||||
verbose_name_plural = '缩略语和行业词汇'
|
||||
0
apps/project/schemas/__init__.py
Normal file
0
apps/project/schemas/__init__.py
Normal file
BIN
apps/project/schemas/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/case.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/case.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/case.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/case.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/design.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/design.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/design.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/design.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/dut.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/dut.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/dut.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/dut.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/problem.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/problem.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/problem.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/problem.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/project.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/project.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/project.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/project.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/round.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/round.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/round.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/round.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/testDemand.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/testDemand.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/testDemand.cpython-38.pyc
Normal file
BIN
apps/project/schemas/__pycache__/testDemand.cpython-38.pyc
Normal file
Binary file not shown.
BIN
apps/project/schemas/__pycache__/treeOperation.cpython-313.pyc
Normal file
BIN
apps/project/schemas/__pycache__/treeOperation.cpython-313.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user