104 lines
3.5 KiB
Ruby
104 lines
3.5 KiB
Ruby
# == Schema Information
|
|
#
|
|
# Table name: completions
|
|
#
|
|
# id :integer not null, primary key
|
|
# code :string
|
|
# 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? }
|
|
validate :contest_code_is_correct, if: -> { code.present? }
|
|
|
|
def remaining_pieces_is_correct
|
|
if self.remaining_pieces > self.puzzle.pieces
|
|
errors.add(:remaining_pieces, I18n.t("activerecord.errors.models.completion.attributes.remaining_pieces.too_large"))
|
|
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
|
|
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
|
|
|
|
def contest_code_is_correct
|
|
if self.code != self.contest.code
|
|
errors.add(:code, I18n.t("activerecord.errors.models.completion.attributes.code.mismatch"))
|
|
end
|
|
end
|
|
end
|