Add schedule interval as attribute of a program

Also, the part of the schedule event is deleted and the length of the event types
changes to the nearest suitable after changing the length of the interval.

This closes #1220
This commit is contained in:
JewelSam 2017-03-02 20:53:20 +03:00
parent d1bf19867c
commit f6e74461ba
19 changed files with 104 additions and 35 deletions

View file

@ -12,13 +12,15 @@ module Admin
@program = @conference.program
@program.assign_attributes(program_params)
send_mail_on_schedule_public = @program.notify_on_schedule_public?
event_schedules_count_was = @program.event_schedules.count
if @program.update_attributes(program_params)
ConferenceScheduleUpdateMailJob.perform_later(@conference) if send_mail_on_schedule_public
respond_to do |format|
format.html do
redirect_to admin_conference_program_path(@conference.short_title),
notice: 'The program was successfully updated.'
notice = 'The program was successfully updated.'
notice += ' You changed schedule interval and some events were unscheduled.' if @program.event_schedules.count != event_schedules_count_was
redirect_to admin_conference_program_path(@conference.short_title), notice: notice
end
format.js { render json: {} }
end
@ -36,7 +38,7 @@ module Admin
private
def program_params
params.require(:program).permit(:rating, :schedule_public, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id)
params.require(:program).permit(:rating, :schedule_public, :schedule_interval, :schedule_fluid, :languages, :blind_voting, :voting_start_date, :voting_end_date, :selected_schedule_id)
end
end
end

View file

@ -13,7 +13,7 @@ class SchedulesController < ApplicationController
@events_xml = schedules.map(&:event).group_by{ |event| event.time.to_date } if schedules
@dates = @conference.start_date..@conference.end_date
@step_minutes = EventType::LENGTH_STEP.minutes
@step_minutes = @program.schedule_interval.minutes
@conf_start = @conference.start_hour
@conf_period = @conference.end_hour - @conf_start

View file

@ -15,17 +15,13 @@ class EventType < ActiveRecord::Base
alias_attribute :name, :title
# If LENGTH_STEP must be divisor of 60, otherwise the schedule wont be displayed properly
LENGTH_STEP = defined?(SCHEDULE_CELL_SIZE) ? SCHEDULE_CELL_SIZE : 15
private
##
# Check if length is multiple of LENGTH_STEP. Used as validation.
# Check if length is a divisor of program schedule cell size. Used as validation.
#
def length_step
errors.add(:length, "must be multiple of #{LENGTH_STEP}") if length % LENGTH_STEP != 0
errors.add(:length, "must be a divisor of #{program.schedule_interval}") if program && length % program.schedule_interval != 0
end
def capitalize_color

View file

@ -38,6 +38,7 @@ class Program < ActiveRecord::Base
where(state: :confirmed, is_highlight: true)
end
end
has_many :event_schedules, through: :events
has_many :event_users, through: :events
has_many :speakers, -> { distinct }, through: :event_users, source: :user do
@ -52,11 +53,15 @@ class Program < ActiveRecord::Base
# validates :conference_id, presence: true, uniqueness: true
validates :rating, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 10, only_integer: true }
validates :schedule_interval, numericality: { greater_than_or_equal_to: 5, less_than_or_equal_to: 60 }, presence: true
validate :schedule_interval_divisor_60
validate :voting_start_date_before_end_date
validate :voting_dates_exist
after_create :create_event_types
after_create :create_difficulty_levels
after_save :unschedule_unfit_events, if: :schedule_interval_changed?
after_save :normalize_event_types_length, if: :schedule_interval_changed?
validate :check_languages_format
# Returns all event_schedules for the selected schedule ordered by start_time
@ -199,4 +204,31 @@ class Program < ActiveRecord::Base
# We check if every language is a valid ISO 639-1 language
errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty?
end
##
# Check if schedule_interval is a divisor of 60 minutes
#
def schedule_interval_divisor_60
errors.add(:schedule_interval, 'must be a divisor of 60') if schedule_interval > 0 && 60 % schedule_interval > 0
end
##
# Unschedule all the events which don't fit
#
def unschedule_unfit_events
unfit_schedules = event_schedules.select do |event_schedule|
event_schedule.start_time.min % schedule_interval > 0
end
EventSchedule.where(id: unfit_schedules.map(&:id)).destroy_all
end
##
# Change event type length according schedule interval
#
def normalize_event_types_length
event_types.each do |event_type|
new_length = event_type.length > schedule_interval ? event_type.length - (event_type.length % schedule_interval) : schedule_interval
event_type.update_attributes length: new_length
end
end
end

View file

@ -26,6 +26,6 @@ class EventSerializer < ActiveModel::Serializer
end
def length
object.event_type.try(:length) || EventType::LENGTH_STEP
object.event_type.try(:length) || object.event_type.program.schedule_interval
end
end

View file

