Skip to content

Commit 64da46b

Browse files
committed
Implement user display name override
Fixes #1745
1 parent 1570113 commit 64da46b

File tree

11 files changed

+53
-25
lines changed

11 files changed

+53
-25
lines changed

judge/admin/profile.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def has_add_permission(self, request):
5555

5656
class ProfileAdmin(NoBatchDeleteMixin, VersionAdmin):
5757
fields = ('user', 'display_rank', 'about', 'organizations', 'timezone', 'language', 'ace_theme',
58-
'math_engine', 'last_access', 'ip', 'mute', 'is_unlisted', 'notes', 'is_totp_enabled', 'user_script',
59-
'current_contest')
58+
'math_engine', 'last_access', 'ip', 'mute', 'is_unlisted', 'username_display_override',
59+
'notes', 'is_totp_enabled', 'user_script', 'current_contest')
6060
readonly_fields = ('user',)
6161
list_display = ('admin_user_admin', 'email', 'is_totp_enabled', 'timezone_full',
6262
'date_joined', 'last_access', 'ip', 'show_public')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.2.24 on 2021-08-09 01:42
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('judge', '0121_per_problem_sub_access_control'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='profile',
15+
name='username_display_override',
16+
field=models.CharField(blank=True, max_length=100, verbose_name='display name override', help_text='name displayed in place of username'),
17+
),
18+
]

judge/models/profile.py

+6
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ class Profile(models.Model):
141141
notes = models.TextField(verbose_name=_('internal notes'), null=True, blank=True,
142142
help_text=_('Notes for administrators regarding this user.'))
143143
data_last_downloaded = models.DateTimeField(verbose_name=_('last data download time'), null=True, blank=True)
144+
username_display_override = models.CharField(max_length=100, blank=True, verbose_name=_('display name override'),
145+
help_text=_('name displayed in place of username'))
144146

145147
@cached_property
146148
def organization(self):
@@ -152,6 +154,10 @@ def organization(self):
152154
def username(self):
153155
return self.user.username
154156

157+
@cached_property
158+
def display_name(self):
159+
return self.username_display_override or self.username
160+
155161
@cached_property
156162
def has_any_solves(self):
157163
return self.submission_set.filter(points=F('problem__points')).exists()

judge/views/contests.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ def get_context_data(self, **kwargs):
565565
ContestRankingProfile = namedtuple(
566566
'ContestRankingProfile',
567567
'id user css_class username points cumtime tiebreaker organization participation '
568-
'participation_rating problem_cells result_cell',
568+
'participation_rating problem_cells result_cell display_name',
569569
)
570570

571571
BestSolutionData = namedtuple('BestSolutionData', 'code points time state is_pretested')
@@ -594,6 +594,7 @@ def display_user_problem(contest_problem):
594594
problem_cells=[display_user_problem(contest_problem) for contest_problem in contest_problems],
595595
result_cell=contest.format.display_participation_result(participation),
596596
participation=participation,
597+
display_name=user.display_name,
597598
)
598599

599600

judge/views/select2.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,19 @@ def get(self, request, *args, **kwargs):
8282
self.gravatar_size = request.GET.get('gravatar_size', 128)
8383
self.gravatar_default = request.GET.get('gravatar_default', None)
8484

85-
self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank')
85+
self.object_list = self.get_queryset().values_list('pk', 'user__username', 'user__email', 'display_rank',
86+
'username_display_override')
8687

8788
context = self.get_context_data()
8889

8990
return JsonResponse({
9091
'results': [
9192
{
92-
'text': username,
93+
'text': username_override or username,
9394
'id': username,
9495
'gravatar_url': gravatar(email, self.gravatar_size, self.gravatar_default),
9596
'display_rank': display_rank,
96-
} for pk, username, email, display_rank in context['object_list']],
97+
} for pk, username, email, display_rank, username_override in context['object_list']],
9798
'more': context['page_obj'].has_next(),
9899
})
99100

judge/views/submission.py

+16-14
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def submission_related(queryset):
3333
.only('id', 'user__user__username', 'user__display_rank', 'user__rating', 'problem__name',
3434
'problem__code', 'problem__is_public', 'language__short_name', 'language__key', 'date', 'time', 'memory',
3535
'points', 'result', 'status', 'case_points', 'case_total', 'current_testcase', 'contest_object',
36-
'locked_after', 'problem__submission_source_visibility_mode') \
36+
'locked_after', 'problem__submission_source_visibility_mode', 'user__username_display_override') \
3737
.prefetch_related('contest_object__authors', 'contest_object__curators')
3838

3939

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

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

7171

@@ -342,7 +342,7 @@ def get_context_data(self, **kwargs):
342342
context['tab'] = 'my_submissions_tab'
343343
else:
344344
context['tab'] = 'user_submissions_tab'
345-
context['tab_username'] = self.profile.user.username
345+
context['tab_username'] = self.profile.display_name
346346
return context
347347

348348

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

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

