change enum
This commit is contained in:
@@ -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"),
|
||||
),
|
||||
]
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user