diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index d7dc9af0..82474426 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -3,9 +3,14 @@ module Admin class BaseController < ApplicationController before_action :verify_user_admin + before_action :load_all_conferences private + def load_all_conferences + @conferences = Conference.all + end + def current_ability @current_ability ||= AdminAbility.new(current_user) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 38310dee..c4a4dde2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,7 +5,6 @@ class ApplicationController < ActionController::Base include ApplicationHelper add_flash_types :error protect_from_forgery with: :exception, prepend: true - before_action :get_conferences before_action :store_location # Ensure every controller authorizes resource or skips authorization (skip_authorization_check) check_authorization unless: :devise_controller? @@ -37,10 +36,6 @@ class ApplicationController < ActionController::Base end end - def get_conferences - @conferences = Conference.all - end - def current_ability @current_ability ||= Ability.new(current_user) end diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb index ec901eb0..b9cdc238 100644 --- a/app/controllers/conferences_controller.rb +++ b/app/controllers/conferences_controller.rb @@ -6,8 +6,9 @@ class ConferencesController < ApplicationController load_and_authorize_resource find_by: :short_title, except: :show def index - @current = Conference.where('end_date >= ?', Date.current).reorder(start_date: :asc) - @antiquated = @conferences - @current + @current, @antiquated = Conference.reorder(start_date: :asc).all.partition do |conference| + conference.end_date >= Date.current + end end def show diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index 7137c9ea..44c44c38 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -6,39 +6,52 @@ class SchedulesController < ApplicationController before_action :respond_to_options load_resource :conference, find_by: :short_title load_resource :program, through: :conference, singleton: true, except: :index + before_action :load_withdrawn_event_schedules, only: [:show, :events] def show - @rooms = @conference.venue.rooms if @conference.venue - schedules = @program.selected_event_schedules - unless schedules + @event_schedules = @program.selected_event_schedules( + includes: [{ event: %i[event_type speakers submitter] }] + ) + + unless @event_schedules redirect_to events_conference_schedule_path(@conference.short_title) + return end - @events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules - @dates = @conference.start_date..@conference.end_date - @step_minutes = @program.schedule_interval.minutes - @conf_start = @conference.start_hour - @conf_period = @conference.end_hour - @conf_start + respond_to do |format| + format.xml do + @events_xml = @event_schedules.map(&:event).group_by{ |event| event.time.to_date } if @event_schedules + end - # the schedule takes you to today if it is a date of the schedule - @current_day = @conference.current_conference_day - @day = @current_day.present? ? @current_day : @dates.first - unless @current_day - # the schedule takes you to the current time if it is beetween the start and the end time. - @hour_column = @conference.hours_from_start_time(@conf_start, @conference.end_hour) + format.html do + @rooms = @conference.venue.rooms if @conference.venue + @dates = @conference.start_date..@conference.end_date + @step_minutes = @program.schedule_interval.minutes + @conf_start = @conference.start_hour + @conf_period = @conference.end_hour - @conf_start + + # the schedule takes you to today if it is a date of the schedule + @current_day = @conference.current_conference_day + @day = @current_day.present? ? @current_day : @dates.first + unless @current_day + # the schedule takes you to the current time if it is beetween the start and the end time. + @hour_column = @conference.hours_from_start_time(@conf_start, @conference.end_hour) + end + # Ids of the @event_schedules of confrmed self_organized tracks along with the selected_schedule_id + @selected_schedules_ids = [@conference.program.selected_schedule_id] + @conference.program.tracks.self_organized.confirmed.each do |track| + @selected_schedules_ids << track.selected_schedule_id + end + @selected_schedules_ids.compact! + end end - # Ids of the schedules of confrmed self_organized tracks along with the selected_schedule_id - @selected_schedules_ids = [@conference.program.selected_schedule_id] - @conference.program.tracks.self_organized.confirmed.each do |track| - @selected_schedules_ids << track.selected_schedule_id - end - @selected_schedules_ids.compact! end def events @dates = @conference.start_date..@conference.end_date - - @events_schedules = @program.selected_event_schedules + @events_schedules = @program.selected_event_schedules( + includes: [:room, { event: %i[track event_type speakers submitter] }] + ) @events_schedules = [] unless @events_schedules @unscheduled_events = @program.events.confirmed - @events_schedules.map(&:event) @@ -54,4 +67,10 @@ class SchedulesController < ApplicationController format.html { head :ok } end if request.options? end + + def load_withdrawn_event_schedules + # Avoid making repetitive EXISTS queries for these later. + # See usage in EventsHelper#canceled_replacement_event_label + @withdrawn_event_schedules = EventSchedule.withdrawn_or_canceled_event_schedules(@program.schedule_ids) + end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 5e67e0a9..15358ecd 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -28,8 +28,8 @@ module EventsHelper end def replacement_event_notice(event_schedule) - if event_schedule.present? && event_schedule.replacement? - replaced_event = (event_schedule.intersecting_event_schedules.withdrawn.first || event_schedule.intersecting_event_schedules.canceled.first).event + if event_schedule.present? && event_schedule.replacement?(@withdrawn_event_schedules) + replaced_event = event_schedule.replaced_event_schedule.try(:event) content_tag :span do concat content_tag :span, 'Please note that this talk replaces ' concat link_to replaced_event.title, conference_program_proposal_path(@conference.short_title, replaced_event.id) @@ -40,7 +40,7 @@ module EventsHelper def canceled_replacement_event_label(event, event_schedule, *label_classes) if event.state == 'canceled' || event.state == 'withdrawn' content_tag :span, 'CANCELED', class: (['label', 'label-danger'] + label_classes) - elsif event_schedule.present? && event_schedule.replacement? + elsif event_schedule.present? && event_schedule.replacement?(@withdrawn_event_schedules) content_tag :span, 'REPLACEMENT', class: (['label', 'label-info'] + label_classes) end end diff --git a/app/models/event_schedule.rb b/app/models/event_schedule.rb index 184284cd..12510b5b 100644 --- a/app/models/event_schedule.rb +++ b/app/models/event_schedule.rb @@ -23,8 +23,17 @@ class EventSchedule < ApplicationRecord scope :canceled, -> { joins(:event).where('state = ?', 'canceled') } scope :withdrawn, -> { joins(:event).where('state = ?', 'withdrawn') } + scope :with_event_states, ->(*states){ joins(:event).where('events.state IN (?)', states) } + delegate :guid, to: :room, prefix: true + def self.withdrawn_or_canceled_event_schedules(schedule_ids) + EventSchedule + .unscoped + .where(schedule_id: schedule_ids) + .with_event_states(:withdrawn, :canceled) + end + ## # Returns end of the event # @@ -36,15 +45,41 @@ class EventSchedule < ApplicationRecord # Returns event schedules that are scheduled in the same room and start_time as event # def intersecting_event_schedules - EventSchedule.unscoped.where(room: room, start_time: start_time, schedule: schedule).where.not(id: id) + EventSchedule + .unscoped + .where(room_id: room_id, start_time: start_time, schedule_id: schedule_id) + .where.not(id: id) end - def replacement? - event.state == 'confirmed' && (!intersecting_event_schedules.canceled.empty? || !intersecting_event_schedules.withdrawn.empty?) + # 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 + event_schedule_source.any? { |event_schedule| intersects_with?(event_schedule) } + end + + # 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 end private + def replaced_event_schedules + intersecting_event_schedules.with_event_states(:withdrawn, :canceled) + end + def start_after_end_hour return unless event && start_time && event.program && event.program.conference && event.program.conference.end_hour 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 diff --git a/app/models/program.rb b/app/models/program.rb index 9bc5a460..eb2eb28c 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -77,12 +77,14 @@ class Program < ApplicationRecord validate :check_languages_format # Returns all event_schedules for the selected schedule ordered by start_time - def selected_event_schedules - event_schedules = selected_schedule.event_schedules.order(start_time: :asc) if selected_schedule - tracks.self_organized.confirmed.order(start_date: :asc).each do |track| - event_schedules += track.selected_schedule.event_schedules.order(start_time: :asc) if track.selected_schedule + def selected_event_schedules(includes: [:event]) + event_schedules = [] + event_schedules = selected_schedule.event_schedules.includes(*includes).order(start_time: :asc) if selected_schedule + tracks.self_organized.confirmed.includes(selected_schedule: { event_schedules: includes }).order(start_date: :asc).each do |track| + next unless track.selected_schedule + event_schedules += track.selected_schedule.event_schedules end - event_schedules.sort_by(&:start_time) if event_schedules + event_schedules.sort_by(&:start_time) end ## @@ -171,7 +173,8 @@ class Program < ApplicationRecord # * +False+ -> If there is not any event for the given date def any_event_for_this_date?(date) parsed_date = DateTime.parse("#{date} 00:00").utc - EventSchedule.where(schedule: selected_schedule).where(start_time: parsed_date..(parsed_date + 1.day)).any? + range = parsed_date..(parsed_date + 1.day) + selected_schedule.event_schedules.any? { |es| range.cover?(es.start_time) } end ## diff --git a/app/views/schedules/_carousel.html.haml b/app/views/schedules/_carousel.html.haml index dde7e72c..9ce87c76 100644 --- a/app/views/schedules/_carousel.html.haml +++ b/app/views/schedules/_carousel.html.haml @@ -29,13 +29,13 @@ %td.room{ style: "height: #{ td_height(@rooms) }px;" } .room.elipsis.break-words{ style: "-webkit-line-clamp: #{ room_lines(@rooms) }; height: #{ room_height(@rooms) }px;" } = room.name - - event_schedules = room.event_schedules.select{ |e| @selected_schedules_ids.include?(e.schedule_id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } + - event_schedules = @event_schedules.select{ |e| e.room_id == room.id && @selected_schedules_ids.include?(e.schedule_id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } - (1..intervals).each do |i| - if span > 1 - span -= 1 - else - event_schedule = event_schedules.find{ |e| e.start_time <= start_room_time and e.end_time > start_room_time } - - if event_schedule && (event_schedule.event.state == 'canceled' || event_schedule.event.state == 'withdrawn') && !event_schedule.intersecting_event_schedules.confirmed.empty? + - if event_schedule && (event_schedule.event.state == 'canceled' || event_schedule.event.state == 'withdrawn') && event_schedule.intersecting_event_schedules.confirmed.exists? - replacement_event = event_schedule.intersecting_event_schedules.confirmed.first - event_schedule = (replacement_event.start_time <= start_room_time && replacement_event.end_time > start_room_time) ? replacement_event : nil