Add contest duration & complete ranking mode implementation
Some checks failed
CI / scan_ruby (push) Successful in 21s
CI / scan_js (push) Successful in 14s
CI / lint (push) Successful in 13s
CI / test (push) Failing after 37s

This commit is contained in:
sto
2025-11-18 09:18:18 +01:00
parent b88460ae71
commit e67ee92838
12 changed files with 62 additions and 12 deletions

View File

@@ -2,11 +2,15 @@ module ContestantsConcern
extend ActiveSupport::Concern
def ranked_contestants(contest)
contest.contestants.sort_by { |contestant| [
-contestant.completions.where(remaining_pieces: nil).size,
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
contestant.time_seconds
] }
if contest.ranking_mode == "actual"
contest.contestants.sort_by { |contestant| [
-contestant.completions.where(remaining_pieces: nil).size,
(contestant.completions.where(remaining_pieces: nil).size == @contest.puzzles.length ? 1 : 0) * contestant.time_seconds,
contestant.completions.size > 0 && contestant.completions[-1].remaining_pieces ? contestant.completions[-1].remaining_pieces : 1000000,
contestant.time_seconds
] }
elsif contest.ranking_mode == "theorical"
contest.contestants.sort_by { |contestant| contestant.completions.map { |completion| completion.projected_time }.sum }
end
end
end

View File

@@ -204,7 +204,7 @@ class ContestsController < ApplicationController
end
def settings_general_params
params.expect(contest: [ :lang, :name, :public, :ranking_mode, :team, :allow_registration ])
params.expect(contest: [ :lang, :name, :duration, :public, :ranking_mode, :team, :allow_registration ])
end
def settings_offline_params

View File

@@ -32,6 +32,8 @@
# puzzle_id (puzzle_id => puzzles.id)
#
class Completion < ApplicationRecord
include ContestsHelper
belongs_to :contest
belongs_to :contestant
belongs_to :puzzle
@@ -42,7 +44,7 @@ class Completion < ApplicationRecord
before_save :clean_pieces
before_save :compute_projected_time
validates :display_time_from_start, presence: true, format: { with: /\A(((\d\d|\d):\d\d|\d\d|\d):\d\d|\d\d|\d)\z/ }
validates :display_time_from_start, presence: true, format: { with: /\A(((\d\d|\d):\d\d|\d\d|\d):\d\d|\d\d|\d)\z/ }, if: -> { completed || offline.present? }
validates :remaining_pieces, presence: true, if: -> { !completed }
validates :contestant_id, uniqueness: { scope: :puzzle }, if: -> { contest.puzzles.size == 1 }
validates :puzzle_id, uniqueness: { scope: :contestant }, if: -> { contest.puzzles.size > 1 }
@@ -66,7 +68,8 @@ class Completion < ApplicationRecord
self.time_seconds = arr[0].to_i
end
else
self.time_seconds = 1
self.time_seconds = self.contest.duration_seconds
self.display_time_from_start = display_time(self.time_seconds)
end
end
@@ -75,6 +78,9 @@ class Completion < ApplicationRecord
self.remaining_pieces = nil
else
self.missing_pieces = nil
if !self.offline.present?
self.display_time_from_start = nil
end
end
end
@@ -82,7 +88,7 @@ class Completion < ApplicationRecord
add_time_seconds
if self.completed
self.projected_time = self.time_seconds
elsif self.offline.present?
else
assembled_time = self.time_seconds
assembled_pieces = self.puzzle.pieces - self.remaining_pieces
pieces_per_second = assembled_pieces.to_f / assembled_time.to_f

View File

@@ -4,6 +4,8 @@
#
# id :integer not null, primary key
# allow_registration :boolean default(FALSE)
# duration :string
# duration_seconds :integer
# lang :string default("en")
# name :string
# offline_form :boolean default(FALSE)
@@ -37,9 +39,19 @@ class Contest < ApplicationRecord
friendly_id :name, use: :slugged
before_save :add_duration_seconds
validates :name, presence: true
validates :lang, inclusion: { in: Languages::AVAILABLE_LANGUAGES.map { |lang| lang[:id] } }
validates :ranking_mode, inclusion: { in: Ranking::AVAILABLE_RANKING_MODES.map { |lang| lang[:id] } }
validates :duration, format: { with: /\A(\d\d:\d\d|\d:\d\d)\z/ }
generates_token_for :token
def add_duration_seconds
arr = self.duration.split(":")
if arr.size == 2
self.duration_seconds = arr[0].to_i * 3600 + arr[1].to_i * 60
end
end
end

View File

@@ -48,7 +48,7 @@ css:
= contestant.completions.where(remaining_pieces: nil).length
td style="position: relative"
- if index > 0 && contestant.time_seconds > 0 && contestant.completions.where(completed: true).size > 0
.relative-time style="position:absolute; margin: 1px 0 0 100px; font-size: 14px; color: grey"
.relative-time style="position:absolute; margin: 1px 0 0 112px; font-size: 14px; color: grey"
|> +
= display_time(contestant.time_seconds - @contestants[index - 1].time_seconds)
= contestant.display_time

View File

@@ -4,6 +4,12 @@
.form-floating
= form.text_field :name, autocomplete: "off", class: "form-control"
= form.label :name, class: "required"
.row.mb-3
.col
.form-floating
= form.text_field :duration, autocomplete: "off", class: "form-control"
= form.label :duration, class: "required"
.form-text = t("activerecord.attributes.contest.duration_description")
.row.mb-3
.col
.form-floating