add flowchart list

This commit is contained in:
2025-10-20 20:05:10 +08:00
parent 6465f8fab2
commit a103dd6b38
4 changed files with 156 additions and 52 deletions

View File

@@ -17,19 +17,19 @@ from utils.shortcuts import get_env
from account.models import User from account.models import User
from problem.models import Problem from problem.models import Problem
from submission.models import Submission, JudgeStatus from submission.models import Submission, JudgeStatus
from flowchart.models import FlowchartSubmission, FlowchartSubmissionStatus
from account.decorators import login_required from account.decorators import login_required
from ai.models import AIAnalysis from ai.models import AIAnalysis
CACHE_TIMEOUT = 300 CACHE_TIMEOUT = 300
DIFFICULTY_MAP = {"Low": "简单", "Mid": "中等", "High": "困难"} DIFFICULTY_MAP = {"Low": "简单", "Mid": "中等", "High": "困难"}
DEFAULT_CLASS_SIZE = 45 DEFAULT_CLASS_SIZE = 45
# 评级阈值配置:(百分位上限, 评级) # 评级阈值配置:(百分位上限, 评级)
GRADE_THRESHOLDS = [ GRADE_THRESHOLDS = [
(10, "S"), # 前10%: S级 - 卓越 (10, "S"), # 前10%: S级 - 卓越
(35, "A"), # 前35%: A级 - 优秀 (35, "A"), # 前35%: A级 - 优秀
(75, "B"), # 前75%: B级 - 良好 (75, "B"), # 前75%: B级 - 良好
(100, "C"), # 其余: C级 - 及格 (100, "C"), # 其余: C级 - 及格
] ]
@@ -163,6 +163,7 @@ class AIDetailDataAPI(APIView):
"start": start, "start": start,
"end": end, "end": end,
"solved": [], "solved": [],
"flowcharts": [],
"grade": "", "grade": "",
"tags": {}, "tags": {},
"difficulty": {}, "difficulty": {},
@@ -179,9 +180,79 @@ class AIDetailDataAPI(APIView):
solved, contest_ids = self._build_solved_records( solved, contest_ids = self._build_solved_records(
user_first_ac, by_problem, problems, user.id user_first_ac, by_problem, problems, user.id
) )
# 查找 flowchart submissions
flowcharts_query = FlowchartSubmission.objects.filter(
user_id=user,
status=FlowchartSubmissionStatus.COMPLETED,
)
# 添加时间范围过滤
if start:
flowcharts_query = flowcharts_query.filter(create_time__gte=start)
if end:
flowcharts_query = flowcharts_query.filter(create_time__lte=end)
flowcharts = flowcharts_query.select_related("problem").only(
"id",
"create_time",
"ai_score",
"ai_grade",
"problem___id",
"problem__title",
)
# 按problem分组
problem_groups = defaultdict(list)
for flowchart in flowcharts:
problem_id = flowchart.problem._id
problem_groups[problem_id].append(flowchart)
flowcharts_data = []
for problem_id, submissions in problem_groups.items():
if not submissions:
continue
# 获取第一个提交的基本信息
first_submission = submissions[0]
# 计算统计数据
scores = [s.ai_score for s in submissions if s.ai_score is not None]
times = [s.create_time for s in submissions]
# 找到最高分和对应的等级
best_score = max(scores) if scores else 0
best_submission = next(
(s for s in submissions if s.ai_score == best_score), submissions[0]
)
best_grade = best_submission.ai_grade or ""
# 计算平均分
avg_score = sum(scores) / len(scores) if scores else 0
# 最新提交时间
latest_time = max(times) if times else first_submission.create_time
merged_item = {
"problem__id": problem_id,
"problem_title": first_submission.problem.title,
"submission_count": len(submissions),
"best_score": best_score,
"best_grade": best_grade,
"latest_submission_time": latest_time.isoformat() if latest_time else None,
"avg_score": round(avg_score, 0),
}
flowcharts_data.append(merged_item)
# 按最新提交时间排序
flowcharts_data.sort(
key=lambda x: x["latest_submission_time"] or "", reverse=True
)
result.update( result.update(
{ {
"solved": solved, "solved": solved,
"flowcharts": flowcharts_data,
"grade": self._calculate_average_grade(solved), "grade": self._calculate_average_grade(solved),
"tags": self._calculate_top_tags(problems.values()), "tags": self._calculate_top_tags(problems.values()),
"difficulty": self._calculate_difficulty_distribution( "difficulty": self._calculate_difficulty_distribution(
@@ -572,9 +643,10 @@ class AIHeatmapDataAPI(APIView):
submission_count = submission_dict.get(day_date, 0) submission_count = submission_dict.get(day_date, 0)
heatmap_data.append( heatmap_data.append(
{ {
"timestamp": int(datetime.combine( "timestamp": int(
day_date, datetime.min.time() datetime.combine(day_date, datetime.min.time()).timestamp()
).timestamp() * 1000), * 1000
),
"value": submission_count, "value": submission_count,
} }
) )

View File

@@ -41,14 +41,15 @@ class FlowchartSubmissionListSerializer(serializers.ModelSerializer):
"""用于列表显示的简化序列化器""" """用于列表显示的简化序列化器"""
username = serializers.CharField(source="user.username") username = serializers.CharField(source="user.username")
problem = serializers.CharField(source="problem._id")
problem_title = serializers.CharField(source="problem.title") problem_title = serializers.CharField(source="problem.title")
class Meta: class Meta:
model = FlowchartSubmission model = FlowchartSubmission
fields = [ fields = [
"id", "id",
"username", "username",
"problem_title", "problem_title",
"problem",
"status", "status",
"create_time", "create_time",
"ai_score", "ai_score",
@@ -58,3 +59,33 @@ class FlowchartSubmissionListSerializer(serializers.ModelSerializer):
"processing_time", "processing_time",
"evaluation_time", "evaluation_time",
] ]
class FlowchartSubmissionSummarySerializer(serializers.ModelSerializer):
"""用于AI详情页面的极简序列化器只包含必要字段"""
problem_title = serializers.CharField(source="problem.title")
problem__id = serializers.CharField(source="problem._id")
class Meta:
model = FlowchartSubmission
fields = [
"id",
"problem__id",
"problem_title",
"ai_score",
"ai_grade",
"create_time",
]
class FlowchartSubmissionMergedSerializer(serializers.Serializer):
"""合并后的流程图提交序列化器"""
problem__id = serializers.CharField()
problem_title = serializers.CharField()
submission_count = serializers.IntegerField()
best_score = serializers.FloatField()
best_grade = serializers.CharField()
latest_submission_time = serializers.DateTimeField()
avg_score = serializers.FloatField()

View File

@@ -65,32 +65,32 @@ class FlowchartSubmissionAPI(APIView):
class FlowchartSubmissionListAPI(APIView): class FlowchartSubmissionListAPI(APIView):
@login_required
def get(self, request): def get(self, request):
"""获取流程图提交列表""" """获取流程图提交列表"""
user_id = request.GET.get("user_id") username = request.GET.get("username")
problem_id = request.GET.get("problem_id") problem_id = request.GET.get("problem_id")
offset = int(request.GET.get("offset", 0)) myself = request.GET.get("myself")
limit = int(request.GET.get("limit", 20))
queryset = FlowchartSubmission.objects.select_related("user", "problem") queryset = FlowchartSubmission.objects.select_related("user", "problem")
# 权限过滤
if not request.user.is_admin_role():
queryset = queryset.filter(user=request.user)
# 其他过滤条件
if user_id:
queryset = queryset.filter(user_id=user_id)
if problem_id: if problem_id:
queryset = queryset.filter(problem_id=problem_id) try:
problem = Problem.objects.get(
_id=problem_id, contest_id__isnull=True, visible=True
)
except Problem.DoesNotExist:
return self.error("Problem doesn't exist")
queryset = queryset.filter(problem=problem)
if myself and myself == "1":
queryset = queryset.filter(user=request.user)
if username:
queryset = queryset.filter(user__username__icontains=username)
total = queryset.count() data = self.paginate_data(request, queryset)
submissions = queryset[offset : offset + limit] data["results"] = FlowchartSubmissionListSerializer(
data["results"], many=True
serializer = FlowchartSubmissionListSerializer(submissions, many=True) ).data
return self.success(data)
return self.success({"results": serializer.data, "total": total})
class FlowchartSubmissionRetryAPI(APIView): class FlowchartSubmissionRetryAPI(APIView):

View File

@@ -34,6 +34,7 @@ class SubmissionSafeModelSerializer(serializers.ModelSerializer):
class SubmissionListSerializer(serializers.ModelSerializer): class SubmissionListSerializer(serializers.ModelSerializer):
problem = serializers.SlugRelatedField(read_only=True, slug_field="_id") problem = serializers.SlugRelatedField(read_only=True, slug_field="_id")
problem_title = serializers.CharField(source="problem.title")
show_link = serializers.SerializerMethodField() show_link = serializers.SerializerMethodField()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):