Skip to content

Commit

Permalink
Add filter by university groups: HSG, Institution, Guest (#350)
Browse files Browse the repository at this point in the history
* add option to filter by university groups: HSG, Institution, Guest

* small fixes

* reduce backend query

* fix error when local storage is empty

* restore submission_related

* Refactor, rename group to tag

* Remove None option, sort

---------

Co-authored-by: winprn <winprn@github.com>
Co-authored-by: Le Duy Thuc <leduythuc.cs@gmail.com>
  • Loading branch information
3 people committed Oct 21, 2023
1 parent 96cd6a0 commit 13689cf
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 14 deletions.
4 changes: 3 additions & 1 deletion judge/jinja2/reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,13 @@ def item_title(item):

@registry.function
def link_user(user):
is_contest_ranking_profile = False
if isinstance(user, Profile):
user, profile = user.user, user
elif isinstance(user, AbstractUser):
profile = user.profile
elif type(user).__name__ == 'ContestRankingProfile':
is_contest_ranking_profile = True
user, profile = user.user, user
else:
raise ValueError('Expected profile or user, got %s' % (type(user),))
Expand All @@ -160,7 +162,7 @@ def link_user(user):
else:
display_badge_img = ''

return mark_safe(f'<span class="{profile.css_class}">'
return mark_safe(f'<span data-user-tag="{ profile.group if is_contest_ranking_profile else None }" class="{profile.css_class}">'
f'<a href="{escape(reverse("user_page", args=[user.username]))}"'
f' style="display: inline-block;">'
f'{escape(profile.display_name)}</a>{display_badge_img}</span>')
Expand Down
9 changes: 6 additions & 3 deletions judge/management/commands/batch_add_icpc_team.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def generate_password():
return ''.join(secrets.choice(ALPHABET) for _ in range(8))


def add_user(username, teamname, password, org, internalid):
def add_user(username, teamname, password, org, org_group, internalid):
usr = User(username=username, is_active=True)
usr.set_password(password)
usr.save()
Expand All @@ -28,6 +28,7 @@ def add_user(username, teamname, password, org, internalid):
profile.language = Language.objects.get(key=settings.DEFAULT_USER_LANGUAGE)
profile.site_theme = 'light'
profile.notes = internalid # save the internal id for later use.
profile.group = org_group
profile.save()
profile.organizations.set([org])

Expand Down Expand Up @@ -68,7 +69,7 @@ def handle(self, *args, **options):
prefix = options['prefix']

reader = csv.DictReader(fin)
writer = csv.DictWriter(fout, fieldnames=['username', 'teamname', 'password'])
writer = csv.DictWriter(fout, fieldnames=['username', 'teamname', 'password', 'group'])
writer.writeheader()

done_team_ids = set()
Expand All @@ -79,16 +80,18 @@ def handle(self, *args, **options):
org = get_org(row['instName'])
password = generate_password()
internalid = row['id']
org_group = row['group']
if internalid in done_team_ids:
continue
done_team_ids.add(internalid)

add_user(username, teamname, password, org, internalid)
add_user(username, teamname, password, org, org_group, internalid)

writer.writerow({
'username': username,
'teamname': teamname,
'password': password,
'group': org_group
})

fin.close()
Expand Down
19 changes: 19 additions & 0 deletions judge/migrations/0199_add_user_group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.22 on 2023-10-18 07:02

from django.db import migrations, models
import judge.models.problem


class Migration(migrations.Migration):

dependencies = [
('judge', '0198_add_ranking_stop_last_minutes'),
]

operations = [
migrations.AddField(
model_name='profile',
name='group',
field=models.TextField(blank=True, null=True, verbose_name='uni group'),
),
]
2 changes: 2 additions & 0 deletions judge/models/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ class Profile(models.Model):
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.'))
# For ICPC only
group = models.TextField(verbose_name=_('uni group'), null=True, blank=True)

@cached_property
def organization(self):
Expand Down
3 changes: 2 additions & 1 deletion judge/views/contests.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ def get_context_data(self, **kwargs):

ContestRankingProfile = namedtuple(
'ContestRankingProfile',
'id user css_class username points cumtime tiebreaker organization participation '
'id user css_class username points cumtime tiebreaker organization participation group '
'participation_rating problem_cells result_cell virtual display_name',
)

Expand Down Expand Up @@ -870,6 +870,7 @@ def display_user_problem(contest_problem):
participation=participation,
virtual=participation.virtual,
display_name=user.display_name,
group=user.group,
)


Expand Down
2 changes: 1 addition & 1 deletion resources/contest.scss
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ form.contest-join-pseudotab {
}

#org-dropdown-check-list {
display: inline-block;
display: flex;
}

