Skip to content

Commit

Permalink
Implement user display name override
Browse files Browse the repository at this point in the history
Fixes #1745
  • Loading branch information
quantum5 committed Aug 10, 2021
1 parent 1570113 commit 64da46b
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 25 deletions.
4 changes: 2 additions & 2 deletions judge/admin/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def has_add_permission(self, request):

class ProfileAdmin(NoBatchDeleteMixin, VersionAdmin):
fields = ('user', 'display_rank', 'about', 'organizations', 'timezone', 'language', 'ace_theme',
'math_engine', 'last_access', 'ip', 'mute', 'is_unlisted', 'notes', 'is_totp_enabled', 'user_script',
'current_contest')
'math_engine', 'last_access', 'ip', 'mute', 'is_unlisted', 'username_display_override',
'notes', 'is_totp_enabled', 'user_script', 'current_contest')
readonly_fields = ('user',)
list_display = ('admin_user_admin', 'email', 'is_totp_enabled', 'timezone_full',
'date_joined', 'last_access', 'ip', 'show_public')
Expand Down
18 changes: 18 additions & 0 deletions judge/migrations/0122_username_display_override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.24 on 2021-08-09 01:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('judge', '0121_per_problem_sub_access_control'),
]

operations = [
migrations.AddField(
model_name='profile',
name='username_display_override',
field=models.CharField(blank=True, max_length=100, verbose_name='display name override', help_text='name displayed in place of username'),
),
]
6 changes: 6 additions & 0 deletions judge/models/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ class Profile(models.Model):
notes = models.TextField(verbose_name=_('internal notes'), null=True, blank=True,
help_text=_('Notes for administrators regarding this user.'))
data_last_downloaded = models.DateTimeField(verbose_name=_('last data download time'), null=True, blank=True)
username_display_override = models.CharField(max_length=100, blank=True, verbose_name=_('display name override'),
help_text=_('name displayed in place of username'))

@cached_property
def organization(self):
Expand All @@ -152,6 +154,10 @@ def organization(self):
def username(self):
return self.user.username

@cached_property
def display_name(self):
return self.username_display_override or self.username

@cached_property
def has_any_solves(self):
return self.submission_set.filter(points=F('problem__points')).exists()
Expand Down
3 changes: 2 additions & 1 deletion judge/views/contests.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ def get_context_data(self, **kwargs):
ContestRankingProfile = namedtuple(
'ContestRankingProfile',
'id user css_class username points cumtime tiebreaker organization participation '
'participation_rating problem_cells result_cell',
'participation_rating problem_cells result_cell display_name',
)

BestSolutionData = namedtuple('BestSolutionData', 'code points time state is_pretested')
Expand Down Expand Up @@ -594,6 +594,7 @@ def display_user_problem(contest_problem):
problem_cells=[display_user_problem(contest_problem) for contest_problem in contest_problems],
result_cell=contest.format.display_participation_result(participation),
participation=participation,
display_name=user.display_name,
)


Expand Down
7 changes: 4 additions & 3 deletions judge/views/select2.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,19 @@ def get(self, request, *args, **kwargs):
self.gravatar_size = request.GET.get('gravatar_size', 128)
self.gravatar_default = request.GET.get('gravatar_default', None)

self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank')
self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank',
'username_display_override')

context = self.get_context_data()

return JsonResponse({
'results': [
{
'text': username,
'text': username_override or username,
'id': username,
'gravatar_url': gravatar(email, self.gravatar_size, self.gravatar_default),
'display_rank': display_rank,
} for pk, username, email, display_rank in context['object_list']],
} for pk, username, email, display_rank, username_override in context['object_list']],
'more': context['page_obj'].has_next(),
})

Expand Down
30 changes: 16 additions & 14 deletions judge/views/submission.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def submission_related(queryset):
.only('id', 'user__user__username', 'user__display_rank', 'user__rating', 'problem__name',
'problem__code', 'problem__is_public', 'language__short_name', 'language__key', 'date', 'time', 'memory',
'points', 'result', 'status', 'case_points', 'case_total', 'current_testcase', 'contest_object',
'locked_after', 'problem__submission_source_visibility_mode') \
'locked_after', 'problem__submission_source_visibility_mode', 'user__username_display_override') \
.prefetch_related('contest_object__authors', 'contest_object__curators')


Expand All @@ -54,7 +54,7 @@ def get_title(self):
submission = self.object
return _('Submission of %(problem)s by %(user)s') % {
'problem': submission.problem.translated_name(self.request.LANGUAGE_CODE),
'user': submission.user.user.username,
'user': submission.user.display_name,
}

def get_content_title(self):
Expand All @@ -65,7 +65,7 @@ def get_content_title(self):
submission.problem.translated_name(self.request.LANGUAGE_CODE)),
'user': format_html('<a href="{0}">{1}</a>',
reverse('user_page', args=[submission.user.user.username]),
submission.user.user.username),
submission.user.display_name),
})


Expand Down Expand Up @@ -342,7 +342,7 @@ def get_context_data(self, **kwargs):
context['tab'] = 'my_submissions_tab'
else:
context['tab'] = 'user_submissions_tab'
context['tab_username'] = self.profile.user.username
context['tab_username'] = self.profile.display_name
return context


Expand All @@ -353,12 +353,12 @@ def get_queryset(self):
def get_title(self):
if self.is_own:
return _('All my submissions')
return _('All submissions by %s') % self.username
return _('All submissions by %s') % self.profile.display_name

