feat: add Teacher Admin role to four-tier permission system
Introduces a four-tier role system: Regular User → Student Admin → Teacher Admin → Super Admin. Teacher Admin can manage own contests, problemsets, and view classroom data. Student Admin (renamed from Admin) retains problem management only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,6 +57,12 @@ class super_admin_required(BasePermissionDecorator):
|
|||||||
return user.is_authenticated and user.is_super_admin()
|
return user.is_authenticated and user.is_super_admin()
|
||||||
|
|
||||||
|
|
||||||
|
class teacher_admin_required(BasePermissionDecorator):
|
||||||
|
def check_permission(self, request):
|
||||||
|
user = request.user
|
||||||
|
return user.is_authenticated and user.is_teacher_or_above()
|
||||||
|
|
||||||
|
|
||||||
class admin_role_required(BasePermissionDecorator):
|
class admin_role_required(BasePermissionDecorator):
|
||||||
def check_permission(self, request):
|
def check_permission(self, request):
|
||||||
user = request.user
|
user = request.user
|
||||||
|
|||||||
18
account/migrations/0006_alter_user_admin_type.py
Normal file
18
account/migrations/0006_alter_user_admin_type.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 6.0.4 on 2026-06-03 00:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0005_alter_user_is_disabled_alter_user_open_api_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='admin_type',
|
||||||
|
field=models.TextField(choices=[('Regular User', 'Regular User'), ('Student Admin', 'Student Admin'), ('Teacher Admin', 'Teacher Admin'), ('Super Admin', 'Super Admin')], default='Regular User'),
|
||||||
|
),
|
||||||
|
]
|
||||||
27
account/migrations/0007_rename_admin_to_student_admin.py
Normal file
27
account/migrations/0007_rename_admin_to_student_admin.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 6.0.4 on 2026-06-03 00:08
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def rename_admin_to_student_admin(apps, schema_editor):
|
||||||
|
User = apps.get_model("account", "User")
|
||||||
|
User.objects.filter(admin_type="Admin").update(admin_type="Student Admin")
|
||||||
|
|
||||||
|
|
||||||
|
def rename_student_admin_to_admin(apps, schema_editor):
|
||||||
|
User = apps.get_model("account", "User")
|
||||||
|
User.objects.filter(admin_type="Student Admin").update(admin_type="Admin")
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("account", "0006_alter_user_admin_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
rename_admin_to_student_admin,
|
||||||
|
rename_student_admin_to_admin,
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -7,7 +7,8 @@ from utils.models import JSONField
|
|||||||
|
|
||||||
class AdminType(models.TextChoices):
|
class AdminType(models.TextChoices):
|
||||||
REGULAR_USER = "Regular User", "Regular User"
|
REGULAR_USER = "Regular User", "Regular User"
|
||||||
ADMIN = "Admin", "Admin"
|
STUDENT_ADMIN = "Student Admin", "Student Admin"
|
||||||
|
TEACHER_ADMIN = "Teacher Admin", "Teacher Admin"
|
||||||
SUPER_ADMIN = "Super Admin", "Super Admin"
|
SUPER_ADMIN = "Super Admin", "Super Admin"
|
||||||
|
|
||||||
|
|
||||||
@@ -56,14 +57,24 @@ class User(AbstractBaseUser):
|
|||||||
def is_regular_user(self):
|
def is_regular_user(self):
|
||||||
return self.admin_type == AdminType.REGULAR_USER
|
return self.admin_type == AdminType.REGULAR_USER
|
||||||
|
|
||||||
def is_admin(self):
|
def is_student_admin(self):
|
||||||
return self.admin_type == AdminType.ADMIN
|
return self.admin_type == AdminType.STUDENT_ADMIN
|
||||||
|
|
||||||
|
def is_teacher_admin(self):
|
||||||
|
return self.admin_type == AdminType.TEACHER_ADMIN
|
||||||
|
|
||||||
def is_super_admin(self):
|
def is_super_admin(self):
|
||||||
return self.admin_type == AdminType.SUPER_ADMIN
|
return self.admin_type == AdminType.SUPER_ADMIN
|
||||||
|
|
||||||
def is_admin_role(self):
|
def is_admin_role(self):
|
||||||
return self.admin_type in [AdminType.ADMIN, AdminType.SUPER_ADMIN]
|
return self.admin_type in [
|
||||||
|
AdminType.STUDENT_ADMIN,
|
||||||
|
AdminType.TEACHER_ADMIN,
|
||||||
|
AdminType.SUPER_ADMIN,
|
||||||
|
]
|
||||||
|
|
||||||
|
def is_teacher_or_above(self):
|
||||||
|
return self.admin_type in [AdminType.TEACHER_ADMIN, AdminType.SUPER_ADMIN]
|
||||||
|
|
||||||
def can_mgmt_all_problem(self):
|
def can_mgmt_all_problem(self):
|
||||||
return self.problem_permission == ProblemPermission.ALL
|
return self.problem_permission == ProblemPermission.ALL
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ class UserAdminAPI(APIView):
|
|||||||
user.admin_type = data["admin_type"]
|
user.admin_type = data["admin_type"]
|
||||||
user.is_disabled = data["is_disabled"]
|
user.is_disabled = data["is_disabled"]
|
||||||
|
|
||||||
if data["admin_type"] == AdminType.ADMIN:
|
if data["admin_type"] == AdminType.STUDENT_ADMIN:
|
||||||
|
user.problem_permission = data["problem_permission"] or ProblemPermission.OWN
|
||||||
|
elif data["admin_type"] == AdminType.TEACHER_ADMIN:
|
||||||
user.problem_permission = data["problem_permission"] or ProblemPermission.OWN
|
user.problem_permission = data["problem_permission"] or ProblemPermission.OWN
|
||||||
elif data["admin_type"] == AdminType.SUPER_ADMIN:
|
elif data["admin_type"] == AdminType.SUPER_ADMIN:
|
||||||
user.problem_permission = ProblemPermission.ALL
|
user.problem_permission = ProblemPermission.ALL
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ class UserRankAPI(AsyncAPIView):
|
|||||||
n = 0
|
n = 0
|
||||||
|
|
||||||
profiles = UserProfile.objects.filter(
|
profiles = UserProfile.objects.filter(
|
||||||
user__admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
user__admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
user__is_disabled=False,
|
user__is_disabled=False,
|
||||||
user__username__icontains=username,
|
user__username__icontains=username,
|
||||||
).select_related("user").filter(accepted_number__gte=0).order_by("-accepted_number", "submission_number")
|
).select_related("user").filter(accepted_number__gte=0).order_by("-accepted_number", "submission_number")
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class ClassRankAPI(APIView):
|
|||||||
User.objects.filter(
|
User.objects.filter(
|
||||||
class_name__isnull=False,
|
class_name__isnull=False,
|
||||||
is_disabled=False,
|
is_disabled=False,
|
||||||
admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
class_name__startswith=str(grade),
|
class_name__startswith=str(grade),
|
||||||
)
|
)
|
||||||
.values("class_name")
|
.values("class_name")
|
||||||
@@ -35,7 +35,7 @@ class ClassRankAPI(APIView):
|
|||||||
users = User.objects.filter(
|
users = User.objects.filter(
|
||||||
class_name=class_name,
|
class_name=class_name,
|
||||||
is_disabled=False,
|
is_disabled=False,
|
||||||
admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
)
|
)
|
||||||
user_ids = list(users.values_list("id", flat=True))
|
user_ids = list(users.values_list("id", flat=True))
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ class UserClassRankAPI(APIView):
|
|||||||
class_users = User.objects.filter(
|
class_users = User.objects.filter(
|
||||||
class_name=user.class_name,
|
class_name=user.class_name,
|
||||||
is_disabled=False,
|
is_disabled=False,
|
||||||
admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
).select_related("userprofile")
|
).select_related("userprofile")
|
||||||
|
|
||||||
user_ranks = []
|
user_ranks = []
|
||||||
@@ -187,7 +187,7 @@ class ClassPKAPI(APIView):
|
|||||||
User.objects.filter(
|
User.objects.filter(
|
||||||
class_name__in=class_names,
|
class_name__in=class_names,
|
||||||
is_disabled=False,
|
is_disabled=False,
|
||||||
admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
).values_list("id", flat=True)
|
).values_list("id", flat=True)
|
||||||
)
|
)
|
||||||
all_ac_list = sorted(
|
all_ac_list = sorted(
|
||||||
@@ -206,7 +206,7 @@ class ClassPKAPI(APIView):
|
|||||||
users = User.objects.filter(
|
users = User.objects.filter(
|
||||||
class_name=class_name,
|
class_name=class_name,
|
||||||
is_disabled=False,
|
is_disabled=False,
|
||||||
admin_type__in=[AdminType.REGULAR_USER, AdminType.ADMIN],
|
admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
)
|
)
|
||||||
user_ids = list(users.values_list("id", flat=True))
|
user_ids = list(users.values_list("id", flat=True))
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import dateutil.parser
|
|||||||
from django.http import FileResponse
|
from django.http import FileResponse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from account.decorators import super_admin_required
|
from account.decorators import ensure_created_by, super_admin_required, teacher_admin_required
|
||||||
from account.models import User
|
from account.models import User
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from submission.models import JudgeStatus, Submission
|
from submission.models import JudgeStatus, Submission
|
||||||
@@ -31,7 +31,7 @@ from ..serializers import (
|
|||||||
|
|
||||||
class ContestAPI(APIView):
|
class ContestAPI(APIView):
|
||||||
@validate_serializer(CreateConetestSeriaizer)
|
@validate_serializer(CreateConetestSeriaizer)
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
data = request.data
|
data = request.data
|
||||||
data["start_time"] = dateutil.parser.parse(data["start_time"])
|
data["start_time"] = dateutil.parser.parse(data["start_time"])
|
||||||
@@ -50,13 +50,14 @@ class ContestAPI(APIView):
|
|||||||
return self.success(ContestAdminSerializer(contest).data)
|
return self.success(ContestAdminSerializer(contest).data)
|
||||||
|
|
||||||
@validate_serializer(EditConetestSeriaizer)
|
@validate_serializer(EditConetestSeriaizer)
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
data = request.data
|
data = request.data
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.get(id=data.pop("id"))
|
contest = Contest.objects.get(id=data.pop("id"))
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return self.error("Contest does not exist")
|
return self.error("Contest does not exist")
|
||||||
|
ensure_created_by(contest, request.user)
|
||||||
data["start_time"] = dateutil.parser.parse(data["start_time"])
|
data["start_time"] = dateutil.parser.parse(data["start_time"])
|
||||||
data["end_time"] = dateutil.parser.parse(data["end_time"])
|
data["end_time"] = dateutil.parser.parse(data["end_time"])
|
||||||
if data["end_time"] <= data["start_time"]:
|
if data["end_time"] <= data["start_time"]:
|
||||||
@@ -73,17 +74,20 @@ class ContestAPI(APIView):
|
|||||||
contest.save()
|
contest.save()
|
||||||
return self.success(ContestAdminSerializer(contest).data)
|
return self.success(ContestAdminSerializer(contest).data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
contest_id = request.GET.get("id")
|
contest_id = request.GET.get("id")
|
||||||
if contest_id:
|
if contest_id:
|
||||||
try:
|
try:
|
||||||
contest = Contest.objects.get(id=contest_id)
|
contest = Contest.objects.get(id=contest_id)
|
||||||
|
ensure_created_by(contest, request.user)
|
||||||
return self.success(ContestAdminSerializer(contest).data)
|
return self.success(ContestAdminSerializer(contest).data)
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return self.error("Contest does not exist")
|
return self.error("Contest does not exist")
|
||||||
|
|
||||||
contests = Contest.objects.all().order_by("-create_time")
|
contests = Contest.objects.all().order_by("-create_time")
|
||||||
|
if not request.user.is_super_admin():
|
||||||
|
contests = contests.filter(created_by=request.user)
|
||||||
|
|
||||||
keyword = request.GET.get("keyword")
|
keyword = request.GET.get("keyword")
|
||||||
if keyword:
|
if keyword:
|
||||||
@@ -159,7 +163,7 @@ class ContestAnnouncementAPI(APIView):
|
|||||||
|
|
||||||
|
|
||||||
class ACMContestHelper(APIView):
|
class ACMContestHelper(APIView):
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
contest_id = request.GET.get("contest_id")
|
contest_id = request.GET.get("contest_id")
|
||||||
if not contest_id:
|
if not contest_id:
|
||||||
@@ -168,6 +172,7 @@ class ACMContestHelper(APIView):
|
|||||||
contest = Contest.objects.get(id=contest_id, visible=True)
|
contest = Contest.objects.get(id=contest_id, visible=True)
|
||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return self.error("Contest does not exist")
|
return self.error("Contest does not exist")
|
||||||
|
ensure_created_by(contest, request.user)
|
||||||
|
|
||||||
problems = Problem.objects.filter(contest=contest).values("id", "_id")
|
problems = Problem.objects.filter(contest=contest).values("id", "_id")
|
||||||
problem_id_map = {str(p["id"]): p["_id"] for p in problems}
|
problem_id_map = {str(p["id"]): p["_id"] for p in problems}
|
||||||
@@ -191,7 +196,7 @@ class ACMContestHelper(APIView):
|
|||||||
results.sort(key=lambda x: -x["ac_info"]["ac_time"])
|
results.sort(key=lambda x: -x["ac_info"]["ac_time"])
|
||||||
return self.success(results)
|
return self.success(results)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(ACMContesHelperSerializer)
|
@validate_serializer(ACMContesHelperSerializer)
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
data = request.data
|
data = request.data
|
||||||
@@ -258,7 +263,7 @@ class DownloadContestSubmissions(APIView):
|
|||||||
|
|
||||||
class ContestCloneAPI(APIView):
|
class ContestCloneAPI(APIView):
|
||||||
@validate_serializer(ContestCloneSerializer)
|
@validate_serializer(ContestCloneSerializer)
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
try:
|
try:
|
||||||
original = Contest.objects.get(id=request.data["contest_id"])
|
original = Contest.objects.get(id=request.data["contest_id"])
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class ContestRankAPI(APIView):
|
|||||||
return (
|
return (
|
||||||
ACMContestRank.objects.filter(
|
ACMContestRank.objects.filter(
|
||||||
contest=self.contest,
|
contest=self.contest,
|
||||||
user__admin_type=AdminType.REGULAR_USER,
|
user__admin_type__in=[AdminType.REGULAR_USER, AdminType.STUDENT_ADMIN],
|
||||||
user__is_disabled=False,
|
user__is_disabled=False,
|
||||||
)
|
)
|
||||||
.select_related("user")
|
.select_related("user")
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ class ContestProblemAPI(ProblemBase):
|
|||||||
except Contest.DoesNotExist:
|
except Contest.DoesNotExist:
|
||||||
return self.error("Contest does not exist")
|
return self.error("Contest does not exist")
|
||||||
problems = Problem.objects.filter(contest=contest).order_by("-create_time")
|
problems = Problem.objects.filter(contest=contest).order_by("-create_time")
|
||||||
if user.is_admin():
|
if not user.is_super_admin():
|
||||||
problems = problems.filter(contest__created_by=user)
|
problems = problems.filter(contest__created_by=user)
|
||||||
keyword = request.GET.get("keyword")
|
keyword = request.GET.get("keyword")
|
||||||
if keyword:
|
if keyword:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
|
|
||||||
from account.decorators import ensure_created_by, super_admin_required
|
from account.decorators import ensure_created_by, teacher_admin_required
|
||||||
from problem.models import Problem
|
from problem.models import Problem
|
||||||
from problemset.models import (
|
from problemset.models import (
|
||||||
ProblemSet,
|
ProblemSet,
|
||||||
@@ -29,10 +29,12 @@ from utils.api import APIView, validate_serializer
|
|||||||
class ProblemSetAdminAPI(APIView):
|
class ProblemSetAdminAPI(APIView):
|
||||||
"""题单管理API"""
|
"""题单管理API"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""获取题单列表(管理员)"""
|
"""获取题单列表(管理员)"""
|
||||||
problem_sets = ProblemSet.objects.filter(visible=True).annotate(problems_count=Count("problemsetproblem", distinct=True)).order_by("-create_time")
|
problem_sets = ProblemSet.objects.filter(visible=True).annotate(problems_count=Count("problemsetproblem", distinct=True)).order_by("-create_time")
|
||||||
|
if not request.user.is_super_admin():
|
||||||
|
problem_sets = problem_sets.filter(created_by=request.user)
|
||||||
|
|
||||||
# 过滤条件
|
# 过滤条件
|
||||||
keyword = request.GET.get("keyword", "").strip()
|
keyword = request.GET.get("keyword", "").strip()
|
||||||
@@ -51,7 +53,7 @@ class ProblemSetAdminAPI(APIView):
|
|||||||
data = self.paginate_data(request, problem_sets, ProblemSetListSerializer)
|
data = self.paginate_data(request, problem_sets, ProblemSetListSerializer)
|
||||||
return self.success(data)
|
return self.success(data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(CreateProblemSetSerializer)
|
@validate_serializer(CreateProblemSetSerializer)
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""创建题单"""
|
"""创建题单"""
|
||||||
@@ -60,7 +62,7 @@ class ProblemSetAdminAPI(APIView):
|
|||||||
problem_set = ProblemSet.objects.create(**data)
|
problem_set = ProblemSet.objects.create(**data)
|
||||||
return self.success(ProblemSetSerializer(problem_set).data)
|
return self.success(ProblemSetSerializer(problem_set).data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(EditProblemSetSerializer)
|
@validate_serializer(EditProblemSetSerializer)
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
"""编辑题单"""
|
"""编辑题单"""
|
||||||
@@ -79,7 +81,7 @@ class ProblemSetAdminAPI(APIView):
|
|||||||
|
|
||||||
return self.success(ProblemSetSerializer(problem_set).data)
|
return self.success(ProblemSetSerializer(problem_set).data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def delete(self, request):
|
def delete(self, request):
|
||||||
"""删除题单"""
|
"""删除题单"""
|
||||||
problem_set_id = request.GET.get("id")
|
problem_set_id = request.GET.get("id")
|
||||||
@@ -99,7 +101,7 @@ class ProblemSetAdminAPI(APIView):
|
|||||||
class ProblemSetDetailAdminAPI(APIView):
|
class ProblemSetDetailAdminAPI(APIView):
|
||||||
"""题单详情管理API"""
|
"""题单详情管理API"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request, problem_set_id):
|
def get(self, request, problem_set_id):
|
||||||
"""获取题单详情(管理员)"""
|
"""获取题单详情(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -115,7 +117,7 @@ class ProblemSetDetailAdminAPI(APIView):
|
|||||||
class ProblemSetProblemAdminAPI(APIView):
|
class ProblemSetProblemAdminAPI(APIView):
|
||||||
"""题单题目管理API(管理员)"""
|
"""题单题目管理API(管理员)"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request, problem_set_id):
|
def get(self, request, problem_set_id):
|
||||||
"""获取题单中的题目列表(管理员)"""
|
"""获取题单中的题目列表(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -128,7 +130,7 @@ class ProblemSetProblemAdminAPI(APIView):
|
|||||||
serializer = ProblemSetProblemSerializer(problems, many=True, context={"request": request})
|
serializer = ProblemSetProblemSerializer(problems, many=True, context={"request": request})
|
||||||
return self.success(serializer.data)
|
return self.success(serializer.data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(AddProblemToSetSerializer)
|
@validate_serializer(AddProblemToSetSerializer)
|
||||||
def post(self, request, problem_set_id):
|
def post(self, request, problem_set_id):
|
||||||
"""添加题目到题单(管理员)"""
|
"""添加题目到题单(管理员)"""
|
||||||
@@ -163,7 +165,7 @@ class ProblemSetProblemAdminAPI(APIView):
|
|||||||
|
|
||||||
return self.success("题目已添加到题单")
|
return self.success("题目已添加到题单")
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(EditProblemInSetSerializer)
|
@validate_serializer(EditProblemInSetSerializer)
|
||||||
def put(self, request, problem_set_id, problem_set_problem_id):
|
def put(self, request, problem_set_id, problem_set_problem_id):
|
||||||
"""编辑题单中的题目(管理员)"""
|
"""编辑题单中的题目(管理员)"""
|
||||||
@@ -193,7 +195,7 @@ class ProblemSetProblemAdminAPI(APIView):
|
|||||||
|
|
||||||
return self.success("题目已更新")
|
return self.success("题目已更新")
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def delete(self, request, problem_set_id, problem_set_problem_id):
|
def delete(self, request, problem_set_id, problem_set_problem_id):
|
||||||
"""从题单中移除题目(管理员)"""
|
"""从题单中移除题目(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -213,7 +215,7 @@ class ProblemSetProblemAdminAPI(APIView):
|
|||||||
class ProblemSetBadgeAdminAPI(APIView):
|
class ProblemSetBadgeAdminAPI(APIView):
|
||||||
"""题单奖章管理API(管理员)"""
|
"""题单奖章管理API(管理员)"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request, problem_set_id):
|
def get(self, request, problem_set_id):
|
||||||
"""获取题单的奖章列表(管理员)"""
|
"""获取题单的奖章列表(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -226,7 +228,7 @@ class ProblemSetBadgeAdminAPI(APIView):
|
|||||||
serializer = ProblemSetBadgeSerializer(badges, many=True)
|
serializer = ProblemSetBadgeSerializer(badges, many=True)
|
||||||
return self.success(serializer.data)
|
return self.success(serializer.data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(CreateProblemSetBadgeSerializer)
|
@validate_serializer(CreateProblemSetBadgeSerializer)
|
||||||
def post(self, request, problem_set_id):
|
def post(self, request, problem_set_id):
|
||||||
"""创建题单奖章(管理员)"""
|
"""创建题单奖章(管理员)"""
|
||||||
@@ -242,7 +244,7 @@ class ProblemSetBadgeAdminAPI(APIView):
|
|||||||
|
|
||||||
return self.success(ProblemSetBadgeSerializer(badge).data)
|
return self.success(ProblemSetBadgeSerializer(badge).data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(EditProblemSetBadgeSerializer)
|
@validate_serializer(EditProblemSetBadgeSerializer)
|
||||||
def put(self, request, problem_set_id, badge_id):
|
def put(self, request, problem_set_id, badge_id):
|
||||||
"""编辑题单奖章(管理员)"""
|
"""编辑题单奖章(管理员)"""
|
||||||
@@ -282,7 +284,7 @@ class ProblemSetBadgeAdminAPI(APIView):
|
|||||||
return self.success("奖章已更新,并重新计算了所有用户的徽章资格")
|
return self.success("奖章已更新,并重新计算了所有用户的徽章资格")
|
||||||
return self.success("奖章已更新")
|
return self.success("奖章已更新")
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def delete(self, request, problem_set_id, badge_id):
|
def delete(self, request, problem_set_id, badge_id):
|
||||||
"""删除题单奖章(管理员)"""
|
"""删除题单奖章(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -302,7 +304,7 @@ class ProblemSetBadgeAdminAPI(APIView):
|
|||||||
class ProblemSetProgressAdminAPI(APIView):
|
class ProblemSetProgressAdminAPI(APIView):
|
||||||
"""题单进度管理API(管理员)"""
|
"""题单进度管理API(管理员)"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def get(self, request, problem_set_id):
|
def get(self, request, problem_set_id):
|
||||||
"""获取题单的所有用户进度(管理员)"""
|
"""获取题单的所有用户进度(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -315,7 +317,7 @@ class ProblemSetProgressAdminAPI(APIView):
|
|||||||
serializer = ProblemSetProgressSerializer(progress_list, many=True)
|
serializer = ProblemSetProgressSerializer(progress_list, many=True)
|
||||||
return self.success(serializer.data)
|
return self.success(serializer.data)
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def delete(self, request, problem_set_id, user_id):
|
def delete(self, request, problem_set_id, user_id):
|
||||||
"""移除用户从题单(管理员)"""
|
"""移除用户从题单(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -336,7 +338,7 @@ class ProblemSetProgressAdminAPI(APIView):
|
|||||||
class ProblemSetSyncAPI(APIView):
|
class ProblemSetSyncAPI(APIView):
|
||||||
"""题单同步管理API"""
|
"""题单同步管理API"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
def post(self, request, problem_set_id):
|
def post(self, request, problem_set_id):
|
||||||
"""手动同步题单的所有用户进度(管理员)"""
|
"""手动同步题单的所有用户进度(管理员)"""
|
||||||
try:
|
try:
|
||||||
@@ -354,7 +356,7 @@ class ProblemSetSyncAPI(APIView):
|
|||||||
class ProblemSetVisibleAPI(APIView):
|
class ProblemSetVisibleAPI(APIView):
|
||||||
"""题单可见性管理API"""
|
"""题单可见性管理API"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(ProblemSetVisibleSerializer)
|
@validate_serializer(ProblemSetVisibleSerializer)
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
"""切换题单可见性"""
|
"""切换题单可见性"""
|
||||||
@@ -373,7 +375,7 @@ class ProblemSetVisibleAPI(APIView):
|
|||||||
class ProblemSetStatusAPI(APIView):
|
class ProblemSetStatusAPI(APIView):
|
||||||
"""题单状态管理API"""
|
"""题单状态管理API"""
|
||||||
|
|
||||||
@super_admin_required
|
@teacher_admin_required
|
||||||
@validate_serializer(ProblemSetUpdateStatusSerializer)
|
@validate_serializer(ProblemSetUpdateStatusSerializer)
|
||||||
def put(self, request):
|
def put(self, request):
|
||||||
"""更新题单状态"""
|
"""更新题单状态"""
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from account.models import User, UserProfile, AdminType, ProblemPermission
|
|||||||
from problem.models import Problem, ProblemTag, ProblemRuleType
|
from problem.models import Problem, ProblemTag, ProblemRuleType
|
||||||
from utils.constants import Difficulty
|
from utils.constants import Difficulty
|
||||||
|
|
||||||
admin_type_map = {0: AdminType.REGULAR_USER, 1: AdminType.ADMIN, 2: AdminType.SUPER_ADMIN}
|
admin_type_map = {0: AdminType.REGULAR_USER, 1: AdminType.STUDENT_ADMIN, 2: AdminType.SUPER_ADMIN}
|
||||||
languages_map = {1: "C", 2: "C++", 3: "Java"}
|
languages_map = {1: "C", 2: "C++", 3: "Java"}
|
||||||
email_regex = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")
|
email_regex = re.compile(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)")
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ def import_users():
|
|||||||
user.email = data["email"]
|
user.email = data["email"]
|
||||||
admin_type = admin_type_map[data["admin_type"]]
|
admin_type = admin_type_map[data["admin_type"]]
|
||||||
user.admin_type = admin_type
|
user.admin_type = admin_type
|
||||||
if admin_type == AdminType.ADMIN:
|
if admin_type == AdminType.STUDENT_ADMIN:
|
||||||
user.problem_permission = ProblemPermission.OWN
|
user.problem_permission = ProblemPermission.OWN
|
||||||
elif admin_type == AdminType.SUPER_ADMIN:
|
elif admin_type == AdminType.SUPER_ADMIN:
|
||||||
user.problem_permission = ProblemPermission.ALL
|
user.problem_permission = ProblemPermission.ALL
|
||||||
|
|||||||
Reference in New Issue
Block a user