2025-04-29 18:09:00 +08:00
|
|
|
|
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)
|
2025-12-23 10:36:55 +08:00
|
|
|
|
# 组装查询条件
|
|
|
|
|
|
query_params = {
|
|
|
|
|
|
"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
|
|
|
|
|
|
}
|
|
|
|
|
|
# 如果没有多个key传递则是汇总界面
|
|
|
|
|
|
if data.dut_id and data.design_id and data.test_id and data.case_id:
|
|
|
|
|
|
case_key = "".join(
|
|
|
|
|
|
[data.round_id, '-', data.dut_id, '-', data.design_id, '-', data.test_id, '-', data.case_id])
|
|
|
|
|
|
query_params['case__key'] = case_key
|
|
|
|
|
|
else:
|
|
|
|
|
|
query_params['case__round__key'] = data.round_id
|
|
|
|
|
|
qs = Problem.objects.filter(**query_params).order_by("id")
|
2025-04-29 18:09:00 +08:00
|
|
|
|
|
|
|
|
|
|
# 遍历通过代码不通过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
|
2025-12-23 10:36:55 +08:00
|
|
|
|
conditionNoneToBlank(data)
|
2025-04-29 18:09:00 +08:00
|
|
|
|
# 先查询当前项目
|
|
|
|
|
|
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()
|