后台可以筛选管理员
This commit is contained in:
@@ -60,7 +60,9 @@ class User(AbstractBaseUser):
|
|||||||
return self.problem_permission == ProblemPermission.ALL
|
return self.problem_permission == ProblemPermission.ALL
|
||||||
|
|
||||||
def is_contest_admin(self, contest):
|
def is_contest_admin(self, contest):
|
||||||
return self.is_authenticated and (contest.created_by == self or self.admin_type == AdminType.SUPER_ADMIN)
|
return self.is_authenticated and (
|
||||||
|
contest.created_by == self or self.admin_type == AdminType.SUPER_ADMIN
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "user"
|
db_table = "user"
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ class GenerateUserSerializer(serializers.Serializer):
|
|||||||
password_length = serializers.IntegerField(max_value=16, default=8)
|
password_length = serializers.IntegerField(max_value=16, default=8)
|
||||||
|
|
||||||
|
|
||||||
class ImportUserSeralizer(serializers.Serializer):
|
class ImportUserSerializer(serializers.Serializer):
|
||||||
users = serializers.ListField(
|
users = serializers.ListField(
|
||||||
child=serializers.ListField(child=serializers.CharField(max_length=64)))
|
child=serializers.ListField(child=serializers.CharField(max_length=64))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserAdminSerializer(serializers.ModelSerializer):
|
class UserAdminSerializer(serializers.ModelSerializer):
|
||||||
@@ -53,8 +54,19 @@ class UserAdminSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ["id", "username", "email", "admin_type", "problem_permission", "real_name",
|
fields = [
|
||||||
"create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"]
|
"id",
|
||||||
|
"username",
|
||||||
|
"email",
|
||||||
|
"admin_type",
|
||||||
|
"problem_permission",
|
||||||
|
"real_name",
|
||||||
|
"create_time",
|
||||||
|
"last_login",
|
||||||
|
"two_factor_auth",
|
||||||
|
"open_api",
|
||||||
|
"is_disabled",
|
||||||
|
]
|
||||||
|
|
||||||
def get_real_name(self, obj):
|
def get_real_name(self, obj):
|
||||||
return obj.userprofile.real_name
|
return obj.userprofile.real_name
|
||||||
@@ -63,8 +75,18 @@ class UserAdminSerializer(serializers.ModelSerializer):
|
|||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ["id", "username", "email", "admin_type", "problem_permission",
|
fields = [
|
||||||
"create_time", "last_login", "two_factor_auth", "open_api", "is_disabled"]
|
"id",
|
||||||
|
"username",
|
||||||
|
"email",
|
||||||
|
"admin_type",
|
||||||
|
"problem_permission",
|
||||||
|
"create_time",
|
||||||
|
"last_login",
|
||||||
|
"two_factor_auth",
|
||||||
|
"open_api",
|
||||||
|
"is_disabled",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserProfileSerializer(serializers.ModelSerializer):
|
class UserProfileSerializer(serializers.ModelSerializer):
|
||||||
@@ -87,11 +109,16 @@ class EditUserSerializer(serializers.Serializer):
|
|||||||
id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
username = serializers.CharField(max_length=32)
|
username = serializers.CharField(max_length=32)
|
||||||
real_name = serializers.CharField(max_length=32, allow_blank=True, allow_null=True)
|
real_name = serializers.CharField(max_length=32, allow_blank=True, allow_null=True)
|
||||||
password = serializers.CharField(min_length=6, allow_blank=True, required=False, default=None)
|
password = serializers.CharField(
|
||||||
|
min_length=6, allow_blank=True, required=False, default=None
|
||||||
|
)
|
||||||
email = serializers.EmailField(max_length=64)
|
email = serializers.EmailField(max_length=64)
|
||||||
admin_type = serializers.ChoiceField(choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN))
|
admin_type = serializers.ChoiceField(
|
||||||
problem_permission = serializers.ChoiceField(choices=(ProblemPermission.NONE, ProblemPermission.OWN,
|
choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN)
|
||||||
ProblemPermission.ALL))
|
)
|
||||||
|
problem_permission = serializers.ChoiceField(
|
||||||
|
choices=(ProblemPermission.NONE, ProblemPermission.OWN, ProblemPermission.ALL)
|
||||||
|
)
|
||||||
open_api = serializers.BooleanField()
|
open_api = serializers.BooleanField()
|
||||||
two_factor_auth = serializers.BooleanField()
|
two_factor_auth = serializers.BooleanField()
|
||||||
is_disabled = serializers.BooleanField()
|
is_disabled = serializers.BooleanField()
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ from utils.shortcuts import rand_str
|
|||||||
|
|
||||||
from ..decorators import super_admin_required
|
from ..decorators import super_admin_required
|
||||||
from ..models import AdminType, ProblemPermission, User, UserProfile
|
from ..models import AdminType, ProblemPermission, User, UserProfile
|
||||||
from ..serializers import EditUserSerializer, UserAdminSerializer, GenerateUserSerializer
|
from ..serializers import (
|
||||||
from ..serializers import ImportUserSeralizer
|
EditUserSerializer,
|
||||||
|
UserAdminSerializer,
|
||||||
|
GenerateUserSerializer,
|
||||||
|
)
|
||||||
|
from ..serializers import ImportUserSerializer
|
||||||
|
|
||||||
|
|
||||||
class UserAdminAPI(APIView):
|
class UserAdminAPI(APIView):
|
||||||
@validate_serializer(ImportUserSeralizer)
|
@validate_serializer(ImportUserSerializer)
|
||||||
@super_admin_required
|
@super_admin_required
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""
|
"""
|
||||||
@@ -30,12 +34,23 @@ class UserAdminAPI(APIView):
|
|||||||
for user_data in data:
|
for user_data in data:
|
||||||
if len(user_data) != 4 or len(user_data[0]) > 32:
|
if len(user_data) != 4 or len(user_data[0]) > 32:
|
||||||
return self.error(f"Error occurred while processing data '{user_data}'")
|
return self.error(f"Error occurred while processing data '{user_data}'")
|
||||||
user_list.append(User(username=user_data[0], password=make_password(user_data[1]), email=user_data[2]))
|
user_list.append(
|
||||||
|
User(
|
||||||
|
username=user_data[0],
|
||||||
|
password=make_password(user_data[1]),
|
||||||
|
email=user_data[2],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
ret = User.objects.bulk_create(user_list)
|
ret = User.objects.bulk_create(user_list)
|
||||||
UserProfile.objects.bulk_create([UserProfile(user=ret[i], real_name=data[i][3]) for i in range(len(ret))])
|
UserProfile.objects.bulk_create(
|
||||||
|
[
|
||||||
|
UserProfile(user=ret[i], real_name=data[i][3])
|
||||||
|
for i in range(len(ret))
|
||||||
|
]
|
||||||
|
)
|
||||||
return self.success()
|
return self.success()
|
||||||
except IntegrityError as e:
|
except IntegrityError as e:
|
||||||
# Extract detail from exception message
|
# Extract detail from exception message
|
||||||
@@ -54,9 +69,17 @@ class UserAdminAPI(APIView):
|
|||||||
user = User.objects.get(id=data["id"])
|
user = User.objects.get(id=data["id"])
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return self.error("User does not exist")
|
return self.error("User does not exist")
|
||||||
if User.objects.filter(username=data["username"].lower()).exclude(id=user.id).exists():
|
if (
|
||||||
|
User.objects.filter(username=data["username"].lower())
|
||||||
|
.exclude(id=user.id)
|
||||||
|
.exists()
|
||||||
|
):
|
||||||
return self.error("Username already exists")
|
return self.error("Username already exists")
|
||||||
if User.objects.filter(email=data["email"].lower()).exclude(id=user.id).exists():
|
if (
|
||||||
|
User.objects.filter(email=data["email"].lower())
|
||||||
|
.exclude(id=user.id)
|
||||||
|
.exists()
|
||||||
|
):
|
||||||
return self.error("Email already exists")
|
return self.error("Email already exists")
|
||||||
|
|
||||||
pre_username = user.username
|
pre_username = user.username
|
||||||
@@ -94,7 +117,9 @@ class UserAdminAPI(APIView):
|
|||||||
|
|
||||||
user.save()
|
user.save()
|
||||||
if pre_username != user.username:
|
if pre_username != user.username:
|
||||||
Submission.objects.filter(username=pre_username).update(username=user.username)
|
Submission.objects.filter(username=pre_username).update(
|
||||||
|
username=user.username
|
||||||
|
)
|
||||||
|
|
||||||
UserProfile.objects.filter(user=user).update(real_name=data["real_name"])
|
UserProfile.objects.filter(user=user).update(real_name=data["real_name"])
|
||||||
return self.success(UserAdminSerializer(user).data)
|
return self.success(UserAdminSerializer(user).data)
|
||||||
@@ -114,11 +139,18 @@ class UserAdminAPI(APIView):
|
|||||||
|
|
||||||
user = User.objects.all().order_by("-create_time")
|
user = User.objects.all().order_by("-create_time")
|
||||||
|
|
||||||
|
is_admin = request.GET.get("admin", False)
|
||||||
|
|
||||||
|
if is_admin:
|
||||||
|
user = user.exclude(admin_type=AdminType.REGULAR_USER)
|
||||||
|
|
||||||
keyword = request.GET.get("keyword", None)
|
keyword = request.GET.get("keyword", None)
|
||||||
if keyword:
|
if keyword:
|
||||||
user = user.filter(Q(username__icontains=keyword) |
|
user = user.filter(
|
||||||
Q(userprofile__real_name__icontains=keyword) |
|
Q(username__icontains=keyword)
|
||||||
Q(email__icontains=keyword))
|
| Q(userprofile__real_name__icontains=keyword)
|
||||||
|
| Q(email__icontains=keyword)
|
||||||
|
)
|
||||||
return self.success(self.paginate_data(request, user, UserAdminSerializer))
|
return self.success(self.paginate_data(request, user, UserAdminSerializer))
|
||||||
|
|
||||||
@super_admin_required
|
@super_admin_required
|
||||||
@@ -162,7 +194,9 @@ class GenerateUserAPI(APIView):
|
|||||||
Generate User
|
Generate User
|
||||||
"""
|
"""
|
||||||
data = request.data
|
data = request.data
|
||||||
number_max_length = max(len(str(data["number_from"])), len(str(data["number_to"])))
|
number_max_length = max(
|
||||||
|
len(str(data["number_from"])), len(str(data["number_to"]))
|
||||||
|
)
|
||||||
if number_max_length + len(data["prefix"]) + len(data["suffix"]) > 32:
|
if number_max_length + len(data["prefix"]) + len(data["suffix"]) > 32:
|
||||||
return self.error("Username should not more than 32 characters")
|
return self.error("Username should not more than 32 characters")
|
||||||
if data["number_from"] > data["number_to"]:
|
if data["number_from"] > data["number_to"]:
|
||||||
@@ -180,15 +214,19 @@ class GenerateUserAPI(APIView):
|
|||||||
user_list = []
|
user_list = []
|
||||||
for number in range(data["number_from"], data["number_to"] + 1):
|
for number in range(data["number_from"], data["number_to"] + 1):
|
||||||
raw_password = rand_str(data["password_length"])
|
raw_password = rand_str(data["password_length"])
|
||||||
user = User(username=f"{data['prefix']}{number}{data['suffix']}", password=make_password(raw_password))
|
user = User(
|
||||||
|
username=f"{data['prefix']}{number}{data['suffix']}",
|
||||||
|
password=make_password(raw_password),
|
||||||
|
)
|
||||||
user.raw_password = raw_password
|
user.raw_password = raw_password
|
||||||
user_list.append(user)
|
user_list.append(user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
|
||||||
ret = User.objects.bulk_create(user_list)
|
ret = User.objects.bulk_create(user_list)
|
||||||
UserProfile.objects.bulk_create([UserProfile(user=user) for user in ret])
|
UserProfile.objects.bulk_create(
|
||||||
|
[UserProfile(user=user) for user in ret]
|
||||||
|
)
|
||||||
for item in user_list:
|
for item in user_list:
|
||||||
worksheet.write_string(i, 0, item.username)
|
worksheet.write_string(i, 0, item.username)
|
||||||
worksheet.write_string(i, 1, item.raw_password)
|
worksheet.write_string(i, 1, item.raw_password)
|
||||||
|
|||||||
18
problem/migrations/0016_alter_problem_last_update_time.py
Normal file
18
problem/migrations/0016_alter_problem_last_update_time.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.6 on 2025-04-30 01:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('problem', '0015_alter_problem_io_mode_alter_problem_languages_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='problem',
|
||||||
|
name='last_update_time',
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from utils.models import JSONField
|
from utils.models import JSONField
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ class Problem(models.Model):
|
|||||||
template = JSONField()
|
template = JSONField()
|
||||||
create_time = models.DateTimeField(auto_now_add=True)
|
create_time = models.DateTimeField(auto_now_add=True)
|
||||||
# we can not use auto_now here
|
# we can not use auto_now here
|
||||||
last_update_time = models.DateTimeField(null=True)
|
last_update_time = models.DateTimeField(auto_now=True)
|
||||||
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
# ms
|
# ms
|
||||||
time_limit = models.IntegerField()
|
time_limit = models.IntegerField()
|
||||||
@@ -82,7 +83,7 @@ class Problem(models.Model):
|
|||||||
total_score = models.IntegerField(default=0)
|
total_score = models.IntegerField(default=0)
|
||||||
submission_number = models.BigIntegerField(default=0)
|
submission_number = models.BigIntegerField(default=0)
|
||||||
accepted_number = models.BigIntegerField(default=0)
|
accepted_number = models.BigIntegerField(default=0)
|
||||||
# {JudgeStatus.ACCEPTED: 3, JudgeStaus.WRONG_ANSWER: 11}, the number means count
|
# {JudgeStatus.ACCEPTED: 3, JudgeStatus.WRONG_ANSWER: 11}, the number means count
|
||||||
statistic_info = JSONField(default=dict)
|
statistic_info = JSONField(default=dict)
|
||||||
share_submission = models.BooleanField(default=False)
|
share_submission = models.BooleanField(default=False)
|
||||||
|
|
||||||
@@ -98,3 +99,13 @@ class Problem(models.Model):
|
|||||||
def add_ac_number(self):
|
def add_ac_number(self):
|
||||||
self.accepted_number = models.F("accepted_number") + 1
|
self.accepted_number = models.F("accepted_number") + 1
|
||||||
self.save(update_fields=["accepted_number"])
|
self.save(update_fields=["accepted_number"])
|
||||||
|
|
||||||
|
|
||||||
|
# class ProblemSet(models):
|
||||||
|
# title = models.CharField(max_length=20)
|
||||||
|
# subtitle = models.CharField(max_length=50)
|
||||||
|
# problems = models.ManyToManyField(Problem)
|
||||||
|
# badge = models.FilePathField(path=settings.UPLOAD_DIR)
|
||||||
|
# overviews = models.JSONField()
|
||||||
|
# create_time = models.DateTimeField(auto_now_add=True)
|
||||||
|
# update_time = models.DateTimeField(auto_now=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user