# == Schema Information # # Table name: challenges # # id :integer not null, primary key # contester1_id :integer # contester2_id :integer # match_time :datetime # default_time :datetime # mandatory :boolean # server_id :integer # user_id :integer # details :string(255) # response :string(255) # created_at :datetime # updated_at :datetime # map1_id :string(255) # map2_id :string(255) # status :integer default(0), not null # class Challenge < ActiveRecord::Base include Extra STATUS_PENDING = 0 STATUS_ACCEPTED = 1 STATUS_DEFAULT = 2 STATUS_FORFEIT = 3 STATUS_DECLINED = 4 AUTO_DEFAULT_TIME = 10800 # Normal default time: 3 hours CHALLENGE_BEFORE_MANDATORY = 432000 # Min. time threshold for mandatory matches: 5 days CHALLENGE_BEFORE_VOLUNTARY = 900 # Min. time threshold for voluntary matches: 15 mins ACCEPT_BEFORE_MANDATORY = 86400 # Time to accept before mandatory match time: 1 day ACCEPT_BEFORE_VOLUNTARY = 300 # Time to accept before voluntary match time: 5 mins MATCH_LENGTH = 7200 # Usual match length (for servers): 2 hours attr_protected :id, :updated_at, :created_at, :default_time, :user_id, :status validates_presence_of :contester1, :contester2, :server, :map1 validates_presence_of :map2, :on => :update #validates_datetime :match_time, :default_time #validates_length_of [:details, :response], :maximum => 255, :allow_blank => true, :allow_nil => true #validate_on_create:validate_teams #validate_on_create:validate_contest #validate_on_create:validate_mandatory #validate_on_create:validate_match_time #validate_on_create:validate_server #validate_on_create:validate_map1 #validate_on_update :validate_map2 #validate_on_update :validate_status scope :of_contester, lambda { |contester| {:conditions => ["contester1_id = ? OR contester2_id = ?", contester.id, contester.id]} } scope :within_time, lambda { |from, to| {:conditions => ["match_time > ? AND match_time < ?", from.utc, to.utc]} } scope :around, lambda { |time| {:conditions => ["match_time > ? AND match_time < ?", time.ago(MATCH_LENGTH).utc, time.ago(-MATCH_LENGTH).utc]} } scope :on_week, lambda { |time| {:conditions => ["match_time > ? and match_time < ?", time.beginning_of_week, time.end_of_week]} } scope :pending, :conditions => {:status => STATUS_PENDING} scope :mandatory, :conditions => {:mandatory => true} scope :future, :conditions => "match_time > UTC_TIMESTAMP()" scope :past, :conditions => "match_time < UTC_TIMESTAMP()" has_one :match belongs_to :map1, :class_name => "Map" belongs_to :map2, :class_name => "Map" belongs_to :user belongs_to :server belongs_to :contester1, :class_name => "Contester", :include => 'team' belongs_to :contester2, :class_name => "Contester", :include => 'team' def statuses {STATUS_PENDING => "Pending response", STATUS_ACCEPTED => "Accepted", STATUS_DEFAULT => "Default Time", STATUS_FORFEIT => "Forfeited", STATUS_DECLINED => "Declined"} end def autodefault match_time - (mandatory ? ACCEPT_BEFORE_MANDATORY : ACCEPT_BEFORE_VOLUNTARY) end def get_margin mandatory ? CHALLENGE_BEFORE_MANDATORY : CHALLENGE_BEFORE_VOLUNTARY end def get_deadline mandatory ? ACCEPT_BEFORE_MANDATORY : ACCEPT_BEFORE_VOLUNTARY end def get_contester1 self.contester1 = user.active_contesters.of_contest(contester2.contest).first end def before_validation_on_create self.status = STATUS_PENDING self.default_time = match_time.end_of_week.change \ :hour => contester1.contest.default_time.hour, :minute => contester1.contest.default_time.strftime("%M").to_i end def after_create contester2.team.teamers.active.leaders.each do |teamer| if teamer.user.profile.notify_pms Notifications.challenge teamer.user, self end end end def validate_teams if contester1.team == contester2.team errors.add_to_base I18n.t(:challenges_yourself) end if contester1.contest != contester2.contest errors.add_to_base I18n.t(:challenges_opponent_contest) end if !contester2.active or !contester2.team.active errors.add_to_base I18n.t(:challenges_opponent_inactive) end if !contester1.active or !contester1.team.active errors.add_to_base I18n.t(:challenges_inactive) end end def validate_contest if contester1.contest.end.past? or contester1.contest.status == Contest::STATUS_CLOSED errors.add_to_base I18n.t(:contests_closed) end if contester1.contest.contest_type != Contest::TYPE_LADDER and !match errors.add_to_base I18n.t(:contests_notladder) end end def validate_mandatory return unless mandatory if contester2.score < contester1.score errors.add_to_base I18n.t(:challenges_mandatory) end if Challenge.pending.count(:conditions => \ ["contester1_id = ? AND contester2_id = ? AND mandatory = true AND default_time < UTC_TIMESTAMP()", contester1.id, contester2.id]) > 0 errors.add_to_base I18n.t(:challenges_mandatory_handled) end if Match.of_contester(contester2).on_week(match_time).count > 0 errors.add_to_base I18n.t(:challenges_opponent_week) end if Challenge.of_contester(contester2).mandatory.on_week(match_time).count > 0 errors.add_to_base I18n.t(:challenges_opponent_mandatory_week) end if Challenge.of_contester(contester2).mandatory.on_week(default_time).count > 0 errors.add_to_base I18n.t(:challenges_opponent_mandatory_week_defaulttime) end if Match.of_contester(contester2).around(default_time).count > 0 errors.add_to_base I18n.t(:challenges_opponent_defaulttime) end end def validate_match_time if (match_time-get_margin).past? if get_margin > 86400 errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 / 60 / 24 + I18n.t(:matches_time2) else errors.add_to_base I18n.t(:matches_time1) + get_margin / 60 + I18n.t(:matches_time3) end end if Challenge.of_contester(contester2).around(match_time).pending.count > 0 errors.add_to_base I18n.t(:challenges_opponent_specifictime) end if Match.of_contester(contester2).around(match_time).count > 0 errors.add_to_base I18n.t(:challenges_opponent_match_specifictime) end if match_time > contester1.contest.end errors.add_to_base I18n.t(:contests_end) end end def validate_server unless server and server.official errors.add_to_base I18n.t(:servers_notavailable) end unless server.is_free match_time errors.add_to_base I18n.t(:servers_notfree_specifictime) end if !server.is_free default_time errors.add_to_base I18n.t(:servers_notfree_defaulttime) end end def validate_map1 unless contester1.contest.maps.exists?(map1) errors.add_to_base I18n.t(:contests_map_notavailable) end end def validate_map2 unless contester2.contest.maps.exists?(map2) errors.add_to_base I18n.t(:contests_map_notavailable) end end def validate_status if mandatory and ![STATUS_ACCEPTED, STATUS_DEFAULT, STATUS_FORFEIT].include? status errors.add_to_base I18n.t(:challenges_mandatory_invalidresult) end unless statuses.include? status errors.add_to_base I18n.t(:challenges_mandatory_invalidresult) end end def after_update if status_changed? if status == STATUS_ACCEPTED make_match.save elsif status == STATUS_DEFAULT m = make_match m.match_time = default_time m.save elsif status == STATUS_FORFEIT m = make_match m.forfeit = true m.score1 = 4 m.score2 = 0 m.match_time = default_time m.save end end end def make_match match = Match.new match.contester1 = contester1 match.contester2 = contester2 match.map1 = map1 match.map2 = map2 match.contest = contester1.contest match.challenge = self match.server = server match.match_time = match_time match end def can_create? cuser return false unless cuser return false if cuser.banned?(Ban::TYPE_LEAGUE) validate_teams validate_contest true if (contester1.team.is_leader?(cuser) or cuser.admin?) and errors.size == 0 end def can_update? cuser cuser and (contester2.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future? end def can_destroy? cuser cuser and (contester1.team.is_leader? cuser or cuser.admin?) and status == STATUS_PENDING and autodefault.future? end end