2018-05-07 16:47:29 -07:00
# frozen_string_literal: true
2017-09-05 22:23:34 +03:00
class EventSchedule < ApplicationRecord
2017-02-12 17:57:55 +02:00
default_scope { where ( enabled : true ) }
2016-07-08 13:28:02 +02:00
belongs_to :schedule
belongs_to :event
belongs_to :room
2016-07-13 01:33:56 +02:00
2016-08-18 00:32:16 +05:30
has_paper_trail ignore : [ :updated_at ] , meta : { conference_id : :conference_id }
2016-07-26 16:20:31 +02:00
validates :schedule , presence : true
validates :event , presence : true
validates :room , presence : true
validates :start_time , presence : true
2016-08-15 16:08:02 +02:00
validates :event , uniqueness : { scope : :schedule }
2017-04-22 01:20:05 +05:30
validate :start_after_end_hour
validate :start_before_start_hour
2017-08-09 18:12:20 +03:00
validate :same_room_as_track
2017-08-08 16:18:11 +03:00
validate :during_track
2017-08-09 18:12:20 +03:00
validate :valid_schedule
2016-07-26 16:20:31 +02:00
2016-07-20 23:20:47 +02:00
scope :confirmed , - > { joins ( :event ) . where ( 'state = ?' , 'confirmed' ) }
scope :canceled , - > { joins ( :event ) . where ( 'state = ?' , 'canceled' ) }
scope :withdrawn , - > { joins ( :event ) . where ( 'state = ?' , 'withdrawn' ) }
2018-04-11 15:03:05 -07:00
scope :with_event_states , - > ( * states ) { joins ( :event ) . where ( 'events.state IN (?)' , states ) }
2016-07-26 16:20:31 +02:00
delegate :guid , to : :room , prefix : true
2016-07-20 23:20:47 +02:00
2018-04-11 15:03:05 -07:00
def self . withdrawn_or_canceled_event_schedules ( schedule_ids )
EventSchedule
. unscoped
. where ( schedule_id : schedule_ids )
. with_event_states ( :withdrawn , :canceled )
end
2016-07-20 23:20:47 +02:00
##
# Returns end of the event
#
def end_time
start_time + event . event_type . length . minutes
end
##
2017-02-12 19:10:03 +05:30
# Returns event schedules that are scheduled in the same room and start_time as event
2016-07-20 23:20:47 +02:00
#
2017-02-12 19:10:03 +05:30
def intersecting_event_schedules
2018-04-11 15:03:05 -07:00
EventSchedule
. unscoped
. where ( room_id : room_id , start_time : start_time , schedule_id : schedule_id )
. where . not ( id : id )
end
# event_schedule_source is a cached enumerable object that helps
# avoid repetitive EXISTS queries when rendering the schedule carousel partial
def replacement? ( event_schedule_source = nil )
return false unless event . state == 'confirmed'
return replaced_event_schedules . exists? unless event_schedule_source
2018-11-13 18:01:49 -08:00
2018-04-11 15:03:05 -07:00
event_schedule_source . any? { | event_schedule | intersects_with? ( event_schedule ) }
2016-07-20 23:20:47 +02:00
end
2016-08-18 00:32:16 +05:30
2018-04-11 15:03:05 -07:00
# the event schedule that `self` replaced
def replaced_event_schedule
replaced_event_schedules . first
end
# NOTE: This and `intersecting_event_schedules` share the flaw that they do not
# detect overlapping schedules where the start times are different (i.e., where
# only a portion of the time intersects).
def intersects_with? ( other )
other != self &&
other . room_id == room_id &&
other . start_time == start_time &&
other . schedule_id == schedule_id
2016-08-15 17:17:57 +05:30
end
2016-08-18 00:32:16 +05:30
private
2018-04-11 15:03:05 -07:00
def replaced_event_schedules
intersecting_event_schedules . with_event_states ( :withdrawn , :canceled )
end
2017-04-22 01:20:05 +05:30
def start_after_end_hour
2025-08-12 16:44:20 +02:00
return unless event && start_time && event . program & . conference & . end_hour
2018-11-13 18:01:49 -08:00
2017-04-22 01:20:05 +05:30
errors . add ( :start_time , " can't be after the conference end hour ( #{ event . program . conference . end_hour } ) " ) if start_time . hour > = event . program . conference . end_hour
end
def start_before_start_hour
2025-08-12 16:44:20 +02:00
return unless event && start_time && event . program & . conference & . start_hour
2018-11-13 18:01:49 -08:00
2017-04-22 01:20:05 +05:30
errors . add ( :start_time , " can't be before the conference start hour ( #{ event . program . conference . start_hour } ) " ) if start_time . hour < event . program . conference . start_hour
end
2016-08-18 00:32:16 +05:30
def conference_id
schedule . program . conference_id
end
2017-08-08 16:18:11 +03:00
##
2017-08-09 18:12:20 +03:00
# Validates that the event is scheduled in the same room as its track
2017-08-08 16:18:11 +03:00
#
2017-08-09 18:12:20 +03:00
def same_room_as_track
return unless event . try ( :track ) . try ( :room )
2018-11-13 18:01:49 -08:00
2017-08-09 18:12:20 +03:00
errors . add ( :room , " must be the same as the track's room ( #{ event . track . room . name } ) " ) unless event . track . room == room
2017-08-08 16:18:11 +03:00
end
##
2017-08-09 18:12:20 +03:00
# Validates that the event is scheduled within its track's time slot
2017-08-08 16:18:11 +03:00
#
def during_track
2017-08-09 18:12:20 +03:00
return unless event . try ( :track ) && start_time
if event . track . try ( :start_date ) && event . track . start_date > start_time
2017-08-08 16:18:11 +03:00
errors . add ( :start_time , " can't be before the track's start date ( #{ event . track . start_date } ) " )
end
2017-08-09 18:12:20 +03:00
if event . track . try ( :end_date ) && event . track . end_date + 1 . day < end_time
2017-08-08 16:18:11 +03:00
errors . add ( :end_time , " can't be after the track's end date ( #{ event . track . end_date } ) " )
end
end
2017-08-09 18:12:20 +03:00
##
# Validates that the event is scheduled in its self-organized tracks's schedules
#
def valid_schedule
return unless event . try ( :track ) . try ( :self_organized? ) && schedule
2018-11-13 18:01:49 -08:00
2017-08-09 18:12:20 +03:00
errors . add ( :schedule , " must be one of #{ event . track . name } track's schedules " ) unless event . track . schedules . include? ( schedule )
end
2016-07-08 13:28:02 +02:00
end