2013-01-07 09:04:09 +01:00
|
|
|
class Event < ActiveRecord::Base
|
|
|
|
|
include ActiveRecord::Transitions
|
2013-01-07 16:45:48 +01:00
|
|
|
has_paper_trail
|
2014-02-23 16:09:09 +02:00
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
acts_as_commentable
|
|
|
|
|
|
2014-06-25 14:58:33 +02:00
|
|
|
after_create :set_week
|
|
|
|
|
|
2014-06-24 12:14:53 +03:00
|
|
|
has_many :event_users, dependent: :destroy
|
|
|
|
|
has_many :users, through: :event_users
|
|
|
|
|
has_many :speakers, through: :event_users, source: :user
|
2014-07-17 16:48:27 +03:00
|
|
|
has_many :votes, dependent: :destroy
|
2014-06-24 12:14:53 +03:00
|
|
|
has_many :voters, through: :votes, source: :user
|
2014-08-05 18:23:42 +02:00
|
|
|
has_many :commercials, as: :commercialable, dependent: :destroy
|
2013-01-07 09:04:09 +01:00
|
|
|
belongs_to :event_type
|
2013-08-16 12:46:49 +03:00
|
|
|
|
2016-04-22 18:18:46 +03:00
|
|
|
has_many :events_registrations
|
|
|
|
|
has_many :registrations, through: :events_registrations
|
2016-07-08 13:28:02 +02:00
|
|
|
has_many :event_schedules, dependent: :destroy
|
2013-01-07 09:04:09 +01:00
|
|
|
|
|
|
|
|
belongs_to :track
|
2014-02-23 16:09:09 +02:00
|
|
|
belongs_to :difficulty_level
|
2015-10-25 13:29:02 +02:00
|
|
|
belongs_to :program
|
2013-01-07 09:04:09 +01:00
|
|
|
|
2014-06-24 12:14:53 +03:00
|
|
|
accepts_nested_attributes_for :event_users, allow_destroy: true
|
2014-06-23 19:07:50 +03:00
|
|
|
accepts_nested_attributes_for :users
|
2015-04-16 18:34:41 +03:00
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
before_create :generate_guid
|
2013-01-16 08:22:20 +01:00
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
validate :abstract_limit
|
2014-12-18 13:38:40 +01:00
|
|
|
validate :before_end_of_conference, on: :create
|
2014-06-24 12:14:53 +03:00
|
|
|
validates :title, presence: true
|
|
|
|
|
validates :abstract, presence: true
|
2014-07-16 00:01:40 +02:00
|
|
|
validates :event_type, presence: true
|
2015-10-25 13:29:02 +02:00
|
|
|
validates :program, presence: true
|
2016-04-22 18:18:46 +03:00
|
|
|
validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true }
|
|
|
|
|
|
|
|
|
|
validate :max_attendees_no_more_than_room_size
|
2013-01-07 09:04:09 +01:00
|
|
|
|
2014-04-23 15:45:55 +02:00
|
|
|
scope :confirmed, -> { where(state: 'confirmed') }
|
2016-07-13 18:36:22 +05:30
|
|
|
scope :canceled, -> { where(state: 'canceled') }
|
|
|
|
|
scope :withdrawn, -> { where(state: 'withdrawn') }
|
2015-04-16 18:34:41 +03:00
|
|
|
scope :highlighted, -> { where(is_highlight: true) }
|
2013-07-16 18:42:22 +02:00
|
|
|
|
2014-06-24 12:14:53 +03:00
|
|
|
state_machine initial: :new do
|
2013-01-07 09:04:09 +01:00
|
|
|
state :new
|
|
|
|
|
state :withdrawn
|
|
|
|
|
state :unconfirmed
|
|
|
|
|
state :confirmed
|
|
|
|
|
state :canceled
|
|
|
|
|
state :rejected
|
|
|
|
|
|
2014-06-06 10:23:01 +02:00
|
|
|
event :restart do
|
|
|
|
|
transitions to: :new, from: [:rejected, :withdrawn, :canceled]
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
event :withdraw do
|
2014-06-06 10:23:01 +02:00
|
|
|
transitions to: :withdrawn, from: [:new, :unconfirmed, :confirmed]
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
event :accept do
|
2014-06-06 10:23:01 +02:00
|
|
|
transitions to: :unconfirmed, from: [:new], on_transition: :process_acceptance
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
event :confirm do
|
2014-06-06 10:23:01 +02:00
|
|
|
transitions to: :confirmed, from: :unconfirmed, on_transition: :process_confirmation
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
event :cancel do
|
2014-06-06 10:23:01 +02:00
|
|
|
transitions to: :canceled, from: [:unconfirmed, :confirmed]
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
event :reject do
|
2014-06-06 10:23:01 +02:00
|
|
|
transitions to: :rejected, from: [:new], on_transition: :process_rejection
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2016-04-22 18:18:46 +03:00
|
|
|
##
|
2016-07-08 13:28:02 +02:00
|
|
|
# Checkes if the event has a start_time and a room for the selected schedule if there is any
|
2016-04-22 18:18:46 +03:00
|
|
|
# ====Returns
|
|
|
|
|
# * +true+ or +false+
|
|
|
|
|
def scheduled?
|
2016-07-08 13:28:02 +02:00
|
|
|
selected_event_schedule.try(:start_time).present? && selected_event_schedule.try(:room).present?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
##
|
2016-07-13 01:33:56 +02:00
|
|
|
# Checkes if the event has a start_time and a room for the given schedule
|
|
|
|
|
# (given by its id) or for the selected one if there is any.
|
2016-07-08 13:28:02 +02:00
|
|
|
# ====Returns
|
|
|
|
|
# * +true+ or +false+
|
2016-07-13 01:33:56 +02:00
|
|
|
def unscheduled?(schedule_id)
|
|
|
|
|
state == 'confirmed' && (!event_schedule(schedule_id).try(:start_time).present? || !event_schedule(schedule_id).try(:room).present?)
|
2016-04-22 18:18:46 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def registration_possible?
|
2016-05-15 14:01:30 +03:00
|
|
|
return false unless require_registration && state == 'confirmed'
|
|
|
|
|
return true if max_attendees.nil?
|
2016-04-22 18:18:46 +03:00
|
|
|
registrations.count < max_attendees
|
|
|
|
|
end
|
|
|
|
|
|
2016-07-05 16:44:53 +03:00
|
|
|
##
|
|
|
|
|
# Finds the rating of the user for the event
|
|
|
|
|
# ====Returns
|
|
|
|
|
# * +integer+ -> the rating of the user for the event
|
|
|
|
|
def user_rating(user)
|
|
|
|
|
(vote = votes.find_by(user: user)) ? vote.rating : 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# Checks if the event has votes
|
|
|
|
|
# If a user is provided, it checks if the event has votes by the user
|
|
|
|
|
# ====Returns
|
|
|
|
|
# * +true+ -> If the event has votes (optionally, by the user)
|
|
|
|
|
# * +false+ -> If the event does not have any votes (optionally, by the user)
|
|
|
|
|
def voted?(user=nil)
|
|
|
|
|
return votes.where(user: user).any? if user
|
|
|
|
|
|
|
|
|
|
votes.any?
|
2013-08-16 12:46:49 +03:00
|
|
|
end
|
2014-06-23 19:07:50 +03:00
|
|
|
|
2013-08-16 12:46:49 +03:00
|
|
|
def average_rating
|
|
|
|
|
@total_rating = 0
|
2014-06-26 15:43:24 +03:00
|
|
|
votes.each do |vote|
|
2013-08-16 12:46:49 +03:00
|
|
|
@total_rating = @total_rating + vote.rating
|
|
|
|
|
end
|
2014-06-26 15:43:24 +03:00
|
|
|
@total = votes.size
|
2015-08-06 07:41:20 +03:00
|
|
|
@total_rating > 0 ? number_with_precision(@total_rating / @total.to_f, precision: 2, strip_insignificant_zeros: true) : 0
|
2013-08-16 12:46:49 +03:00
|
|
|
end
|
|
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
def submitter
|
2014-06-26 15:43:24 +03:00
|
|
|
result = event_users.where(event_role: 'submitter').first
|
2016-08-06 00:57:07 +02:00
|
|
|
if result.nil?
|
2014-06-23 19:07:50 +03:00
|
|
|
user = nil
|
|
|
|
|
# Perhaps the event_users haven't been saved, if this is a new proposal
|
2014-06-26 15:43:24 +03:00
|
|
|
event_users.each do |u|
|
|
|
|
|
if u.event_role == 'submitter'
|
|
|
|
|
user = u.user
|
2013-02-06 20:17:54 +01:00
|
|
|
end
|
|
|
|
|
end
|
2014-06-23 19:07:50 +03:00
|
|
|
user
|
2016-08-06 00:57:07 +02:00
|
|
|
else
|
|
|
|
|
result.user
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
end
|
2014-02-23 16:09:09 +02:00
|
|
|
|
2016-07-13 01:33:56 +02:00
|
|
|
def as_json(options={})
|
|
|
|
|
json = super({ include: { event_schedules: { methods: [:room_guid] } } }.merge(options))
|
2016-07-04 17:53:21 +02:00
|
|
|
json[:track_color] = track.try(:color) || '#FFFFFF'
|
2016-07-13 01:33:56 +02:00
|
|
|
json[:track_text_color] = ApplicationController.helpers.contrast_color(json[:track_color])
|
2016-07-18 11:45:59 +02:00
|
|
|
json[:length] = event_type.try(:length) || EventType::LENGTH_STEP
|
2013-01-11 15:00:54 +01:00
|
|
|
|
|
|
|
|
json
|
|
|
|
|
end
|
2014-02-23 16:09:09 +02:00
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
def transition_possible?(transition)
|
2014-06-26 15:43:24 +03:00
|
|
|
self.class.state_machine.events_for(current_state).include?(transition)
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
|
2014-06-06 10:23:01 +02:00
|
|
|
def process_confirmation
|
2015-10-25 13:29:02 +02:00
|
|
|
if program.conference.email_settings.send_on_confirmed_without_registration? &&
|
|
|
|
|
program.conference.email_settings.confirmed_without_registration_body &&
|
|
|
|
|
program.conference.email_settings.confirmed_without_registration_subject
|
2016-02-17 18:19:32 +01:00
|
|
|
if program.conference.registrations.where(user_id: submitter.id).first.nil?
|
2016-04-20 18:59:24 +02:00
|
|
|
Mailbot.confirm_reminder_mail(self).deliver_later
|
2013-02-12 19:17:19 +01:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2014-02-23 16:09:09 +02:00
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
def process_acceptance(options)
|
2015-10-25 13:29:02 +02:00
|
|
|
if program.conference.email_settings.send_on_accepted &&
|
|
|
|
|
program.conference.email_settings.accepted_body &&
|
|
|
|
|
program.conference.email_settings.accepted_subject &&
|
2014-07-23 13:42:03 +02:00
|
|
|
!options[:send_mail].blank?
|
2016-04-20 18:59:24 +02:00
|
|
|
Mailbot.acceptance_mail(self).deliver_later
|
2013-01-17 13:56:05 +01:00
|
|
|
end
|
|
|
|
|
end
|
2013-01-07 09:04:09 +01:00
|
|
|
|
2013-01-17 13:56:05 +01:00
|
|
|
def process_rejection(options)
|
2015-10-25 13:29:02 +02:00
|
|
|
if program.conference.email_settings.send_on_rejected &&
|
|
|
|
|
program.conference.email_settings.rejected_body &&
|
|
|
|
|
program.conference.email_settings.rejected_subject &&
|
2014-07-23 13:42:03 +02:00
|
|
|
!options[:send_mail].blank?
|
2016-04-20 18:59:24 +02:00
|
|
|
Mailbot.rejection_mail(self).deliver_later
|
2013-01-17 13:56:05 +01:00
|
|
|
end
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def abstract_word_count
|
2015-04-17 15:37:21 +02:00
|
|
|
abstract.to_s.split.size
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
2014-02-23 16:09:09 +02:00
|
|
|
|
2014-06-11 09:31:13 +02:00
|
|
|
def self.get_state_color(state)
|
2015-04-17 16:33:30 +02:00
|
|
|
color = {
|
|
|
|
|
new: '#0000FF', # blue
|
|
|
|
|
withdrawn: '#FF8000', # orange
|
|
|
|
|
confirmed: '#00FF00', # green
|
|
|
|
|
unconfirmed: '#FFFF00', # yellow
|
|
|
|
|
rejected: '#FF0000', # red
|
|
|
|
|
canceled: '#848484' # grey
|
|
|
|
|
}[state.to_sym]
|
|
|
|
|
|
|
|
|
|
color || '#00FFFF' # azure
|
2014-06-11 09:31:13 +02:00
|
|
|
end
|
|
|
|
|
|
2014-07-23 13:42:03 +02:00
|
|
|
def update_state(transition, mail = false, subject = false, send_mail = false, send_mail_param)
|
|
|
|
|
alert = ''
|
|
|
|
|
if mail && send_mail_param && subject && send_mail
|
|
|
|
|
alert = 'Update Email Subject before Sending Mails'
|
|
|
|
|
end
|
|
|
|
|
begin
|
|
|
|
|
if mail
|
|
|
|
|
self.send(transition,
|
2014-07-30 23:32:29 +02:00
|
|
|
send_mail: send_mail_param)
|
2014-07-23 13:42:03 +02:00
|
|
|
else
|
|
|
|
|
self.send(transition)
|
|
|
|
|
end
|
|
|
|
|
self.save
|
|
|
|
|
rescue Transitions::InvalidTransition => e
|
|
|
|
|
alert = "Update state failed. #{e.message}"
|
|
|
|
|
end
|
|
|
|
|
alert
|
|
|
|
|
end
|
|
|
|
|
|
2014-08-28 15:21:05 +02:00
|
|
|
def speaker_names
|
2015-03-09 15:31:06 +01:00
|
|
|
result = Set.new
|
2014-08-28 15:21:05 +02:00
|
|
|
speakers.each do |speaker|
|
2015-03-09 15:31:06 +01:00
|
|
|
result.add(speaker.name)
|
2014-08-28 15:21:05 +02:00
|
|
|
end
|
2015-03-09 15:31:06 +01:00
|
|
|
result.to_a.to_sentence
|
2014-08-28 15:21:05 +02:00
|
|
|
end
|
|
|
|
|
|
2015-04-16 18:34:41 +03:00
|
|
|
##
|
|
|
|
|
#
|
|
|
|
|
# Returns +Hash+
|
|
|
|
|
def progress_status
|
|
|
|
|
{
|
2015-10-25 13:29:02 +02:00
|
|
|
registered: self.program.conference.user_registered?(self.submitter),
|
2015-04-16 18:34:41 +03:00
|
|
|
commercials: self.commercials.any?,
|
|
|
|
|
biography: !self.submitter.biography.blank?,
|
|
|
|
|
subtitle: !self.subtitle.blank?,
|
2016-03-18 01:59:05 +05:30
|
|
|
track: (!self.track.blank? unless self.program.tracks.empty?),
|
2015-04-16 18:34:41 +03:00
|
|
|
difficulty_level: !self.difficulty_level.blank?,
|
|
|
|
|
title: true,
|
|
|
|
|
abstract: true
|
|
|
|
|
}.with_indifferent_access
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# Returns the progress of the proposal's set up
|
|
|
|
|
#
|
|
|
|
|
# ====Returns
|
2015-05-16 15:17:20 +02:00
|
|
|
# * +String+ -> Progress in Percent
|
2015-04-16 18:34:41 +03:00
|
|
|
def calculate_progress
|
|
|
|
|
result = self.progress_status
|
2016-03-18 01:59:05 +05:30
|
|
|
(100 * result.values.count(true) / result.values.compact.count).to_s
|
2015-04-16 18:34:41 +03:00
|
|
|
end
|
|
|
|
|
|
2016-06-10 16:33:57 +02:00
|
|
|
##
|
|
|
|
|
# Returns end of the event
|
|
|
|
|
#
|
|
|
|
|
def end_time
|
2016-07-08 13:28:02 +02:00
|
|
|
self.scheduled_start_time + self.event_type.length.minutes
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# Returns the room in which the event is scheduled
|
|
|
|
|
#
|
|
|
|
|
def scheduled_room
|
|
|
|
|
selected_event_schedule.try(:room)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
|
# Returns the start time at which this event is scheduled
|
|
|
|
|
#
|
|
|
|
|
def scheduled_start_time
|
|
|
|
|
selected_event_schedule.try(:start_time)
|
2016-06-10 16:33:57 +02:00
|
|
|
end
|
|
|
|
|
|
2016-07-13 18:36:22 +05:30
|
|
|
##
|
|
|
|
|
# Returns events that are scheduled in the same room and start_time as event
|
|
|
|
|
#
|
|
|
|
|
def intersecting_events
|
|
|
|
|
room.events.where(start_time: start_time).where.not(id: id)
|
|
|
|
|
end
|
|
|
|
|
|
2016-07-13 01:33:56 +02:00
|
|
|
# returns the event_schedule for this event and the schedule given in case that it exists
|
|
|
|
|
def event_schedule(schedule_id)
|
|
|
|
|
event_schedules.find_by(schedule_id: schedule_id)
|
|
|
|
|
end
|
|
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
private
|
|
|
|
|
|
2016-07-08 13:28:02 +02:00
|
|
|
# returns the event_schedule for this event and for the selected_schedule
|
|
|
|
|
def selected_event_schedule
|
|
|
|
|
event_schedules.find_by(schedule_id: program.try(:selected_schedule))
|
|
|
|
|
end
|
|
|
|
|
|
2016-04-22 18:18:46 +03:00
|
|
|
##
|
|
|
|
|
# Do not allow, for the event, more attendees than the size of the room
|
|
|
|
|
def max_attendees_no_more_than_room_size
|
2016-07-08 13:28:02 +02:00
|
|
|
return unless scheduled_room && max_attendees_changed?
|
|
|
|
|
errors.add(:max_attendees, "cannot be more than the room's capacity (#{scheduled_room.size})") if max_attendees && (max_attendees > scheduled_room.size)
|
2016-04-22 18:18:46 +03:00
|
|
|
end
|
|
|
|
|
|
2013-01-07 09:04:09 +01:00
|
|
|
def abstract_limit
|
2014-07-16 00:01:40 +02:00
|
|
|
# If we don't have an event type, there is no need to count anything
|
2016-02-26 15:19:23 +02:00
|
|
|
return unless event_type && abstract
|
2014-06-26 15:43:24 +03:00
|
|
|
len = abstract.split.size
|
2014-07-16 00:01:40 +02:00
|
|
|
max_words = event_type.maximum_abstract_length
|
|
|
|
|
min_words = event_type.minimum_abstract_length
|
2013-01-31 14:38:58 +01:00
|
|
|
|
2014-07-16 00:01:40 +02:00
|
|
|
errors.add(:abstract, "cannot have less than #{min_words} words") if len < min_words
|
|
|
|
|
errors.add(:abstract, "cannot have more than #{max_words} words") if len > max_words
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
|
2014-07-21 14:27:32 +02:00
|
|
|
# TODO: create a module to be mixed into model to perform same operation
|
|
|
|
|
# venue.rb has same functionality which can be shared
|
|
|
|
|
# TODO: rename guid to UUID as guid is specifically Microsoft term
|
2013-01-07 09:04:09 +01:00
|
|
|
def generate_guid
|
2014-07-21 14:27:32 +02:00
|
|
|
loop do
|
|
|
|
|
@guid = SecureRandom.urlsafe_base64
|
2016-08-06 00:57:07 +02:00
|
|
|
break unless self.class.where(guid: guid).any?
|
2014-07-21 14:27:32 +02:00
|
|
|
end
|
|
|
|
|
self.guid = @guid
|
2013-01-07 09:04:09 +01:00
|
|
|
end
|
|
|
|
|
|
2014-06-25 14:58:33 +02:00
|
|
|
def set_week
|
|
|
|
|
self.week = created_at.strftime('%W')
|
|
|
|
|
save!
|
|
|
|
|
end
|
2014-07-07 14:58:53 +02:00
|
|
|
|
|
|
|
|
def before_end_of_conference
|
|
|
|
|
errors.
|
2015-10-25 13:29:02 +02:00
|
|
|
add(:created_at, "can't be after the conference end date!") if program.conference && program.conference.end_date &&
|
|
|
|
|
(Date.today > program.conference.end_date)
|
2014-07-07 14:58:53 +02:00
|
|
|
end
|
2013-05-07 17:00:26 +02:00
|
|
|
end
|