2018-05-07 16:47:29 -07:00
# frozen_string_literal: true
2021-02-20 09:57:27 -08:00
# == Schema Information
#
# Table name: event_schedules
#
# id :bigint not null, primary key
# enabled :boolean default(TRUE)
# start_time :datetime
# created_at :datetime not null
# updated_at :datetime not null
# event_id :integer
# room_id :integer
# schedule_id :integer
#
# Indexes
#
# index_event_schedules_on_event_id (event_id)
# index_event_schedules_on_event_id_and_schedule_id (event_id,schedule_id) UNIQUE
# index_event_schedules_on_room_id (room_id)
# index_event_schedules_on_schedule_id (schedule_id)
#
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
2020-07-30 22:31:45 -07:00
def timezone
event . conference . timezone
end
2020-07-29 22:42:45 -07:00
##
2020-07-30 22:31:45 -07:00
# True within `threshold` before and after the event.
2020-07-29 22:42:45 -07:00
#
2020-07-31 12:24:25 -07:00
def happening_now? ( threshold = 30 . minutes )
2021-03-19 17:13:34 +08:00
return false if end_time < Time . now
2021-03-19 17:22:08 +08:00
2021-03-18 18:53:55 +08:00
event_time_range = start_time .. end_time
now_range = ( Time . now - threshold ) .. ( Time . now + threshold )
2021-03-19 17:13:34 +08:00
event_time_range . overlaps? ( now_range )
2020-07-29 22:42:45 -07:00
end
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
2020-07-23 02:28:36 -07:00
##
# Returns a time + room number string for sorting.
#
def sortable_timestamp
" #{ start_time . to_i } - #{ room & . order } "
end
2016-07-20 23:20:47 +02:00
##
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
return unless event && start_time && event . program && event . program . conference && 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
return unless event && start_time && event . program && event . program . conference && 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