@ -10,7 +10,7 @@
.col-md-12
= semantic_form_for(@event_type, url: (@event_type.new_record? ? admin_conference_program_event_types_path : admin_conference_program_event_type_path(@conference.short_title, @event_type))) do |f|
= f.input :title
= f.input :length, input_html: {size: 3, type: 'number', step: EventType::LENGTH_STEP, min: EventType::LENGTH_STEP}
= f.input :length, input_html: {size: 3, type: 'number', step: @event_type.program.schedule_interval, min: @event_type.program.schedule_interval}
= f.input :description
= f.input :minimum_abstract_length, input_html: {size: 3}
= f.input :maximum_abstract_length, input_html: {size: 3}

View file

@ -9,6 +9,7 @@
= f.input :schedule_fluid, label: 'Allow submitters to change their event after it is scheduled'
= f.input :rating, hint: 'Enter the number of different rating levels you want to have for voting on proposals. Enter 0 if you do not want to vote on proposals.'
= f.input :languages, hint: "Enter the languages allowed for events as values of #{link_to('ISO 639-1', 'http://www.loc.gov/standards/iso639-2/php/code_list.php', target: "_blank")} language codes separated with commas. The first language would be the default language. Leave it blank if you do not want to specify languages.".html_safe
= f.input :schedule_interval, hint: "It is the minimal time interval of your schedule. The value should be 5, 6, 10, 12, 15, 20, 30 or 60. Warning! Some events could be unscheduled when changing this value."
= f.input :blind_voting, hint: 'Enable this feature if you do not want to show voting results and voters prior to user submitting a vote. For the feature to work you need to set the voting dates below as well'
= f.input :voting_start_date, as: :string, input_html: { id: 'datetimepicker-voting_start_date', readonly: true, value: (f.object.voting_start_date.to_formatted_s(:db_without_seconds) unless f.object.voting_start_date.nil?) }
= f.input :voting_end_date, as: :string, input_html: { id: 'datetimepicker-voting_start_date', readonly: true, value: (f.object.voting_end_date.to_formatted_s(:db_without_seconds) unless f.object.voting_end_date.nil?) }

View file

@ -49,6 +49,11 @@
Yes
- else
No
%dt
Schedule interval
%dd
= @program.schedule_interval
minutes
%h3 Voting Options
%hr

View file

@ -1,5 +1,5 @@
- compact_grid = EventType::LENGTH_STEP < 15
- cells_per_hour = 60 / EventType::LENGTH_STEP
- compact_grid = @program.schedule_interval < 15
- cells_per_hour = 60 / @program.schedule_interval
/ use smaller cell heights for more compact grids
- cell_height = compact_grid ? 32 : 58
- date_event_schedules = @event_schedules.select{ |e| e.start_time.to_date.eql? date }
@ -11,7 +11,7 @@
= room.name
- (@conference.start_hour * cells_per_hour..@conference.end_hour * cells_per_hour).each do |slot|
- hour = slot / cells_per_hour
- minutes = (EventType::LENGTH_STEP * (slot % cells_per_hour)).to_s.rjust(2, '0')
- minutes = (@program.schedule_interval * (slot % cells_per_hour)).to_s.rjust(2, '0')
- time = "#{hour}:#{minutes}"
.schedule-room-slot{ id: "schedule-room-#{room.guid}-#{hour}-#{minutes}", |
room_id: room.id, |

View file

@ -1,6 +1,6 @@
- cells_length = event.event_type.length / EventType::LENGTH_STEP
- cells_length = event.event_type.length / @program.schedule_interval
/ this height fits the room cells
- compact_grid = EventType::LENGTH_STEP < 15
- compact_grid = @program.schedule_interval < 15
- single_cell_height = compact_grid ? 32 : 58
- height = (cells_length * single_cell_height)
- height -= 23 unless compact_grid

View file

@ -1,4 +1,4 @@
- intervals = hrs_per_slide * 60 / EventType::LENGTH_STEP + 1
- intervals = hrs_per_slide * 60 / @conference.program.schedule_interval + 1
- width = 85 / intervals
- carousel_number = (@conf_period / hrs_per_slide.to_f).ceil
.carousel.slide{ id: "carousel-#{ date }-#{ hrs_per_slide }", |
@ -41,7 +41,7 @@
- if event_schedule
/ There is an event, calculate the span and show it
- event_span = (event_schedule.end_time.to_i - start_room_time.to_i) / 60 / EventType::LENGTH_STEP
- event_span = (event_schedule.end_time.to_i - start_room_time.to_i) / 60 / @conference.program.schedule_interval
- span = ((event_span + i - 1 ) > intervals ? intervals + 1 - i : event_span)
= render partial: 'schedule_item', locals: {event: event_schedule.event, event_schedule: event_schedule, span: span, width: width}
- else

View file

@ -6,7 +6,7 @@
%start= @conference.start_date
%end= @conference.end_date
%days= (@conference.end_date - @conference.start_date).to_i + 1
%timeslot_duration= length_timestamp(EventType::LENGTH_STEP)
%timeslot_duration= length_timestamp(@conference.program.schedule_interval)
- if @events_xml.any?
- @events_xml.keys.each.with_index(1) do |day, index|