#org-check-list-wrapper {
Expand Down
10 changes: 10 additions & 0 deletions templates/contest/media-icpc-css.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,14 @@
font-size: 1.25em;
padding-bottom: -0.75em;
}

span.rating.top-team::after {
content: attr(data-user-tag);
font-size: 0.75em;
padding: 0.25em;
margin-left: 0.5em;
background-color: #FFC008;
border-radius: 2px;
color: black;
}
</style>
118 changes: 110 additions & 8 deletions templates/contest/ranking.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
.filter-checklist-button {
float: right;
}

.select-container {
display: flex;
gap: 2em;
}
</style>
{% endblock %}

Expand Down Expand Up @@ -81,6 +86,7 @@
window.enableAdminOperations();
window.loadOrgLogo();
window.applyFavoriteUsers();
window.getTagNamesAndSetTopTag();
}).always(function () {
ranking_outdated = false;
setTimeout(update_ranking, 10000);
Expand Down Expand Up @@ -225,12 +231,58 @@
});
});

$(function() {
$('#tag-check-list').select2({
theme: '{{ DMOJ_SELECT2_THEME }}',
multiple: true,
closeOnSelect: false,
placeholder: '{{ _('Filter by tag') }}',
});

const selection = $('#tag-check-list').data().select2.selection;
const results = $('#tag-check-list').data().select2.results;

$('#tag-check-list').on('select2:selecting', function(e) {
let id = e.params.args.data.id;
let val = $(e.target).val().concat([id]);
$(e.target).val(val).trigger('change');

if (selection.$search.val() != '') {
selection.$search.val('');
selection.trigger('query', {});
} else {
results.setClasses();
}

return false;
});

$('#tag-check-list').on('select2:unselecting', function(e) {
const id = e.params.args.data.id;
const val = $(e.target).val().filter(function(v) { return v != id; });
$(e.target).val(val).trigger('change');

if (selection.$search.val() != '') {
selection.$search.val('');
selection.trigger('query', {});
} else {
results.setClasses();
}

return false;
});
})

if (localStorage.getItem(`filter-cleared-${contest_key}`) === null) {
localStorage.setItem(`filter-cleared-${contest_key}`, 'true');
}

if (localStorage.getItem(`filter-selected-orgs-${contest_key}`) === null) {
localStorage.setItem(`filter-selected-orgs-${contest_key}`, []);
localStorage.setItem(`filter-selected-orgs-${contest_key}`, JSON.stringify([]));
}

if (localStorage.getItem(`filter-selected-tags-${contest_key}`) === null) {
localStorage.setItem(`filter-selected-tags-${contest_key}`, JSON.stringify([]));
}

$('#apply-organization-filter').click(function () {
Expand All @@ -240,12 +292,15 @@
$('#org-check-list').val().forEach(function (el) {
selected_orgs.push(el.trim());
});
let selected_tags = $('#tag-check-list').val().map(x => x.trim());
localStorage.setItem(`filter-selected-orgs-${contest_key}`, JSON.stringify(selected_orgs));
localStorage.setItem(`filter-selected-tags-${contest_key}`, JSON.stringify(selected_tags));
window.applyRankingFilter();
});

$('#clear-organization-filter').click(function () {
$('#org-check-list').val(null).trigger('change');
$('#tag-check-list').val(null).trigger('change');
$('#apply-organization-filter').click();
});

Expand All @@ -259,14 +314,18 @@
return;
}

