2018-05-07 16:47:29 -07:00
# frozen_string_literal: true
2017-09-05 22:23:34 +03:00
class Track < ApplicationRecord
2017-07-12 14:31:22 +03:00
include ActiveRecord :: Transitions
2017-01-04 22:27:46 -05:00
include RevisionCount
2017-06-08 12:25:33 +03:00
resourcify :roles , dependent : :delete_all
2018-02-23 00:00:25 -08:00
belongs_to :program , touch : true
2017-06-08 12:25:33 +03:00
belongs_to :submitter , class_name : 'User'
2017-07-12 15:56:07 +03:00
belongs_to :room
2017-08-09 18:12:20 +03:00
belongs_to :selected_schedule , class_name : 'Schedule'
2014-12-18 13:38:40 +01:00
has_many :events , dependent : :nullify
2017-08-09 18:12:20 +03:00
has_many :schedules
2013-01-07 09:04:09 +01:00
2017-08-08 12:41:37 +03:00
has_paper_trail ignore : [ :updated_at ] , meta : { conference_id : :conference_id }
2016-05-24 23:54:46 +05:30
2013-01-07 09:04:09 +01:00
before_create :generate_guid
2014-12-05 16:04:34 +01:00
validates :name , presence : true
2016-07-04 17:53:21 +02:00
validates :color , format : / \ A # [0-9A-F]{6} \ z /
2017-06-29 23:39:56 +03:00
validates :short_name ,
2018-11-13 18:01:49 -08:00
presence : true ,
format : / \ A[a-zA-Z0-9_-]* \ z / ,
2017-06-29 23:39:56 +03:00
uniqueness : {
scope : :program
}
2017-07-12 14:31:22 +03:00
validates :state ,
2018-11-13 18:01:49 -08:00
presence : true ,
2017-07-14 15:57:34 +03:00
inclusion : { in : %w( new to_accept accepted confirmed to_reject rejected canceled withdrawn ) }
validates :cfp_active , inclusion : { in : [ true , false ] }
2017-07-15 19:55:45 +03:00
validates :start_date , presence : true , if : :self_organized_and_accepted_or_confirmed?
validates :end_date , presence : true , if : :self_organized_and_accepted_or_confirmed?
validates :room , presence : true , if : :self_organized_and_accepted_or_confirmed?
2017-07-26 14:54:10 +03:00
validates :relevance , presence : true , if : :self_organized?
2017-08-08 12:41:37 +03:00
validates :description , presence : true , if : :self_organized?
2017-08-17 13:36:46 +03:00
validate :dates_within_conference_dates
validate :start_date_before_end_date
validate :valid_room
2017-08-08 14:18:42 +03:00
validate :overlapping
2016-07-04 17:53:21 +02:00
before_validation :capitalize_color
2013-01-07 09:04:09 +01:00
2017-08-08 14:18:42 +03:00
scope :accepted , - > { where ( state : 'accepted' ) }
2017-07-19 18:28:57 +03:00
scope :confirmed , - > { where ( state : 'confirmed' ) }
scope :cfp_active , - > { where ( cfp_active : true ) }
2017-08-09 18:12:20 +03:00
scope :self_organized , - > { where . not ( submitter : nil ) }
2017-07-19 18:28:57 +03:00
2019-02-15 01:00:35 +01:00
state_machine initial : :new do
2017-07-12 14:31:22 +03:00
state :new
state :to_accept
state :accepted
state :confirmed
state :to_reject
state :rejected
state :canceled
state :withdrawn
event :restart do
transitions to : :new , from : [ :rejected , :withdrawn , :canceled ]
end
2017-07-14 15:57:34 +03:00
event :to_accept do
2017-08-17 13:36:46 +03:00
transitions to : :to_accept , from : [ :new , :to_reject ]
2017-07-12 14:31:22 +03:00
end
event :accept do
transitions to : :accepted , from : [ :new , :to_accept ] , on_transition : :create_organizer_role
end
event :confirm do
transitions to : :confirmed , from : [ :accepted ] , on_transition : :assign_role_to_submitter
end
2017-07-14 15:57:34 +03:00
event :to_reject do
2017-08-17 13:36:46 +03:00
transitions to : :to_reject , from : [ :new , :to_accept ]
2017-07-12 14:31:22 +03:00
end
event :reject do
transitions to : :rejected , from : [ :new , :to_reject ]
end
event :cancel do
transitions to : :canceled , from : [ :to_accept , :to_reject , :accepted , :confirmed ] , on_transition : :revoke_role_and_cleanup
end
event :withdraw do
transitions to : :withdrawn , from : [ :new , :to_accept , :to_reject , :accepted , :confirmed ] , on_transition : :revoke_role_and_cleanup
end
end
2017-06-08 12:25:33 +03:00
2017-01-04 22:27:46 -05:00
def conference
program . conference
end
2017-06-08 12:25:33 +03:00
##
# Checks if the track is self-organized
# ====Returns
# * +true+ -> If the track has a submitter
# * +false+ -> if the track doesn't have a submitter
def self_organized?
return true if submitter
2018-11-13 18:01:49 -08:00
2017-06-08 12:25:33 +03:00
false
end
2017-07-07 00:21:26 +03:00
def to_param
short_name
end
2017-07-12 14:31:22 +03:00
def transition_possible? ( transition )
self . class . state_machine . events_for ( current_state ) . include? ( transition )
end
# Gives the role of the track_organizer to the submitter
def assign_role_to_submitter
submitter . add_role 'track_organizer' , self
end
2017-08-09 18:12:20 +03:00
##
# Revokes the track organizer role, destroys the track's schedule, removes the
# track from events that have it set and reverts their state to new
#
2017-07-12 14:31:22 +03:00
def revoke_role_and_cleanup
role = Role . find_by ( name : 'track_organizer' , resource : self )
2018-05-08 14:17:30 -07:00
role & . users & . each do | user |
user . remove_role 'track_organizer' , self
2017-07-12 14:31:22 +03:00
end
2017-08-09 18:12:20 +03:00
self . selected_schedule_id = nil
save!
schedules . each ( & :destroy! )
2017-07-12 14:31:22 +03:00
events . each do | event |
event . track = nil
2017-08-09 18:12:20 +03:00
event . state = 'new'
2017-07-14 15:57:34 +03:00
event . save!
2017-07-12 14:31:22 +03:00
end
end
2017-07-12 15:56:07 +03:00
##
# Checks if the track is accepted
# ====Returns
# * +true+ -> If the track's state is 'accepted'
# * +false+ -> If the track's state isn't 'accepted'
def accepted?
state == 'accepted'
end
##
# Checks if the track is confirmed
# ====Returns
# * +true+ -> If the track's state is 'confirmed'
# * +false+ -> If the track's state isn't 'confirmed'
def confirmed?
state == 'confirmed'
end
##
2017-07-15 19:55:45 +03:00
# Checks if a self-organized track is accepted or confirmed
2017-07-12 15:56:07 +03:00
# ====Returns
# * +true+ -> If the track's state is 'accepted' or 'confirmed'
# * +false+ -> If the track's state is neither 'accepted' nor 'confirmed'
2017-07-15 19:55:45 +03:00
def self_organized_and_accepted_or_confirmed?
self_organized? && ( accepted? || confirmed? )
2017-07-12 15:56:07 +03:00
end
2017-07-14 15:57:34 +03:00
def update_state ( transition )
error = ''
begin
send ( transition )
rescue Transitions :: InvalidTransition = > e
error += " State update failed. #{ e . message } "
end
error += errors . full_messages . join ( ', ' ) unless save
error
end
2013-01-07 09:04:09 +01:00
private
def generate_guid
2014-06-23 19:07:50 +03:00
guid = SecureRandom . urlsafe_base64
# begin
# guid = SecureRandom.urlsafe_base64
# end while Person.where(:guid => guid).exists?
2013-01-07 09:04:09 +01:00
self . guid = guid
end
2016-07-04 17:53:21 +02:00
def capitalize_color
self . color = color . upcase if color . present?
end
2016-05-24 23:54:46 +05:30
def conference_id
program . conference_id
end
2017-06-08 12:25:33 +03:00
##
# Creates the role of the track organizer
def create_organizer_role
Role . where ( name : 'track_organizer' , resource : self ) . first_or_create ( description : 'For the organizers of the Track' )
end
2017-07-12 15:56:07 +03:00
2017-08-17 13:36:46 +03:00
##
# Verify that the track's dates are between the conference's dates
#
def dates_within_conference_dates
return unless start_date && end_date && program . try ( :conference ) . try ( :start_date ) && program . try ( :conference ) . try ( :end_date )
2018-11-13 18:01:49 -08:00
2017-08-17 13:36:46 +03:00
errors . add ( :start_date , " can't be outside of the conference's dates ( #{ program . conference . start_date } - #{ program . conference . end_date } ) " ) unless ( program . conference . start_date .. program . conference . end_date ) . cover? ( start_date )
errors . add ( :end_date , " can't be outside of the conference's dates ( #{ program . conference . start_date } - #{ program . conference . end_date } ) " ) unless ( program . conference . start_date .. program . conference . end_date ) . cover? ( end_date )
end
2017-07-12 15:56:07 +03:00
2017-08-17 13:36:46 +03:00
##
# Verify that the start date isn't after the end date
#
def start_date_before_end_date
return unless start_date && end_date
2018-11-13 18:01:49 -08:00
2017-08-17 13:36:46 +03:00
errors . add ( :start_date , 'can\'t be after the end date' ) if start_date > end_date
2017-07-14 15:57:34 +03:00
end
##
# Verify that the room is a room of the conference
2017-08-17 13:36:46 +03:00
#
2017-07-14 15:57:34 +03:00
def valid_room
2017-08-17 13:36:46 +03:00
return unless room . try ( :venue ) . try ( :conference ) && program . try ( :conference )
2018-11-13 18:01:49 -08:00
2017-08-17 13:36:46 +03:00
errors . add ( :room , " must be a room of #{ program . conference . venue . name } " ) unless room . venue . conference == program . conference
2017-07-12 15:56:07 +03:00
end
2017-08-08 14:18:42 +03:00
##
# Check that there is no other track in the same room with overlapping dates
2017-08-17 13:36:46 +03:00
#
2017-08-08 14:18:42 +03:00
def overlapping
return unless start_date && end_date && room && program . try ( :tracks )
2018-11-13 18:01:49 -08:00
2017-08-17 13:36:46 +03:00
( program . tracks . accepted + program . tracks . confirmed - [ self ] ) . each do | existing_track |
next unless existing_track . room == room && existing_track . start_date && existing_track . end_date
2018-11-13 18:01:49 -08:00
2025-08-12 16:44:20 +02:00
if start_date . between? ( existing_track . start_date , existing_track . end_date ) ||
end_date . between? ( existing_track . start_date , existing_track . end_date ) ||
2017-08-17 13:36:46 +03:00
start_date < = existing_track . start_date && end_date > = existing_track . end_date
2017-08-08 14:18:42 +03:00
errors . add ( :track , 'has overlapping dates with a confirmed or accepted track in the same room' )
break
end
end
end
2014-06-30 17:07:53 +02:00
end