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

Update contest scoreboard #123

Closed
wants to merge 47 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bbb0a51
Show all results publiclly.
puqeko Apr 17, 2020
23aa9ed
Add description about Public/Protected/Private to new contest form.
puqeko Apr 18, 2020
c600659
Show contest problem set
puqeko Apr 18, 2020
937df69
Update contest scoreboard and edit page plus remove info tab.
puqeko Apr 18, 2020
8b9d651
Fix indentation, add trailing newlines
Holmes98 Apr 20, 2020
7d6ee47
Only show ranks for official contestants
Holmes98 Apr 20, 2020
c2a1cb6
Add checkbox to show unofficial contestants
Holmes98 Apr 20, 2020
2482fe7
Revert lowered opacity for unofficial contestants
Holmes98 Apr 20, 2020
6a0aefa
Add back problem columns and hide scores
Holmes98 Apr 20, 2020
aca4daa
Require school to be blank if outside NZ
Holmes98 Apr 21, 2020
cab715a
Update contest form descriptions
Holmes98 Apr 21, 2020
7190e34
Fix default overriding existing contest duration
Holmes98 Apr 21, 2020
48a2b04
Fix contest scoreboard with deleted users
Holmes98 Apr 21, 2020
d78da20
Fix unofficial contestant filter on Safari
Holmes98 Apr 21, 2020
6f92807
Persist checkbox state using localStorage
Holmes98 Apr 21, 2020
d4f97fd
Hide entire scoreboard if live scoreboard is disabled
Holmes98 Feb 6, 2021
cbaaa99
Display the flag of the country from contest relation
Holmes98 Feb 6, 2021
3c838aa
Rename competitors to contestants
Holmes98 Feb 6, 2021
e4c35fe
Remove schools of users outside New Zealand
Holmes98 Feb 6, 2021
cbd73df
Rewrite descriptions of scoreboard checkboxes (from code review)
tom93 Feb 7, 2021
62d6dca
Refactor logic for showing scoreboard (suggestion from code review)
tom93 Feb 7, 2021
1c87bd6
Fix bug in logic for hiding scoreboard rows of other users (suggestio…
tom93 Feb 7, 2021
265915e
Revert default 3-hour contest duration
tom93 Feb 7, 2021
12771f3
Edit description of Public/Protected/Private in new contest form to r…
tom93 Feb 7, 2021
620faf8
Rename column "show_unofficial_contestants" to "only_rank_official_co…
tom93 Feb 7, 2021
5efcbd1
Fix bug in ranking when there are unofficial contestants
tom93 Feb 7, 2021
563fc76
Convert variable is_unofficial to is_ranked (its negation)
tom93 Feb 7, 2021
8ba5b9c
Fix syntax in JavaScript code to toggle visibility of unofficial cont…
tom93 Feb 7, 2021
7310e63
Use "save!" instead of "save" in remove_schools_for_foreign_users mig…
tom93 Feb 7, 2021
b07fbad
Introduce variable "is_record_of_current_user" in scoreboard view
tom93 Feb 7, 2021
0ff54a9
Fix indentation
tom93 Feb 7, 2021
a817833
Add back "-" to checkbox labels
tom93 Feb 7, 2021
9221380
Fix <div> closing tags in contest form
tom93 Feb 18, 2021
5c9ccb4
Remove redundant <br> elements after checkbox labels in contest form
tom93 Feb 18, 2021
03466fd
Make label for only_rank_official_contestants checkbox more concise
tom93 Feb 19, 2021
8ca52a1
Clarify that unofficial are are still displayed on the scoreboard
tom93 Feb 19, 2021
e238c34
Add the "info" tab back
tom93 Feb 19, 2021
1e89762
Update "info" tab to show whether contest scoreboard is live or hidden
tom93 Feb 19, 2021
df5c053
Remove invalid <p> element around problems table in contest page
tom93 Feb 19, 2021
13be93b
fixup! Show contest problem set
tom93 Feb 19, 2021
0983a79
fixup! Add checkbox to show unofficial contestants
tom93 Feb 19, 2021
5cc4f3a
Remove <br> element before submit button in contest form
tom93 Feb 18, 2021
5b24ce3
Add condition to unofficial contestants script
Holmes98 Feb 19, 2021
6318be8
Clarify label for only_rank_official_contestants checkbox
eric-ide Feb 20, 2021
aede9c9
Revert "Use "save!" instead of "save" in remove_schools_for_foreign_u…
Holmes98 Feb 28, 2021
cfa7005
Revert "Remove schools of users outside New Zealand"
Holmes98 Feb 28, 2021
cc3355d
Revert school abscence validation for users outside NZ
Holmes98 Feb 28, 2021
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
1 change: 0 additions & 1 deletion app/assets/stylesheets/main_table.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,3 @@ table.main_table > tbody > tr > td, table.main_table > thead > tr > th {
table.main_table > thead > tr > th {
font-weight: bold;
}

13 changes: 2 additions & 11 deletions app/controllers/contests_controller.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class ContestsController < ApplicationController
def permitted_params
@_permitted_params ||= begin
permitted_attributes = [:name, :start_time, :end_time, :duration, :problem_set_id, :startcode, :observation]
permitted_attributes = [:name, :start_time, :end_time, :duration, :problem_set_id, :startcode, :observation, :live_scoreboard, :show_unofficial_contestants]
permitted_attributes << :owner_id if policy(@contest || Contest).transfer?
params.require(:contest).permit(*permitted_attributes)
end
Expand Down Expand Up @@ -49,7 +49,7 @@ def browse
def show
@contest = Contest.find(params[:id])
if !policy(@contest).overview?
redirect_to info_contest_path(@contest)
redirect_to scoreboard_contest_path(@contest)
return
end
@problem_associations = @contest.problem_set.problem_associations.includes(:problem)
Expand All @@ -64,15 +64,6 @@ def show
render :layout => 'contest'
end

def info
@contest = Contest.find(params[:id])
authorize @contest, :show?
@groups = Group.all
@contest_relation = @contest.get_relation(current_user.id) if user_signed_in?

render :layout => 'contest'
end

def scoreboard
@contest = Contest.find(params[:id])
authorize @contest, :scoreboard?
Expand Down
2 changes: 1 addition & 1 deletion app/models/contest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def ended?
end

def scoreboard
scoreboard = self.contestant_records.select([:score, :time_taken, :user_id]).order("contest_relations.score DESC, time_taken").includes(:user)
scoreboard = self.contestant_records.select([:score, :time_taken, :user_id, :school_id, :school_year, :country_code]).order("contest_relations.score DESC, time_taken").includes(:user)
problem_set.problems.each do |problem| # for each problem, query problem score as well
scoreboard = scoreboard.select("scores_#{problem.id}.score AS score_#{problem.id}, scores_#{problem.id}.attempt AS attempt_#{problem.id}, scores_#{problem.id}.attempts AS attempts_#{problem.id}, scores_#{problem.id}.submission_id AS sub_#{problem.id}").joins("LEFT OUTER JOIN contest_scores AS scores_#{problem.id} ON scores_#{problem.id}.contest_relation_id = contest_relations.id AND scores_#{problem.id}.problem_id = #{problem.id}")
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/contest_relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ def status_text
return "You have been registered, but the contest has not started yet." if !started?
end

def country_name
country = ISO3166::Country[country_code || 'NZ']
country.name
end

def start! checkin = true
return false if started?
self.checked_in = true
Expand Down
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class User < ActiveRecord::Base
validates :name, :length => {:maximum => 100, :minimum => 2}
validates :username, :length => { :in => 2..32 }, :format => { :with => /\A\w(\.?\w)*\z/, :message => "Only letters, numbers, underscores or non-adjacent dots allowed in username" }, :presence => true, :uniqueness => { :case_sensitive => false }
validates :avatar, :file_size => { :maximum => 40.kilobytes.to_i }
validates :school, :absence => { message: "must be blank if outside New Zealand"}, if: -> {country_code != "NZ"}

before_save do
self.can_change_username = false if self.username_changed? # can only change username once
Expand Down
4 changes: 4 additions & 0 deletions app/views/contests/_admin.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@
<% end %>
</ul>

<p>
<b> Problem set: </b>
<%= link_to @contest.problem_set.name, @contest.problem_set %>
Holmes98 marked this conversation as resolved.
Show resolved Hide resolved
</p>
21 changes: 18 additions & 3 deletions app/views/contests/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@
</div>
<div class="field">
<%= f.label :duration %><br />
<%= f.text_field :duration %>
<%= f.text_field :duration, :value => @contest.duration || 3.0 %> hours
tom93 marked this conversation as resolved.
Show resolved Hide resolved
</div>
<div class="field">
<%= f.label :observation %><br />
<%= f.select :observation, Contest::OBSERVATION.entries.invert %>
<%= f.select :observation, Contest::OBSERVATION.entries.invert %><br />
Users can only join a contest if they have been added manually, or belong to a group with access to the contest.
<ul>
<li>Public: Anyone can see the contest.</li>
<li>Protected: Only those in one of the contest's groups can see the contest.</li>
<li>Private: Only those explicitly added as contestants can see the contest.</li>
</ul>
tom93 marked this conversation as resolved.
Show resolved Hide resolved

</div>
<div class="field">
<%= f.label :startcode %><br />
Expand All @@ -46,8 +53,16 @@
<% else %>
<%= handle(@contest.owner) %>
<% end %>

</div>
<div class="field">
<%= f.check_box :live_scoreboard %>
<%= f.label :live_scoreboard %> - If checked, other users' scores are shown during the contest. Otherwise, you may only see your own scores until the end of the contest.<br />
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<div>
<div class="field">
<%= f.check_box :show_unofficial_contestants %>
<%= f.label :show_unofficial_contestants %> - If checked, unofficial contestants are not ranked. An offical contestant is any student enrolled in a NZ school.<br />
tom93 marked this conversation as resolved.
Show resolved Hide resolved
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<div>
<br>
<div class="actions">
<%= f.submit %>
</div>
Expand Down
9 changes: 0 additions & 9 deletions app/views/contests/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,3 @@
<p>
<%= render 'form' %>
</p>
<% if false # COMMENTED %>
<p>
<b> Problems: </b>
<% @problems.each do |problem| %>
<%= link_to problem.name, problem_path(problem) %>
(<%= link_to "remove", :controller => "problem_contest", :action => "remove", :method => "post", :problem_id => problem, :contest_id => @contest %>)
<% end %>
</p>
<% end %>
20 changes: 0 additions & 20 deletions app/views/contests/info.html.erb

This file was deleted.

59 changes: 43 additions & 16 deletions app/views/contests/scoreboard.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@
//-->
</script>
<% end %>
<% show_scoreboard = @contest.live_scoreboard || @contest.ended? || policy(@contest).inspect? %>
<% if [email protected]? %>
<em>The contest has not started yet.</em>
<% elsif !show_scoreboard && (!user_signed_in? || [email protected]_competitor?(current_user)) %>
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<em>The scoreboard is hidden until the end of the contest.</em>
<% else %>
<% if @contest.show_unofficial_contestants && show_scoreboard %>
<div style="float:right">
<input type="checkbox" id="show_unofficial" onclick="toggle_unofficial()">
<label for="show_unofficial">Show Unofficial Contestants</label>
</div>
<% end %>
<h2><%= @contest.finalized? ? "Final Results" : "Preliminary Results" %></h2>

<table class="main_table" >
<table class="main_table">
<thead>
<tr>
<th style="min-width: 25%;"> User </th>
<th style="width:1%; text-align: right"> # </th>
<th style="width:1%"></th>
<th>Username</th>
<% @contest.problem_set.problems.each_with_index do |problem, prob_num|%>
<th style="min-width: 6em;">
<th >
<% if policy(@contest).show_details? %>
<%= link_to problem.name, problem_path(problem) %>
<% else %>
Expand All @@ -29,20 +39,25 @@
(<%= @weighting[problem.id] %>)
</th>
<% end %>
<th style="text-align: right"> Score </th>
<th> Time </th>
<th> Rank </th>
<th style="width:1%; text-align: right"> Score </th>
<th style="width:1%"> Time </th>
</tr>
</thead>
<tbody>
<% median = @scoreboard[@scoreboard.length/2-1] %>
<% rank = 1 %>
<% num_unofficial = 0 %>
<% previous_record = @scoreboard.first %>
<% @scoreboard.each_with_index do |record,index| %>
<% if !user_signed_in? or record.user && record.user.id != current_user.id && !policy(@contest).inspect? %>
<% next if record.score < median.score || (record.score == median.score && record.time_taken > median.time_taken) # no permission to view %>
<% end %>
<tr <% if user_signed_in? && record.user && record.user.id == current_user.id %> class="emphasized"<% end %>>
<% next if !show_scoreboard && (!user_signed_in? or record.user && record.user.id != current_user.id) # no permission to view %>
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<% is_unofficial = @contest.show_unofficial_contestants && (record.school.nil? || record.school_year.nil?) %>
<tr class="<% if user_signed_in? && record.user && record.user.id == current_user.id %>emphasized<% end %><% if is_unofficial %> unofficial<% end %>">
<% num_unofficial += 1 if is_unofficial %>
<% rank = index + 1 - num_unofficial unless previous_record.score == record.score && previous_record.time_taken == record.time_taken %>
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<% previous_record = record %>
<td style="text-align: right"> <%= rank unless is_unofficial || !show_scoreboard %> </td>
tom93 marked this conversation as resolved.
Show resolved Hide resolved
<td style="padding-left: 0; padding-right: 0;">
<%= flag_list(24){ flag(record.country_code.downcase, record.country_name, title: true) } if record.country_code %>
</td>
<td>
<% if record.user %>
<%= link_to handle(record.user), record.user %>
Expand All @@ -52,7 +67,7 @@
</td>
<% link_to_submissions = user_signed_in? && record.user && (record.user.id == current_user.id || current_user.is_admin?) %>
<% @problems.each do |prob|%>
<td style="min-width: 5em;">
<td>
<span style="float: left; width: 2em; text-align: right;">
<%= record["score_#{prob.id}"].nil? ? "-":link_to_if(link_to_submissions, record["score_#{prob.id}"], submission_path(record["sub_#{prob.id}"])) %>
</span>
Expand All @@ -66,11 +81,23 @@
<% end %>
<td style="text-align: right"> <%= record.score %> </td>
<td> <%= format("%d:%02d:%02d",record.time_taken.to_i/3600,record.time_taken.to_i/60%60,record.time_taken.to_i%60) %> </td>
<% rank = index + 1 unless previous_record.score == record.score and previous_record.time_taken == record.time_taken %>
<% previous_record = record %>
<td align="right"> <%= rank %> </td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
<script>
function toggle_unofficial() {
show_unofficial_contestants = document.getElementById("show_unofficial").checked;
unofficial = document.getElementsByClassName("unofficial");
tom93 marked this conversation as resolved.
Show resolved Hide resolved
for (let row of unofficial) {
if (row.classList.contains("emphasized")) continue // always show current user
tom93 marked this conversation as resolved.
Show resolved Hide resolved
// row.style.visibility = show_unofficial_contestants ? "visible" : "collapse"; // has issues on safari
row.style.display = show_unofficial_contestants ? "table-row" : "none"; // can cause column widths to change
}
localStorage.setItem("show_unofficial", JSON.stringify(show_unofficial_contestants));
}

document.getElementById("show_unofficial").checked = JSON.parse(localStorage.getItem("show_unofficial"));
toggle_unofficial(); // run on page load to match checkbox state
</script>
2 changes: 1 addition & 1 deletion app/views/contests/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<% if @contest.has_competitor?(current_user) %>
<td><%= progress_bar(@contest.problem_score(current_user, problem), problem_association.weighting) %></td>
<% end %>
<td><%= @contest.num_solved(problem) %></td>
<td><%= (@contest.live_scoreboard || @contest.ended? || policy(@contest).inspect?) ? @contest.num_solved(problem) : "?" %></td>
</tr>
<% end %>
</tbody>
Expand Down
1 change: 0 additions & 1 deletion app/views/layouts/contest.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@
# SimpleNavigation::ItemContainer.new do |menu|
render_navigation do |menu|
menu.dom_class = :tab_menu
menu.item :info, "info", info_contest_path(@contest) if policy(@contest).show?
menu.item :problems, "problems", contest_path(@contest) if policy(@contest).overview?
menu.item :contestants, "contestants", contestants_contest_path(@contest) if policy(@contest).contestants?
menu.item :supervisors, "supervisors", supervisors_contest_path(@contest) if policy(@contest).manage?
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20200418113600_add_live_scoreboard_field.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddLiveScoreboardField < ActiveRecord::Migration
def change
add_column :contests, :live_scoreboard, :boolean, :default => true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20200418113601_add_offical_contestants.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOfficalContestants < ActiveRecord::Migration
def change
add_column :contests, :show_unofficial_contestants, :boolean, :default => false
end
end
13 changes: 13 additions & 0 deletions db/migrate/20210206104409_remove_schools_for_foreign_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class RemoveSchoolsForForeignUsers < ActiveRecord::Migration
def up
User.find_each do |user|
if user.country_code != "NZ" && user.school
user.school = nil
user.save
tom93 marked this conversation as resolved.
Show resolved Hide resolved
end
end
end

def down
end
end
10 changes: 6 additions & 4 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160131000430) do
ActiveRecord::Schema.define(version: 20210206104409) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -114,9 +114,11 @@
t.integer "problem_set_id"
t.datetime "finalized_at"
t.string "startcode"
t.integer "observation", default: 1
t.integer "registration", default: 0
t.integer "affiliation", default: 0
t.integer "observation", default: 1
t.integer "registration", default: 0
t.integer "affiliation", default: 0
t.boolean "live_scoreboard", default: true
t.boolean "show_unofficial_contestants", default: false
end

create_table "entities", force: true do |t|
Expand Down