364364
def get_my_submissions_page(self):
@@ -447,15 +447,17 @@ def get_queryset(self):
447447
def get_title(self):
448448
if self.is_own:
449449
return _("My submissions for %(problem)s") % {'problem': self.problem_name}
450-
return _("%(user)s's submissions for %(problem)s") % {'user': self.username, 'problem': self.problem_name}
450+
return _("%(user)s's submissions for %(problem)s") % {
451+
'user': self.profile.display_name, 'problem': self.problem_name,
452+
}
451453

452454
def get_content_title(self):
453455
if self.request.user.is_authenticated and self.request.profile == self.profile:
454456
return format_html('''My submissions for <a href="{3}">{2}</a>''',
455457
self.username, reverse('user_page', args=[self.username]),
456458
self.problem_name, reverse('problem_detail', args=[self.problem.code]))
457459
return format_html('''<a href="{1}">{0}</a>'s submissions for <a href="{3}">{2}</a>''',
458-
self.username, reverse('user_page', args=[self.username]),
460+
self.profile.display_name, reverse('user_page', args=[self.username]),
459461
self.problem_name, reverse('problem_detail', args=[self.problem.code]))
460462

461463
def get_context_data(self, **kwargs):
@@ -553,7 +555,7 @@ def get_title(self):
553555
if self.is_own:
554556
return _('My submissions in %(contest)s') % {'contest': self.contest.name}
555557
return _("%(user)s's submissions in %(contest)s") % {
556-
'user': self.username,
558+
'user': self.profile.display_name,
557559
'contest': self.contest.name,
558560
}
559561

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

575577
def get_queryset(self):
@@ -583,9 +585,9 @@ def get_queryset(self):
583585
class UserContestSubmissions(ForceContestMixin, UserProblemSubmissions):
584586
def get_title(self):
585587
if self.problem.is_accessible_by(self.request.user):
586-
return "%s's submissions for %s in %s" % (self.username, self.problem_name, self.contest.name)
588+
return "%s's submissions for %s in %s" % (self.profile.display_name, self.problem_name, self.contest.name)
587589
return "%s's submissions for problem %s in %s" % (
588-
self.username, self.get_problem_number(self.problem), self.contest.name)
590+
self.profile.display_name, self.get_problem_number(self.problem), self.contest.name)
589591

590592
def access_check(self, request):
591593
super(UserContestSubmissions, self).access_check(request)
@@ -596,11 +598,11 @@ def get_content_title(self):
596598
if self.problem.is_accessible_by(self.request.user):
597599
return format_html(_('<a href="{1}">{0}</a>\'s submissions for '
598600
'<a href="{3}">{2}</a> in <a href="{5}">{4}</a>'),
599-
self.username, reverse('user_page', args=[self.username]),
601+
self.profile.display_name, reverse('user_page', args=[self.username]),
600602
self.problem_name, reverse('problem_detail', args=[self.problem.code]),
601603
self.contest.name, reverse('contest_view', args=[self.contest.key]))
602604
return format_html(_('<a href="{1}">{0}</a>\'s submissions for '
603605
'problem {2} in <a href="{4}">{3}</a>'),
604-
self.username, reverse('user_page', args=[self.username]),
606+
self.profile.display_name, reverse('user_page', args=[self.username]),
605607
self.get_problem_number(self.problem),
606608
self.contest.name, reverse('contest_view', args=[self.contest.key]))

judge/views/user.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def dispatch(self, request, *args, **kwargs):
8080

8181
def get_title(self):
8282
return (_('My account') if self.request.user == self.object.user else
83-
_('User %s') % self.object.user.username)
83+
_('User %s') % self.object.display_name)
8484

8585
# TODO: the same code exists in problem.py, maybe move to problems.py?
8686
@cached_property

templates/base.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@
211211
<span>
212212
<img src="{{ gravatar(request.user, 32) }}" height="24" width="24">{# -#}
213213
<span>
214-
{%- trans username=request.user.username -%}
214+
{%- trans username=request.profile.display_name -%}
215215
Hello, <b>{{ username }}</b>.
216216
{%- endtrans %}
217217
</span>

templates/ticket/message.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="info">
33
<a href="{{ url('user_page', message.user.user.username) }}" class="user">
44
<img src="{{ gravatar(message.user, 135) }}" class="gravatar">
5-
<div class="username {{ message.user.css_class }}">{{ message.user.user.username }}</div>
5+
<div class="username {{ message.user.css_class }}">{{ message.user.display_name }}</div>
66
</a>
77
</div>
88
<div class="detail">

templates/ticket/ticket.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@
329329
<div class="info">
330330
<a href="{{ url('user_page', request.user.username) }}" class="user">
331331
<img src="{{ gravatar(request.user, 135) }}" class="gravatar">
332-
<div class="username {{ request.profile.css_class }}">{{ request.user.username }}</div>
332+
<div class="username {{ request.profile.css_class }}">{{ request.profile.display_name }}</div>
333333
</a>
334334
</div>
335335
<div class="detail">

templates/user/link.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<span class="{{ profile.css_class }}"><a href="{{ url('user_page', user.username) }}">{{ user.username }}</a></span>
1+
<span class="{{ profile.css_class }}"><a href="{{ url('user_page', user.username) }}">{{ profile.display_name }}</a></span>

0 commit comments

Comments
 (0)