def get_content_title(self):
if self.is_own:
return format_html('All my submissions')
return format_html('All submissions by <a href="{1}">{0}</a>', self.username,
return format_html('All submissions by <a href="{1}">{0}</a>', self.profile.display_name,
reverse('user_page', args=[self.username]))

def get_my_submissions_page(self):
Expand Down Expand Up @@ -447,15 +447,17 @@ def get_queryset(self):
def get_title(self):
if self.is_own:
return _("My submissions for %(problem)s") % {'problem': self.problem_name}
return _("%(user)s's submissions for %(problem)s") % {'user': self.username, 'problem': self.problem_name}
return _("%(user)s's submissions for %(problem)s") % {
'user': self.profile.display_name, 'problem': self.problem_name,
}

def get_content_title(self):
if self.request.user.is_authenticated and self.request.profile == self.profile:
return format_html('''My submissions for <a href="{3}">{2}</a>''',
self.username, reverse('user_page', args=[self.username]),
self.problem_name, reverse('problem_detail', args=[self.problem.code]))
return format_html('''<a href="{1}">{0}</a>'s submissions for <a href="{3}">{2}</a>''',
self.username, reverse('user_page', args=[self.username]),
self.profile.display_name, reverse('user_page', args=[self.username]),
self.problem_name, reverse('problem_detail', args=[self.problem.code]))

def get_context_data(self, **kwargs):
Expand Down Expand Up @@ -553,7 +555,7 @@ def get_title(self):
if self.is_own:
return _('My submissions in %(contest)s') % {'contest': self.contest.name}
return _("%(user)s's submissions in %(contest)s") % {
'user': self.username,
'user': self.profile.display_name,
'contest': self.contest.name,
}

Expand All @@ -569,7 +571,7 @@ def get_content_title(self):
return format_html(_('My submissions in <a href="{1}">{0}</a>'),
self.contest.name, reverse("contest_view", args=[self.contest.key]))
return format_html(_('<a href="{1}">{0}</a>\'s submissions in <a href="{3}">{2}</a>'),
self.username, reverse('user_page', args=[self.username]),
self.profile.display_name, reverse('user_page', args=[self.username]),
self.contest.name, reverse('contest_view', args=[self.contest.key]))

def get_queryset(self):
Expand All @@ -583,9 +585,9 @@ def get_queryset(self):
class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions):
def get_title(self):
if self.problem.is_accessible_by(self.request.user):
return "%s's submissions for %s in %s" % (self.username, self.problem_name, self.contest.name)
return "%s's submissions for %s in %s" % (self.profile.display_name, self.problem_name, self.contest.name)
return "%s's submissions for problem %s in %s" % (
self.username, self.get_problem_number(self.problem), self.contest.name)
self.profile.display_name, self.get_problem_number(self.problem), self.contest.name)

def access_check(self, request):
super(UserContestSubmissions, self).access_check(request)
Expand All @@ -596,11 +598,11 @@ def get_content_title(self):
if self.problem.is_accessible_by(self.request.user):
return format_html(_('<a href="{1}">{0}</a>\'s submissions for '
'<a href="{3}">{2}</a> in <a href="{5}">{4}</a>'),
self.username, reverse('user_page', args=[self.username]),
self.profile.display_name, reverse('user_page', args=[self.username]),
self.problem_name, reverse('problem_detail', args=[self.problem.code]),
self.contest.name, reverse('contest_view', args=[self.contest.key]))
return format_html(_('<a href="{1}">{0}</a>\'s submissions for '
'problem {2} in <a href="{4}">{3}</a>'),
self.username, reverse('user_page', args=[self.username]),
self.profile.display_name, reverse('user_page', args=[self.username]),
self.get_problem_number(self.problem),
self.contest.name, reverse('contest_view', args=[self.contest.key]))
2 changes: 1 addition & 1 deletion judge/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def dispatch(self, request, *args, **kwargs):

def get_title(self):
return (_('My account') if self.request.user == self.object.user else
_('User %s') % self.object.user.username)
_('User %s') % self.object.display_name)

# TODO: the same code exists in problem.py, maybe move to problems.py?
@cached_property
Expand Down
2 changes: 1 addition & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
<span>
<img src="{{ gravatar(request.user, 32) }}" height="24" width="24">{# -#}
<span>
{%- trans username=request.user.username -%}
{%- trans username=request.profile.display_name -%}
Hello, <b>{{ username }}</b>.
{%- endtrans %}
</span>
Expand Down
2 changes: 1 addition & 1 deletion templates/ticket/message.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="info">
<a href="{{ url('user_page', message.user.user.username) }}" class="user">
<img src="{{ gravatar(message.user, 135) }}" class="gravatar">
<div class="username {{ message.user.css_class }}">{{ message.user.user.username }}</div>
<div class="username {{ message.user.css_class }}">{{ message.user.display_name }}</div>
</a>
</div>
<div class="detail">
Expand Down
2 changes: 1 addition & 1 deletion templates/ticket/ticket.html
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@
<div class="info">
<a href="{{ url('user_page', request.user.username) }}" class="user">
<img src="{{ gravatar(request.user, 135) }}" class="gravatar">
<div class="username {{ request.profile.css_class }}">{{ request.user.username }}</div>
<div class="username {{ request.profile.css_class }}">{{ request.profile.display_name }}</div>
</a>
</div>
<div class="detail">
Expand Down
2 changes: 1 addition & 1 deletion templates/user/link.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<span class="{{ profile.css_class }}"><a href="{{ url('user_page', user.username) }}">{{ user.username }}</a></span>
<span class="{{ profile.css_class }}"><a href="{{ url('user_page', user.username) }}">{{ profile.display_name }}</a></span>

0 comments on commit 64da46b

Please sign in to comment.