ensl.org/app/models/contest.rb
2020-04-19 06:04:05 +03:00

178 lines
5.5 KiB
Ruby
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# == Schema Information
#
# Table name: contests
#
# id :integer not null, primary key
# contest_type :integer default(0), not null
# default_time :time
# end :datetime
# modulus_3to1 :float(24)
# modulus_4to0 :float(24)
# modulus_base :integer
# modulus_even :float(24)
# name :string(255)
# short_name :string(255)
# start :datetime
# status :integer
# weight :integer
# created_at :datetime
# updated_at :datetime
# demos_id :integer
# rules_id :integer
# winner_id :integer
#
# Indexes
#
# index_contests_on_demos_id (demos_id)
# index_contests_on_rules_id (rules_id)
# index_contests_on_status (status)
# index_contests_on_winner_id (winner_id)
#
class Contest < ActiveRecord::Base
include Extra
WEIGHT = 30.0
STATUS_OPEN = 0
STATUS_PROGRESS = 1
STATUS_CLOSED = 3
TYPE_LADDER = 0
TYPE_LEAGUE = 1
TYPE_BRACKET = 2
scope :active, -> { where.not(status: STATUS_CLOSED) }
scope :inactive, -> { where(status: STATUS_CLOSED) }
scope :joinable, -> { where(table[:status].eq(STATUS_OPEN).and(table[:end].gt(Time.now.utc))) }
scope :with_contesters, -> { includes(:contesters) }
scope :ordered, -> { order("start DESC") }
scope :nsls1, -> { where("name LIKE ?", "NSL S1:%") }
scope :nsls2, -> { where("name LIKE ?", "NSL S2:%") }
scope :ns1seasons, -> { where("name LIKE ?", "S%:%") }
has_many :matches, :dependent => :destroy
has_many :weeks, :dependent => :destroy
has_many :contesters, -> { includes(:team) }, :dependent => :destroy
has_many :predictions, :through => :matches
has_many :brackets
has_many :preds_with_score, -> {
select("predictions.id, predictions.user_id,
SUM(result) AS correct,
SUM(result)/COUNT(*)*100 AS score,
COUNT(*) AS total")
.where("result IS NOT NULL")
.group("predictions.user_id")
.order("correct DESC") },
:source => :predictions,
:through => :matches
has_and_belongs_to_many :maps
belongs_to :demos, :class_name => "Directory", :optional => true
belongs_to :winner, :class_name => "Contester", :optional => true
belongs_to :rules, :class_name => "Article", :optional => true
validates_presence_of :name, :start, :end, :status, :default_time
validates_length_of :name, :in => 1..50
validates_length_of :short_name, :in => 1..8, :allow_nil => true
validate :validate_status
validate :validate_contest_type
def to_s
name
end
def status_s
statuses[status]
end
def default_s
"Sunday " + default_time.to_s
end
def statuses
{STATUS_OPEN => "In Progress (signups open)", STATUS_PROGRESS => "In Progress (signups closed)", STATUS_CLOSED => "Closed"}
end
def types
{TYPE_LADDER => "Ladder", TYPE_LEAGUE => "League", TYPE_BRACKET => "Bracket"}
end
def validate_status
errors.add :status, I18n.t(:invalid_status) unless statuses.include? status
end
def validate_contest_type
errors.add :contest_type, I18n.t(:contests_invalidtype) unless types.include? contest_type
end
def recalculate
Match.where(contest_id: self.id).update_all("diff = null, points1 = null, points2 = null")
Contester.where(contest_id: self.id).update_all("score = 0, win = 0, loss = 0, draw = 0, extra = 0")
matches.finished.chrono.each do |match|
match.recalculate
match.save
end
end
def elo_score score1, score2, diff, level = self.modulus_base, weight = self.weight, moduluses = [self.modulus_even, self.modulus_3to1, self.modulus_4to0]
points = (score2-score1).abs
modulus = moduluses[0].to_f if points <= 1
modulus = moduluses[1].to_f if points == 2
modulus = moduluses[2].to_f if points >= 3
if score1 == score2
result = 0.5
elsif score1 > score2
result = 1.0
elsif score2 > score1
result = 0.0
end
prob = 1.0/(10**(diff.to_f/weight.to_f)+1.0)
total = (level.to_f*modulus*(result-prob)).round
return total
end
def update_ranks contester, old_rank, new_rank
if old_rank < new_rank
Contester.update_all(["score = score -1, trend = ?", Contester::TREND_UP],
["contest_id = ? and score > ? and score <= ?",
self.id, old_rank, new_rank])
contester.trend = Contester::TREND_DOWN
elsif old_rank > new_rank
Contester.update_all(["score = score + 1, trend = ?", Contester::TREND_DOWN],
["contest_id = ? and score < ? and score >= ?",
self.id, old_rank, new_rank])
contester.trend = Contester::TREND_UP
end
contester.score = new_rank
end
def ladder_ranks_unique?
c = Contester.where({:contest_id => self.id})
c.uniq.pluck(:score).count == c.count
end
def can_join?(cuser)
cuser and !cuser&.banned?(Ban::TYPE_LEAGUE) and \
(cuser&.lead_teams.not_in_contest(self).exists?) and \
Contest.joinable.where(id: self).exists?
end
def can_create? cuser
cuser and cuser.admin?
end
def can_update? cuser
cuser and cuser.admin?
end
def can_destroy? cuser
cuser and cuser.admin?
end
def self.params params, cuser
params.require(:contest).permit(:name, :start, :end, :status, :default_time,
:contest_type, :winner_id, :demos_id, :short_name,
:weight, :modulus_base, :modulus_even,
:modulus_3to1, :modulus_4to0, :rules_id)
end
end