add teaching feature

This commit is contained in:
2026-03-08 21:15:46 +08:00
parent 05d46295a0
commit 889380c58e
7 changed files with 281 additions and 1 deletions

View File

@@ -4,6 +4,7 @@ from ..views.admin import (
ContestProblemAPI,
ProblemAPI,
ProblemFlowchartAIGen,
StuckProblemsAPI,
TestCaseAPI,
MakeContestProblemPublicAPIView,
AddContestProblemAPI,
@@ -14,6 +15,7 @@ urlpatterns = [
path("test_case", TestCaseAPI.as_view()),
path("problem", ProblemAPI.as_view()),
path("problem/visible", ProblemVisibleAPI.as_view()),
path("problem/stuck", StuckProblemsAPI.as_view()),
path("problem/flowchart", ProblemFlowchartAIGen.as_view()),
path("contest/problem", ContestProblemAPI.as_view()),
path("contest_problem/make_public", MakeContestProblemPublicAPIView.as_view()),

View File

@@ -7,12 +7,14 @@ from ..views.oj import (
ContestProblemAPI,
PickOneAPI,
ProblemAuthorAPI,
SimilarProblemAPI,
)
urlpatterns = [
path("problem/tags", ProblemTagAPI.as_view()),
path("problem", ProblemAPI.as_view()),
path("problem/beat_count", ProblemSolvedPeopleCount.as_view()),
path("problem/similar", SimilarProblemAPI.as_view()),
path("problem/author", ProblemAuthorAPI.as_view()),
path("pickone", PickOneAPI.as_view()),
path("contest/problem", ContestProblemAPI.as_view()),

View File

@@ -10,7 +10,9 @@ from django.conf import settings
from django.db.models import Q
from django.http import StreamingHttpResponse
from account.decorators import problem_permission_required, ensure_created_by
from django.db.models import Count
from account.decorators import problem_permission_required, ensure_created_by, super_admin_required
from contest.models import Contest, ContestStatus
from submission.models import Submission
from utils.api import APIView, CSRFExemptAPIView, validate_serializer, APIError
@@ -509,3 +511,33 @@ class ProblemFlowchartAIGen(APIView):
mermaid_code = response.choices[0].message.content
return self.success({"flowchart": mermaid_code})
class StuckProblemsAPI(APIView):
@super_admin_required
def get(self, request):
rows = (
Submission.objects.values("problem_id", "problem___id", "problem__title")
.annotate(
total=Count("id"),
accepted=Count("id", filter=Q(result=0)),
failed=Count("id", filter=Q(result__lt=0)),
failed_users=Count("user_id", filter=Q(result__lt=0), distinct=True),
)
.filter(failed_users__gt=0)
.order_by("-failed_users")[:30]
)
result = [
{
"problem_id": r["problem___id"],
"problem_title": r["problem__title"],
"total": r["total"],
"failed": r["failed"],
"failed_users": r["failed_users"],
"ac_rate": round(r["accepted"] / r["total"] * 100, 1)
if r["total"]
else 0,
}
for r in rows
]
return self.success(result)

View File

@@ -63,6 +63,22 @@ class ProblemAPI(APIView):
)
problem_data = ProblemSerializer(problem).data
self._add_problem_status(request, problem_data)
if request.user.is_authenticated:
failed_statuses = [
JudgeStatus.WRONG_ANSWER,
JudgeStatus.CPU_TIME_LIMIT_EXCEEDED,
JudgeStatus.REAL_TIME_LIMIT_EXCEEDED,
JudgeStatus.MEMORY_LIMIT_EXCEEDED,
JudgeStatus.RUNTIME_ERROR,
JudgeStatus.COMPILE_ERROR,
]
problem_data["my_failed_count"] = Submission.objects.filter(
user_id=request.user.id,
problem_id=problem.id,
result__in=failed_statuses,
).count()
else:
problem_data["my_failed_count"] = 0
return self.success(problem_data)
except Problem.DoesNotExist:
return self.error("Problem does not exist")
@@ -187,6 +203,40 @@ class ProblemSolvedPeopleCount(APIView):
return self.success(rate)
class SimilarProblemAPI(APIView):
def get(self, request):
problem_display_id = request.GET.get("problem_id")
if not problem_display_id:
return self.error("problem_id is required")
try:
problem = Problem.objects.get(_id=problem_display_id, contest__isnull=True)
except Problem.DoesNotExist:
return self.error("Problem not found")
tag_ids = list(problem.tags.values_list("id", flat=True))
if not tag_ids:
return self.success([])
exclude_ids = [problem_display_id]
if request.user.is_authenticated:
profile = request.user.userprofile
ac_display_ids = [
v["_id"]
for v in profile.acm_problems_status.get("problems", {}).values()
if v.get("status") == JudgeStatus.ACCEPTED
]
exclude_ids.extend(ac_display_ids)
similar = (
Problem.objects.filter(tags__in=tag_ids, visible=True, contest__isnull=True)
.exclude(_id__in=exclude_ids)
.distinct()
.order_by("difficulty")[:5]
)
return self.success(ProblemListSerializer(similar, many=True).data)
class ProblemAuthorAPI(APIView):
def get(self, request):
show_all = request.GET.get("all", "0") == "1"