Last active
August 29, 2015 14:13
-
-
Save fuyi/b6d2987698e857d65f98 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Competition < ActiveRecord::Base | |
PERCENTATE = 0.1 | |
ON_FIRE_COUNT = 3 | |
scope :on_going, -> { where('start_time < ? AND end_time > ?', Time.now(), Time.now()) } | |
scope :ended, -> { where('end_time < ?', Time.now()) } | |
scope :not_started_yet, -> { where('start_time > ?', Time.now()) } | |
scope :active, -> { where( status: 1)} | |
# include url helper to generate invitation link | |
include Rails.application.routes.url_helpers | |
after_initialize :init | |
attr_accessor :time_status | |
has_one :organization | |
has_many :competition_members | |
has_many :active_competition_members | |
has_many :members, through: :competition_members, class_name: "User" | |
# has_and_belongs_to_many :members, :class_name => "User", :join_table => "competition_members", :association_foreign_key => "member_id" | |
# has_and_belongs_to_many :currencies, :join_table => "competition_currencies" # add af atrribute | |
has_many :currencies, through: :competition_currencies | |
has_many :competition_currencies | |
has_many :teams, autosave: true | |
has_many :team_members, through: :teams | |
has_many :prizes, autosave: true | |
has_many :activities, autosave: true | |
has_many :activity_entries, through: :activities | |
belongs_to :creator, class_name: 'User', foreign_key: 'created_by' | |
belongs_to :organization | |
validates :name, presence: true | |
validates :created_by, presence: true, numericality: { only_integer: true } | |
validates :start_time, presence: true, datetime: true | |
validates :end_time, presence: true, datetime: true | |
validates :organization_id, presence: true, numericality: { only_integer: true } | |
def self.create_comp(comp) | |
c= Competition.new(comp[:competition]) | |
# add activities | |
comp[:activities].each do |act| | |
c.activities.new(act) | |
end | |
# add prizes | |
if(comp[:prizes]) | |
comp[:prizes].each do |prize| | |
c.prizes.new(prize) | |
end | |
end | |
# add teams | |
if (comp[:teams]) | |
comp[:teams].each do |team| | |
team_members = nil # init team_members to nil, assume no team member | |
if(team['team_members']) # why :team_members not working? | |
team_members = team['team_members'] | |
team = team.except('team_members') # remvoe team members key from team | |
end | |
t = c.teams.new(team) | |
#add team members to team, add member to competiiton | |
if(team_members) | |
team_members.each do |tm| | |
t.team_members.new(tm) | |
cm = c.competition_members.new(tm) | |
cm.team = t # add team associate to competiiton member | |
end | |
end | |
end | |
end | |
# add currencies | |
if (comp[:currencies]) | |
comp[:currencies].each do |cc| | |
if cc.has_key?(:default) | |
c.competition_currencies.new({currency_id: cc[:id], default: cc[:default]}) | |
else | |
c.competition_currencies.new({currency_id: cc[:id]}) | |
end | |
end | |
end | |
# add creator as competition member | |
c.competition_members.new comp[:cm] if comp[:cm] | |
c.save! | |
c | |
end | |
# return competition leader user | |
def leader | |
acms = self.active_competition_members | |
acms[0].member unless acms.empty? || acms[0].score == 0 | |
end | |
def check_on_fire(ae) | |
key = "onfire_comp_#{self.id}" | |
puts key | |
# comp = act.competition | |
onfire_setting = Rails.cache.read key | |
# puts onfire_setting | |
if onfire_setting && (onfire_setting[:user_id] == ae.user_id) | |
if onfire_setting[:count] == Competition::ON_FIRE_COUNT-1 | |
# reach limit, set competition member to onfire | |
ae.set_on_fire | |
else | |
# increase counter | |
Rails.cache.write key, { user_id: ae.user_id, count: onfire_setting[:count]+1} | |
end | |
else | |
Rails.cache.write key, { user_id: ae.user_id, count: 1} | |
end | |
puts Rails.cache.read key | |
end | |
# get default currency for cash comp | |
def default_currency | |
self.competition_currencies.find_by_default(true).currency if not self.competition_currencies.blank? | |
end | |
# duration calculation | |
def duration(percent = 1) | |
(self.end_time-self.start_time)*percent | |
end | |
# get competition status time wise | |
def time_status | |
if Time.now < self.start_time | |
:not_started_yet | |
elsif Time.now > self.end_time | |
:ended | |
else | |
:ongoing | |
end | |
end | |
def is_ended? | |
self.time_status == :ended | |
end | |
def on_going? | |
self.time_status == :ongoing | |
end | |
def not_started_yet? | |
self.time_status == :not_started_yet | |
end | |
# check if user participant in this competiiton | |
def has_member?(user) | |
self.active_members.include? user | |
end | |
def admins | |
self.creator.organization.admins | |
end | |
# notice: return value is array, not association | |
def active_competition_members | |
@active_competition_members ||= self.competition_members.select do |cm| | |
cm.status == 1 | |
end | |
end | |
def active_members | |
@active_members ||= self.members.where(competition_members: {status: 1}, status: 1) | |
end | |
def leaderboard_view_by?(user) | |
self.admins.include?(user) || self.active_members.include?(user) || user.super_user? | |
end | |
# def organization_id | |
# @organization_id ||= self.creator.organization.id | |
# end | |
# generate invitation link path | |
def invitation_path | |
url_for controller: :competitions, action: :invite, competition_id: self.random_id, organization_id: self.creator.organization.random_id, only_path: true | |
end | |
# check if competition is team based or not | |
def team_based? | |
@team_based ||= !self.teams.empty? | |
end | |
# current user join competiiton | |
def join(user,team=nil) | |
if(team) # team based competition | |
cm = self.competition_members.create!({member_id: user.id, team_id: team.id}) | |
team.team_members.create!(member_id: user.id) | |
else # none team based competition | |
cm = self.competition_members.create!({member_id: user.id}) | |
end | |
cm | |
end | |
def about_start_time | |
self.start_time - 1.day | |
end | |
def about_end_time | |
diff = self.end_time - self.start_time | |
self.end_time - diff*PERCENTATE | |
end | |
# add time_status attribute | |
def attributes | |
super.merge({ 'time_status' => time_status }) | |
end | |
# generate a pusher channel for each competition | |
def pusher_channel | |
"comp-#{self.random_id}" | |
end | |
# duplicate a existing competition | |
def duplicate_with_associations(creator= nil) | |
Competition.transaction do | |
@new_comp = self.deep_clone include: [:prizes, :competition_members,:activities,:teams, :competition_currencies], except: [ competition_members: [:score,:on_fire]] do |original, copy| | |
# binding.pry | |
# track old team id for copied team assignment | |
copy.copy_from_id = original.id if original.class == Team and copy.respond_to? :copy_from_id | |
end | |
# update attributes | |
@new_comp.name = "Copy of #{self.name}" | |
@new_comp.creator = creator if creator | |
@new_comp.start_time = Time.now + 1.days | |
@new_comp.end_time = @new_comp.start_time + self.duration | |
@new_comp.random_id = SecureRandom.uuid | |
@new_comp.save! | |
# update team_id if competition is team based | |
if @new_comp.team_based? | |
# build map for old and new team | |
@team_map = {} | |
@new_comp.teams.each do |t| | |
@team_map[t.copy_from_id] = t.id | |
end | |
@new_comp.competition_members.each do |cm| | |
cm.team_id = @team_map[cm.team_id] | |
cm.save! | |
end | |
end | |
@new_comp | |
end | |
end | |
# statistics | |
def acts_count | |
@act_count ||= lambda { | |
act_count = [] | |
self.activities.each do |act| | |
count = act.activity_entries.size | |
act_count << {key: act.name, y: count} if count>0 | |
end | |
act_count | |
}.call | |
end | |
def acts_score_count | |
@score_count ||= lambda { | |
score_count = [] | |
self.activities.each do |act| | |
score = ActivityEntry.where(activity_id: act.id).sum(:score).round(2) | |
score_count << {key: act.name, y: score} if score>0 | |
end | |
score_count | |
}.call | |
end | |
# get last activity entry id | |
def last_ae_id | |
self.activity_entries.blank? ? nil : self.activity_entries.first.id | |
end | |
def populate_acts_line_data(type=1) | |
sql = "select count(*), sum(aes.score), date_trunc('day', aes.created_at) as create_date, activity_id, competition_id from activity_entries as aes inner join activities as acs on aes.activity_id = acs.id where competition_id = #{self.id} group by date_trunc('day',aes.created_at), activity_id, competition_id order by create_date" | |
records_array = ActiveRecord::Base.connection.execute(sql) | |
# initiate data matrix | |
matrix = {} | |
self.activities.each do |act| | |
matrix[act.id] = {} | |
self.stats_date_range.each do |d| | |
matrix[act.id][d.to_time.to_i] = 0 # initiate all field with 0 | |
end | |
end | |
# populate matrix | |
records_array.each do |r| | |
count = r['count'].to_i | |
sum = r['sum'].to_f | |
date = r['create_date'].to_time.to_i | |
activity_id = r['activity_id'].to_i | |
if type == 1 | |
matrix[activity_id][date] = count | |
else | |
matrix[activity_id][date] = sum | |
end if (matrix.has_key? activity_id) and (matrix[activity_id].has_key? date) # only set if key exists | |
end | |
results = [] # array to store final formated data | |
# convert from hash to array | |
self.activities.each do |act| | |
results << { key: act.name, values: matrix[act.id].to_a} | |
end | |
puts results | |
results | |
end | |
# get date range to show in statistics | |
def stats_date_range(d = 7) | |
drange = self.start_time.to_date .. self.end_time.to_date | |
if self.time_status === :ongoing | |
drange= (Time.now - d.days).to_date .. Time.now.to_date | |
end | |
drange.to_a | |
end | |
#################################### | |
# callback section | |
#################################### | |
# after_update do |competition| | |
# puts 'updated' | |
# end | |
after_save do |competition| | |
begin | |
if competition.start_time_changed? and competition.about_start_time > Time.now # reschedule about start worker | |
CompetitionAboutStartWorker.perform_at(competition.about_start_time, competition.id, competition.about_start_time) | |
puts 'about start scheduled' | |
end | |
if competition.end_time_changed? | |
# reschedule about end worker | |
CompetitionAboutEndWorker.perform_at(competition.about_end_time, competition.id, competition.about_end_time) | |
puts 'about end scheduled' | |
# reschedule end worker | |
CompetitionEndedWorker.perform_at(competition.end_time, competition.id, competition.end_time) | |
puts 'ended scheduled' | |
end | |
rescue => e | |
# Do nothing now | |
end | |
end | |
#################################### | |
# private section | |
#################################### | |
private | |
# initialize new record | |
def init | |
self.status ||= 1 | |
self.type_id ||= 1 | |
self.random_id ||= SecureRandom.uuid | |
self.specific_time ||= false | |
end | |
# help function, duration in days | |
def duration_in_days | |
(self.duration/ 1.day).ceil | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment