change enum

This commit is contained in:
2026-05-09 02:30:47 -06:00
parent 78158471b2
commit c466dfd3c6
23 changed files with 451 additions and 503 deletions

View File

@@ -0,0 +1,22 @@
# Generated by Django 6.0.4 on 2026-05-09 08:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("account", "0003_remove_userprofile_class_name_user_class_name"),
]
operations = [
migrations.AlterField(
model_name="user",
name="admin_type",
field=models.TextField(choices=[("Regular User", "Regular User"), ("Admin", "Admin"), ("Super Admin", "Super Admin")], default="Regular User"),
),
migrations.AlterField(
model_name="user",
name="problem_permission",
field=models.TextField(choices=[("None", "None"), ("Own", "Own"), ("All", "All")], default="None"),
),
]

View File

@@ -5,16 +5,16 @@ from django.db import models
from utils.models import JSONField
class AdminType(object):
REGULAR_USER = "Regular User"
ADMIN = "Admin"
SUPER_ADMIN = "Super Admin"
class AdminType(models.TextChoices):
REGULAR_USER = "Regular User", "Regular User"
ADMIN = "Admin", "Admin"
SUPER_ADMIN = "Super Admin", "Super Admin"
class ProblemPermission(object):
NONE = "None"
OWN = "Own"
ALL = "All"
class ProblemPermission(models.TextChoices):
NONE = "None", "None"
OWN = "Own", "Own"
ALL = "All", "All"
class UserManager(models.Manager):
@@ -30,8 +30,8 @@ class User(AbstractBaseUser):
email = models.TextField(null=True)
create_time = models.DateTimeField(auto_now_add=True, null=True)
# One of UserType
admin_type = models.TextField(default=AdminType.REGULAR_USER)
problem_permission = models.TextField(default=ProblemPermission.NONE)
admin_type = models.TextField(default=AdminType.REGULAR_USER, choices=AdminType.choices)
problem_permission = models.TextField(default=ProblemPermission.NONE, choices=ProblemPermission.choices)
reset_password_token = models.TextField(null=True)
reset_password_token_expire_time = models.DateTimeField(null=True)
# SSO auth token
@@ -43,9 +43,7 @@ class User(AbstractBaseUser):
open_api = models.BooleanField(default=False)
open_api_appkey = models.TextField(null=True)
is_disabled = models.BooleanField(default=False)
raw_password = models.CharField(
max_length=20, null=True, blank=True, verbose_name="明文密码"
)
raw_password = models.CharField(max_length=20, null=True, blank=True, verbose_name="明文密码")
USERNAME_FIELD = "username"
REQUIRED_FIELDS = []
@@ -68,9 +66,7 @@ class User(AbstractBaseUser):
return self.problem_permission == ProblemPermission.ALL
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)
def set_password(self, raw_password):
super().set_password(raw_password)

View File

@@ -44,9 +44,7 @@ class GenerateUserSerializer(serializers.Serializer):
class ImportUserSerializer(serializers.Serializer):
users = serializers.ListField(
child=serializers.ListField(child=serializers.CharField(max_length=64))
)
users = serializers.ListField(child=serializers.ListField(child=serializers.CharField(max_length=64)))
class UserAdminSerializer(serializers.ModelSerializer):
@@ -118,21 +116,16 @@ class EditUserSerializer(serializers.Serializer):
id = serializers.IntegerField()
username = serializers.CharField(max_length=32)
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)
admin_type = serializers.ChoiceField(
choices=(AdminType.REGULAR_USER, AdminType.ADMIN, AdminType.SUPER_ADMIN)
)
problem_permission = serializers.ChoiceField(
choices=(ProblemPermission.NONE, ProblemPermission.OWN, ProblemPermission.ALL)
)
admin_type = serializers.ChoiceField(choices=AdminType.choices)
problem_permission = serializers.ChoiceField(choices=ProblemPermission.choices)
open_api = serializers.BooleanField()
two_factor_auth = serializers.BooleanField()
is_disabled = serializers.BooleanField()
class_name = serializers.CharField(required=False, allow_null=True, allow_blank=True)
class EditUserProfileSerializer(serializers.Serializer):
real_name = serializers.CharField(max_length=32, allow_null=True, required=False)
avatar = serializers.CharField(max_length=256, allow_blank=True, required=False)
@@ -143,6 +136,7 @@ class EditUserProfileSerializer(serializers.Serializer):
major = serializers.CharField(max_length=64, allow_blank=True, required=False)
language = serializers.CharField(max_length=32, allow_blank=True, required=False)
class ApplyResetPasswordSerializer(serializers.Serializer):
email = serializers.EmailField()
captcha = serializers.CharField()

