NDOJ/judge/admin/profile.py

224 lines
6.5 KiB
Python
Raw Normal View History

2020-01-21 06:35:58 +00:00
from django.contrib import admin
2024-09-18 02:11:40 +00:00
from django.forms import ModelForm, CharField, TextInput
2020-01-21 06:35:58 +00:00
from django.utils.html import format_html
from django.utils.translation import gettext, gettext_lazy as _, ungettext
2023-08-22 04:21:25 +00:00
from django.contrib.auth.admin import UserAdmin as OldUserAdmin
2024-09-18 02:11:40 +00:00
from django.core.exceptions import ValidationError
2020-01-21 06:35:58 +00:00
from django_ace import AceWidget
2024-09-18 02:11:40 +00:00
2024-04-27 03:51:16 +00:00
from judge.models import Profile, ProfileInfo
2020-01-21 06:35:58 +00:00
from judge.widgets import AdminPagedownWidget, AdminSelect2Widget
2024-09-18 02:11:40 +00:00
from reversion.admin import VersionAdmin
import re
2020-01-21 06:35:58 +00:00
class ProfileForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
2022-05-14 17:57:27 +00:00
if "current_contest" in self.base_fields:
2020-01-21 06:35:58 +00:00
# form.fields['current_contest'] does not exist when the user has only view permission on the model.
2022-05-14 17:57:27 +00:00
self.fields[
"current_contest"
].queryset = self.instance.contest_history.select_related("contest").only(
"contest__name", "user_id", "virtual"
)
self.fields["current_contest"].label_from_instance = (
lambda obj: "%s v%d" % (obj.contest.name, obj.virtual)
if obj.virtual
else obj.contest.name
)
2020-01-21 06:35:58 +00:00
class Meta:
widgets = {
2022-05-14 17:57:27 +00:00
"timezone": AdminSelect2Widget,
"language": AdminSelect2Widget,
"ace_theme": AdminSelect2Widget,
"current_contest": AdminSelect2Widget,
2020-01-21 06:35:58 +00:00
}
if AdminPagedownWidget is not None:
2022-05-14 17:57:27 +00:00
widgets["about"] = AdminPagedownWidget
2020-01-21 06:35:58 +00:00
class TimezoneFilter(admin.SimpleListFilter):
2022-05-14 17:57:27 +00:00
title = _("timezone")
parameter_name = "timezone"
2020-01-21 06:35:58 +00:00
def lookups(self, request, model_admin):
2022-05-14 17:57:27 +00:00
return (
Profile.objects.values_list("timezone", "timezone")
.distinct()
.order_by("timezone")
)
2020-01-21 06:35:58 +00:00
def queryset(self, request, queryset):
if self.value() is None:
return queryset
return queryset.filter(timezone=self.value())
2024-04-27 03:51:16 +00:00
class ProfileInfoInline(admin.StackedInline):
model = ProfileInfo
can_delete = False
verbose_name_plural = "profile info"
fk_name = "profile"
2020-01-21 06:35:58 +00:00
class ProfileAdmin(VersionAdmin):
2022-05-14 17:57:27 +00:00
fields = (
"user",
"display_rank",
"about",
"organizations",
"timezone",
"language",
"ace_theme",
"last_access",
"ip",
"mute",
"is_unlisted",
"notes",
"is_totp_enabled",
"current_contest",
)
readonly_fields = ("user",)
list_display = (
"admin_user_admin",
"email",
"is_totp_enabled",
"timezone_full",
"date_joined",
"last_access",
"ip",
"show_public",
)
ordering = ("user__username",)
search_fields = ("user__username", "ip", "user__email")
list_filter = ("language", TimezoneFilter)
actions = ("recalculate_points",)
2020-01-21 06:35:58 +00:00
actions_on_top = True
actions_on_bottom = True
form = ProfileForm
2024-04-27 03:51:16 +00:00
inlines = (ProfileInfoInline,)
2020-01-21 06:35:58 +00:00
def get_queryset(self, request):
2022-05-14 17:57:27 +00:00
return super(ProfileAdmin, self).get_queryset(request).select_related("user")
2020-01-21 06:35:58 +00:00
def get_fields(self, request, obj=None):
2022-05-14 17:57:27 +00:00
if request.user.has_perm("judge.totp"):
2020-01-21 06:35:58 +00:00
fields = list(self.fields)
2022-05-14 17:57:27 +00:00
fields.insert(fields.index("is_totp_enabled") + 1, "totp_key")
2020-01-21 06:35:58 +00:00
return tuple(fields)
else:
return self.fields
def get_readonly_fields(self, request, obj=None):
fields = self.readonly_fields
2022-05-14 17:57:27 +00:00
if not request.user.has_perm("judge.totp"):
fields += ("is_totp_enabled",)
2020-01-21 06:35:58 +00:00
return fields
def show_public(self, obj):
2022-05-14 17:57:27 +00:00
return format_html(
'<a href="{0}" style="white-space:nowrap;">{1}</a>',
obj.get_absolute_url(),
gettext("View on site"),
)
show_public.short_description = ""
2020-01-21 06:35:58 +00:00
def admin_user_admin(self, obj):
return obj.username
2022-05-14 17:57:27 +00:00
admin_user_admin.admin_order_field = "user__username"
admin_user_admin.short_description = _("User")
2020-01-21 06:35:58 +00:00
def email(self, obj):
2023-10-11 00:37:36 +00:00
return obj.email
2022-05-14 17:57:27 +00:00
email.admin_order_field = "user__email"
email.short_description = _("Email")
2020-01-21 06:35:58 +00:00
def timezone_full(self, obj):
return obj.timezone
2022-05-14 17:57:27 +00:00
timezone_full.admin_order_field = "timezone"
timezone_full.short_description = _("Timezone")
2020-01-21 06:35:58 +00:00
def date_joined(self, obj):
return obj.user.date_joined
2022-05-14 17:57:27 +00:00
date_joined.admin_order_field = "user__date_joined"
date_joined.short_description = _("date joined")
2020-01-21 06:35:58 +00:00
def recalculate_points(self, request, queryset):
count = 0
for profile in queryset:
profile.calculate_points()
count += 1
2022-05-14 17:57:27 +00:00
self.message_user(
request,
ungettext(
"%d user have scores recalculated.",
"%d users have scores recalculated.",
count,
)
% count,
)
recalculate_points.short_description = _("Recalculate scores")
2020-01-21 06:35:58 +00:00
2023-08-22 04:21:25 +00:00
2024-09-18 02:11:40 +00:00
class UserForm(ModelForm):
username = CharField(
max_length=150,
help_text=_("Username can only contain letters, digits, and underscores."),
widget=TextInput(attrs={"class": "vTextField"}),
)
def clean_username(self):
username = self.cleaned_data.get("username")
if not re.match(r"^\w+$", username):
raise ValidationError(
_("Username can only contain letters, digits, and underscores.")
)
return username
2023-08-22 04:21:25 +00:00
class UserAdmin(OldUserAdmin):
# Customize the fieldsets for adding and editing users
2024-09-18 02:11:40 +00:00
form = UserForm
2023-08-22 04:21:25 +00:00
fieldsets = (
(None, {"fields": ("username", "password")}),
("Personal Info", {"fields": ("first_name", "last_name", "email")}),
(
"Permissions",
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
)
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
readonly_fields = ("last_login", "date_joined")
def get_readonly_fields(self, request, obj=None):
fields = self.readonly_fields
if not request.user.is_superuser:
fields += (
"is_staff",
"is_active",
"is_superuser",
"groups",
"user_permissions",
)
return fields