Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add filter by university groups: HSG, Institution, Guest #350

Merged
merged 8 commits into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading