Files
puzzle-scoreboard/app/models/completion.rb
sto e67ee92838
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
Add contest duration & complete ranking mode implementation
2025-11-18 09:18:18 +01:00

99 lines
3.3 KiB
Ruby

# == Schema Information
#
# Table name: completions
#
# id :integer not null, primary key
# completed :boolean
# display_relative_time :string
# display_time_from_start :string
# missing_pieces :integer
# projected_time :integer
# remaining_pieces :integer
# time_seconds :integer
# created_at :datetime not null
# updated_at :datetime not null
# contest_id :integer not null
# contestant_id :integer not null
# message_id :integer
# puzzle_id :integer not null
#
# Indexes
#
# index_completions_on_contest_id (contest_id)
# index_completions_on_contestant_id (contestant_id)
# index_completions_on_message_id (message_id)
# index_completions_on_puzzle_id (puzzle_id)
#
# Foreign Keys
#
# contest_id (contest_id => contests.id)
# contestant_id (contestant_id => contestants.id)
# message_id (message_id => messages.id)
# puzzle_id (puzzle_id => puzzles.id)
#
class Completion < ApplicationRecord
include ContestsHelper
belongs_to :contest
belongs_to :contestant
belongs_to :puzzle
belongs_to :message, optional: true
has_one :offline, dependent: :destroy
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/ }, 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 }
validates :remaining_pieces, numericality: { only_integer: true }, if: -> { remaining_pieces.present? }
validate :remaining_pieces_is_correct, if: -> { remaining_pieces.present? }
def remaining_pieces_is_correct
if self.remaining_pieces > self.puzzle.pieces
errors.add(:remaining_pieces, "Cannot be greater than the number of pieces for this puzzle")
end
end
def add_time_seconds
if display_time_from_start.present?
arr = display_time_from_start.split(":")
if arr.size == 3
self.time_seconds = arr[0].to_i * 3600 + arr[1].to_i * 60 + arr[2].to_i
elsif arr.size == 2
self.time_seconds = arr[0].to_i * 60 + arr[1].to_i
elsif arr.size == 1
self.time_seconds = arr[0].to_i
end
else
self.time_seconds = self.contest.duration_seconds
self.display_time_from_start = display_time(self.time_seconds)
end
end
def clean_pieces
if self.completed
self.remaining_pieces = nil
else
self.missing_pieces = nil
if !self.offline.present?
self.display_time_from_start = nil
end
end
end
def compute_projected_time
add_time_seconds
if self.completed
self.projected_time = self.time_seconds
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
self.projected_time = assembled_time + Integer(self.remaining_pieces.to_f / pieces_per_second)
end
end
end