View File

@@ -78,9 +78,7 @@ class UserProfileAPI(APIView):
show_real_name = True
except User.DoesNotExist:
return self.error("User does not exist")
return self.success(
UserProfileSerializer(user.userprofile, show_real_name=show_real_name).data
)
return self.success(UserProfileSerializer(user.userprofile, show_real_name=show_real_name).data)
@validate_serializer(EditUserProfileSerializer)
@login_required
@@ -90,9 +88,7 @@ class UserProfileAPI(APIView):
for k, v in data.items():
setattr(user_profile, k, v)
user_profile.save()
return self.success(
UserProfileSerializer(user_profile, show_real_name=True).data
)
return self.success(UserProfileSerializer(user_profile, show_real_name=True).data)
class Metrics(APIView):
@@ -157,9 +153,7 @@ class TwoFactorAuthAPI(APIView):
user.save()
label = f"{SysOptions.website_name_shortcut}:{user.username}"
image = qrcode.make(
_totp_uri(token, label, SysOptions.website_name.replace(" ", ""))
)
image = qrcode.make(_totp_uri(token, label, SysOptions.website_name.replace(" ", "")))
return self.success(img2base64(image))
@login_required
@@ -224,9 +218,7 @@ class UserLoginAPI(APIView):
if not user.two_factor_auth:
prev_login = user.last_login
auth.login(request, user)
request.session["prev_login"] = (
datetime2str(prev_login) if prev_login else ""
)
request.session["prev_login"] = datetime2str(prev_login) if prev_login else ""
return self.success("Succeeded")
# `tfa_code` not in post data
@@ -236,9 +228,7 @@ class UserLoginAPI(APIView):
if _valid_totp(user.tfa_token, data["tfa_code"]):
prev_login = user.last_login
auth.login(request, user)
request.session["prev_login"] = (
datetime2str(prev_login) if prev_login else ""
)
request.session["prev_login"] = datetime2str(prev_login) if prev_login else ""
return self.success("Succeeded")
else:
return self.error("Invalid two factor verification code")
@@ -262,9 +252,7 @@ class UsernameOrEmailCheck(APIView):
# True means already exist.
result = {"username": False, "email": False}
if data.get("username"):
result["username"] = User.objects.filter(
username=data["username"].lower()
).exists()
result["username"] = User.objects.filter(username=data["username"].lower()).exists()
if data.get("email"):
result["email"] = User.objects.filter(email=data["email"].lower()).exists()
return self.success(result)
@@ -301,9 +289,7 @@ class UserChangeEmailAPI(APIView):
@login_required
def post(self, request):
data = request.data
user = auth.authenticate(
username=request.user.username, password=data["password"]
)
user = auth.authenticate(username=request.user.username, password=data["password"])
if user:
if user.two_factor_auth:
if "tfa_code" not in data:
@@ -356,12 +342,7 @@ class ApplyResetPasswordAPI(APIView):
user = User.objects.get(email__iexact=data["email"])
except User.DoesNotExist:
return self.error("User does not exist")
if (
user.reset_password_token_expire_time
and 0
< int((user.reset_password_token_expire_time - now()).total_seconds())
< 20 * 60
):
if user.reset_password_token_expire_time and 0 < int((user.reset_password_token_expire_time - now()).total_seconds()) < 20 * 60:
return self.error("You can only reset password once per 20 minutes")
user.reset_password_token = rand_str()
user.reset_password_token_expire_time = now() + timedelta(minutes=20)
@@ -453,7 +434,7 @@ class UserRankAPI(APIView):
n = int(request.GET.get("n", "0"))
except ValueError:
n = 0
if rule_type not in ContestRuleType.choices():
if rule_type not in ContestRuleType.values:
rule_type = ContestRuleType.ACM
profiles = UserProfile.objects.filter(
@@ -462,9 +443,7 @@ class UserRankAPI(APIView):
user__username__icontains=username,
).select_related("user")
if rule_type == ContestRuleType.ACM:
profiles = profiles.filter(accepted_number__gte=0).order_by(
"-accepted_number", "submission_number"
)
profiles = profiles.filter(accepted_number__gte=0).order_by("-accepted_number", "submission_number")
else:
profiles = profiles.filter(total_score__gt=0).order_by("-total_score")
if n > 0:
@@ -482,19 +461,13 @@ class UserActivityRankAPI(APIView):
if cached is not None:
return self.success(cached)
hidden_names = User.objects.filter(
Q(admin_type=AdminType.SUPER_ADMIN) | Q(is_disabled=True)
).values_list("username", flat=True)
hidden_names = User.objects.filter(Q(admin_type=AdminType.SUPER_ADMIN) | Q(is_disabled=True)).values_list("username", flat=True)
submissions = Submission.objects.filter(
contest_id__isnull=True,
create_time__gte=start,
result=JudgeStatus.ACCEPTED,
).exclude(username__in=hidden_names)
data = list(
submissions.values("username")
.annotate(count=Count("problem_id", distinct=True))
.order_by("-count")[:10]
)
data = list(submissions.values("username").annotate(count=Count("problem_id", distinct=True)).order_by("-count")[:10])
cache.set(cache_key, data, 600)
return self.success(data)
@@ -506,12 +479,8 @@ class UserProblemRankAPI(APIView):
if not user.is_authenticated:
return self.error("User is not authenticated")
problem = Problem.objects.get(
_id=problem_id, contest_id__isnull=True, visible=True
)
submissions = Submission.objects.filter(
problem=problem, result=JudgeStatus.ACCEPTED
)
problem = Problem.objects.get(_id=problem_id, contest_id__isnull=True, visible=True)
submissions = Submission.objects.filter(problem=problem, result=JudgeStatus.ACCEPTED)
all_ac_count = submissions.values("user_id").distinct().count()
@@ -519,9 +488,7 @@ class UserProblemRankAPI(APIView):
class_ac_count = 0
if class_name:
users = User.objects.filter(
class_name=user.class_name, is_disabled=False
).values_list("id", flat=True)
users = User.objects.filter(class_name=user.class_name, is_disabled=False).values_list("id", flat=True)
user_ids = list(users)
submissions = submissions.filter(user_id__in=user_ids)
class_ac_count = submissions.values("user_id").distinct().count()
@@ -539,9 +506,7 @@ class UserProblemRankAPI(APIView):
)
my_first_submission = my_submissions.order_by("create_time").first()
rank = submissions.filter(
create_time__lte=my_first_submission.create_time
).count()
rank = submissions.filter(create_time__lte=my_first_submission.create_time).count()
return self.success(
{
"class_name": class_name,
@@ -561,9 +526,7 @@ class ProfileProblemDisplayIDRefreshAPI(APIView):
ids = list(acm_problems.keys()) + list(oi_problems.keys())
if not ids:
return self.success()
display_ids = Problem.objects.filter(id__in=ids, visible=True).values_list(
"_id", flat=True
)
display_ids = Problem.objects.filter(id__in=ids, visible=True).values_list("_id", flat=True)
id_map = dict(zip(ids, display_ids))
for k, v in acm_problems.items():
v["_id"] = id_map[k]