if ($('#select2-org-check-list-results').has(e.target).length !== 0) {
if ($('#select2-org-check-list-results').has(e.target).length !== 0 || $('#select2-tag-check-list-results').has(e.target).length !== 0) {
return;
}

if ($(e.target).attr('id') && $(e.target).attr('id').startsWith('select2-org-check-list-result')) {
return;
}

if ($(e.target).attr('id') && $(e.target).attr('id').startsWith('select2-tag-check-list-result')) {
return;
}

// check if the clicked area is the checklist or not
if ($('#org-dropdown-check-list').has(e.target).length === 0) {
$('#apply-organization-filter').click();
Expand Down Expand Up @@ -301,6 +360,34 @@

window.getOrganizationCodes();

window.getTagNamesAndSetTopTag = function () {
const tags = [];
$('#ranking-table > tbody > tr[id] > td span').each(function () {
let row = $(this);

const user_tag = row.data('user-tag');

if (user_tag == undefined || user_tag == 'None') {
return;
}

if (tags.findIndex(x => x == user_tag) == -1) {
row.addClass('top-team');
tags.push(user_tag);
}
});

let tag_options = $('#tag-check-list');
tag_options.empty();
tags.sort();
tags.forEach(function (tag) {
tag_options.append(
`<option value="${tag}">${tag}</option>`
);
});
}
window.getTagNamesAndSetTopTag();

function extractCurrentAbsRank(row_text) {
let paren_surround_text = row_text.match(/\(([^)]+)\)/);
let current_abs_rank
Expand All @@ -327,8 +414,9 @@
let counter = 0;
let previous_abs_rank = -1;
let selected_orgs = JSON.parse(localStorage.getItem(`filter-selected-orgs-${contest_key}`));
let selected_tags = JSON.parse(localStorage.getItem(`filter-selected-tags-${contest_key}`));

if (!selected_orgs.length) {
if (!selected_orgs.length && !selected_tags.length) {
window.clearRankingFilter();
return;
}
Expand All @@ -338,15 +426,24 @@

let org_anchor = row.find("div > div > .personal-info > .organization > a")[0];
let org = org_anchor ? org_anchor.text : 'Other';
let tag_anchor = row.find("span")[0];
let tag = tag_anchor ? tag_anchor.dataset.userTag : 'Other'

let should_show_org = false;
let should_show_org = selected_orgs.length == 0;
selected_orgs.forEach(function (selected_org) {
if (selected_org === org.trim()) {
should_show_org = true;
}
});
let should_show_tag = selected_tags.length == 0;
selected_tags.forEach(function (selected_tag) {
if (selected_tag === tag.trim()) {
should_show_tag = true;
}
});

if (!should_show_org) {

if (!should_show_org || !should_show_tag) {
row.hide();
return;
}
Expand All @@ -370,8 +467,10 @@
window.applyRankingFilter();

window.restoreChecklistOptions = function () {
let selected_orgs = localStorage.getItem(`filter-selected-orgs-${contest_key}`).split(',');
let selected_orgs = JSON.parse(localStorage.getItem(`filter-selected-orgs-${contest_key}`));
$('#org-check-list').val(selected_orgs).trigger('change');
let selected_tags = JSON.parse(localStorage.getItem(`filter-selected-tags-${contest_key}`));
$('#tag-check-list').val(selected_tags).trigger('change');
};

window.restoreChecklistOptions();
Expand Down Expand Up @@ -566,11 +665,14 @@
</button>

<div id="org-check-list-wrapper">
<div>
<div style="margin-bottom: 30px;">
<button id="apply-organization-filter" class="inline-button filter-checklist-button">{{ _('Apply') }}</button>
<button id="clear-organization-filter" class="inline-button filter-checklist-button">{{ _('Clear') }}</button>
</div>
<select id="org-check-list" name="orgs[]" multiple="multiple"></select>
<div style="display: flex; gap: 2em;">
<select id="org-check-list" name="orgs[]" multiple="multiple"></select>
<select id="tag-check-list" name="tags[]" multiple="multiple"></select>
</div>
</div>
</div>
{% endif %}
Expand Down

0 comments on commit 13689cf

Please sign in to comment.