From 7eea9302694943858d81e450e14e188e6832f7bd Mon Sep 17 00:00:00 2001 From: AEtherC0r3 Date: Wed, 9 Aug 2017 18:12:20 +0300 Subject: [PATCH] Implement track scheduling Add track association to schedule Show schedules in admin sidebar to track organizers Allow track organizers to manage the schedules of their tracks Don't allow self-organized track events to be dragged or unscheduled in a conference schedule Make scheduled events of self-organized tracks appear semitransparent in conference schedules Make the rooms of confirmed self_organized tracks appear semitransparent and don't allow events to be scheduled to it in the conference schedules during the dates of its track Create admin/SchedulesController#new action Add a button in admin/Schedules#index to create schedules for tracks Add self_organized scope to Track Modify Schedules#show to handle track schedules and show a unified schedule Allow track organizers to create new schedules for their tracks Correctly identify scheduled and unscheduled events in Schedules#events Fix Event#room and Event#time for when the event is scheduled in a track schedule Modify Program#selected_event_schedules to include the event_schedules of selected track schedules Modify Track#revoke_role_and_cleanup to destroy the track's schedules and revert its events' state to new Add tabs for conference and track schedules in admin/Schedules#index Add button to Create/Show a tracks schedule in Tracks#index and #show Fix concurrent_events in application_helper because of changes in Program#selected_event_schedules Do not take into account cfp_active in Event#valid_track Modify EventsController#get_tracks accordingly Enforce cfp_active of track to be enabled for proposals in ProposalsController#create and #update Add support for multiple schedules per track Add selected_schedule_id to Track Load EventSchedules of selected track schedules for conference schedules in admin/SchedulesController#show Modify SchedulesController#show to take into account only the selected track schedules Create Event#selected_schedule_id and use it in Event#scheduled? and Event#time Validate that an EventSchedule for an event of a self-organized track belongs to one of the track's schedules Add 'Manage' button in Tracks#index, #show that sends you to the admin side of things Add admin/TracksController#update_selected_schedule to update the selected_schedule_id of tracks --- .haml-lint_todo.yml | 2 + .rubocop.yml | 1 + app/assets/javascripts/osem-schedule.js | 7 +- app/assets/stylesheets/osem-schedule.css.scss | 4 + app/controllers/admin/events_controller.rb | 2 +- app/controllers/admin/schedules_controller.rb | 35 ++++- app/controllers/admin/tracks_controller.rb | 12 ++ app/controllers/proposals_controller.rb | 13 ++ app/controllers/schedules_controller.rb | 13 +- app/helpers/application_helper.rb | 2 +- app/models/admin_ability.rb | 13 ++ app/models/event.rb | 32 ++++- app/models/event_schedule.rb | 38 ++++-- app/models/program.rb | 6 +- app/models/schedule.rb | 1 + app/models/track.rb | 14 +- app/views/admin/schedules/_day_tab.html.haml | 4 +- app/views/admin/schedules/_event.html.haml | 3 +- app/views/admin/schedules/_form.html.haml | 10 ++ app/views/admin/schedules/index.html.haml | 121 ++++++++++++------ app/views/admin/schedules/show.html.haml | 11 +- app/views/admin/tracks/index.html.haml | 10 ++ app/views/admin/tracks/show.html.haml | 10 ++ app/views/proposals/show.html.haml | 2 +- app/views/schedules/_carousel.html.haml | 2 +- app/views/tracks/index.html.haml | 2 + app/views/tracks/show.html.haml | 2 + config/routes.rb | 3 +- ...9120927_add_track_reference_to_schedule.rb | 5 + ...4174637_add_selected_schedule_to_tracks.rb | 6 + db/schema.rb | 14 +- spec/features/track_organizer_ability_spec.rb | 10 +- spec/models/admin_ability_spec.rb | 11 +- spec/models/event_schedule_spec.rb | 36 +++++- spec/models/event_spec.rb | 43 +++++-- spec/models/schedule_spec.rb | 1 + spec/models/track_spec.rb | 60 +++++++-- 37 files changed, 443 insertions(+), 118 deletions(-) create mode 100644 app/views/admin/schedules/_form.html.haml create mode 100644 db/migrate/20170809120927_add_track_reference_to_schedule.rb create mode 100644 db/migrate/20170814174637_add_selected_schedule_to_tracks.rb diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml index 49feab3d..b79ddc17 100644 --- a/.haml-lint_todo.yml +++ b/.haml-lint_todo.yml @@ -188,6 +188,7 @@ linters: - "app/views/conferences/_call_for_tracks.html.haml" - "app/views/admin/tracks/_change_state_dropdown.html.haml" - "app/views/proposals/_encouragement_text.html.haml" + - "app/views/admin/schedules/_form.html.haml" # Offense count: 223 InstanceVariables: @@ -253,6 +254,7 @@ linters: - "app/views/admin/cfps/_tracks_cfp.html.haml" - "app/views/conferences/_call_for_tracks.html.haml" - "app/views/admin/tracks/_change_state_dropdown.html.haml" + - "app/views/admin/schedules/_form.html.haml" # Offense count: 32 IdNames: diff --git a/.rubocop.yml b/.rubocop.yml index 5d24032d..9ab6f6b3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -31,3 +31,4 @@ Metrics/BlockLength: - 'spec/models/conference_spec.rb' - 'spec/features/ability_spec.rb' - 'spec/models/ability_spec.rb' + - 'spec/models/admin_ability_spec.rb' diff --git a/app/assets/javascripts/osem-schedule.js b/app/assets/javascripts/osem-schedule.js index baae1933..39e33922 100644 --- a/app/assets/javascripts/osem-schedule.js +++ b/app/assets/javascripts/osem-schedule.js @@ -78,11 +78,12 @@ var Schedule = { }; $(document).ready( function() { - // hide the remove button for unscheduled events + // hide the remove button for unscheduled and non schedulable events $('.unscheduled-events .schedule-event-delete-button').hide(); + $('.non_schedulable .schedule-event-delete-button').hide(); // set events as draggable - $('.schedule-event').draggable({ + $('.schedule-event').not('.non_schedulable').draggable({ snap: '.schedule-room-slot', revertDuration: 200, revert: function (event, ui) { @@ -99,7 +100,7 @@ $(document).ready( function() { }); // set room cells as droppable - $('.schedule-room-slot').droppable({ + $('.schedule-room-slot').not('.non_schedulable .schedule-room-slot').droppable({ accept: '.schedule-event', tolerance: "pointer", drop: function(event, ui) { diff --git a/app/assets/stylesheets/osem-schedule.css.scss b/app/assets/stylesheets/osem-schedule.css.scss index 21fda6f3..75a71767 100644 --- a/app/assets/stylesheets/osem-schedule.css.scss +++ b/app/assets/stylesheets/osem-schedule.css.scss @@ -267,6 +267,10 @@ td.no-padding{ font-size: 7px; } +.non_schedulable{ + opacity: 0.5; +} + /* Small devices (tablets, 768px and up) */ @media (min-width: 768px) { .room, .event-title{ diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb index 2d7af60e..bb23dac9 100644 --- a/app/controllers/admin/events_controller.rb +++ b/app/controllers/admin/events_controller.rb @@ -198,7 +198,7 @@ module Admin end def get_tracks - @tracks = Track.accessible_by(current_ability).where(program: @program).confirmed.cfp_active + @tracks = Track.accessible_by(current_ability).where(program: @program).confirmed end end end diff --git a/app/controllers/admin/schedules_controller.rb b/app/controllers/admin/schedules_controller.rb index d3c7cea3..00ef9a1d 100644 --- a/app/controllers/admin/schedules_controller.rb +++ b/app/controllers/admin/schedules_controller.rb @@ -4,14 +4,21 @@ module Admin # the schedule of a conference, which should not be accessed in the first place load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :program, through: :conference, singleton: true - load_and_authorize_resource :schedule, through: :program + load_and_authorize_resource :schedule, through: :program, except: [:new, :create] load_resource :event_schedules, through: :schedule load_resource :selected_schedule, through: :program, singleton: true load_resource :venue, through: :conference, singleton: true def index; end + def new + @schedule = @program.schedules.build(track: @program.tracks.new) + authorize! :new, @schedule + end + def create + @schedule = @program.schedules.new(schedule_params) + authorize! :create, @schedule if @schedule.save redirect_to admin_conference_schedule_path(@conference.short_title, @schedule.id), notice: 'Schedule was successfully created.' @@ -23,9 +30,23 @@ module Admin def show @event_schedules = @schedule.event_schedules - @unscheduled_events = @program.events.confirmed - @schedule.events - @dates = @conference.start_date..@conference.end_date - @rooms = @conference.venue.rooms if @conference.venue + + if @schedule.track + track = @schedule.track + @unscheduled_events = track.events.confirmed - @schedule.events + @dates = track.start_date..track.end_date + @rooms = [track.room] + else + @program.tracks.self_organized.confirmed.each do |t| + @event_schedules += t.selected_schedule.event_schedules if t.selected_schedule + end + self_organized_tracks_events = @program.tracks.self_organized.confirmed.map do |t| + t.events.confirmed + end + @unscheduled_events = @program.events.confirmed - @schedule.events - self_organized_tracks_events.flatten.compact + @dates = @conference.start_date..@conference.end_date + @rooms = @conference.venue.rooms if @conference.venue + end end def destroy @@ -37,5 +58,11 @@ module Admin error: "Schedule couldn't be deleted. #{@schedule.errors.full_messages.join('. ')}." end end + + private + + def schedule_params + params.require(:schedule).permit(:track_id) if params[:schedule] + end end end diff --git a/app/controllers/admin/tracks_controller.rb b/app/controllers/admin/tracks_controller.rb index 0f3940da..03f26448 100644 --- a/app/controllers/admin/tracks_controller.rb +++ b/app/controllers/admin/tracks_controller.rb @@ -101,6 +101,18 @@ module Admin update_state(:cancel, "Track #{@track.name} canceled!") end + def update_selected_schedule + if @track.update_attributes(params.require(:track).permit(:selected_schedule_id)) + respond_to do |format| + format.js { render json: {} } + end + else + respond_to do |format| + format.js { render json: { errors: "The selected schedule couldn't been updated #{@track.errors.to_a.join('. ')}" }, status: 422 } + end + end + end + private def track_params diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index baf86f3c..b75f648e 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -49,6 +49,13 @@ class ProposalsController < ApplicationController # by default. @event.speakers = [current_user] @event.submitter = current_user + + if Track.find_by(id: params[:event][:track_id]).try(:cfp_active) == false + flash.now[:error] = 'You have selected a track that doesn\'t accept proposals' + render action: 'new' + return + end + if @event.save ahoy.track 'Event submission', title: 'New submission' redirect_to conference_program_proposals_path(@conference.short_title), notice: 'Proposal was successfully submitted.' @@ -61,6 +68,12 @@ class ProposalsController < ApplicationController def update @url = conference_program_proposal_path(@conference.short_title, params[:id]) + if Track.find_by(id: params[:event][:track_id]).try(:cfp_active) == false + flash.now[:error] = 'You have selected a track that doesn\'t accept proposals' + render action: 'edit' + return + end + if @event.update(event_params) redirect_to conference_program_proposals_path(conference_id: @conference.short_title), notice: 'Proposal was successfully updated.' diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index 5fdbeecd..eeb272e0 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -24,6 +24,13 @@ class SchedulesController < ApplicationController return 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) + + # 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 @@ -32,11 +39,7 @@ class SchedulesController < ApplicationController @events_schedules = @program.selected_event_schedules @events_schedules = [] unless @events_schedules - @unscheduled_events = if @program.selected_schedule - @program.events.confirmed - @program.selected_schedule.events - else - @program.events.confirmed - end + @unscheduled_events = @program.events.confirmed - @events_schedules.map(&:event) day = @conference.current_conference_day @tag = day.strftime('%Y-%m-%d') if day diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e55656ea..e4a0e230 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -117,7 +117,7 @@ module ApplicationHelper def concurrent_events(event) return nil unless event.scheduled? && event.program.selected_event_schedules - event_schedule = event.program.selected_event_schedules.find_by(event: event) + event_schedule = event.program.selected_event_schedules.find { |es| es.event == event } other_event_schedules = event.program.selected_event_schedules.reject { |other_event_schedule| other_event_schedule == event_schedule } concurrent_events = [] diff --git a/app/models/admin_ability.rb b/app/models/admin_ability.rb index af0f80eb..cc318da2 100644 --- a/app/models/admin_ability.rb +++ b/app/models/admin_ability.rb @@ -307,5 +307,18 @@ class AdminAbility can :manage, Event, track_id: track_ids_for_track_organizer can :manage, Commercial, commercialable_type: 'Event', commercialable_id: Event.where(track_id: track_ids_for_track_organizer).pluck(:id) + + # Show Scheduless in the admin sidebar + can :update, Schedule do |schedule| + schedule.new_record? && conf_ids_for_track_organizer.include?(schedule.program.conference_id) + end + + # Show new track schedule button + can :new, Schedule do |schedule| + schedule.new_record? && conf_ids_for_track_organizer.include?(schedule.program.conference_id) && schedule.track.try(:new_record?) + end + + can :manage, Schedule, track_id: track_ids_for_track_organizer + can :manage, EventSchedule, schedule: { track_id: track_ids_for_track_organizer } end end diff --git a/app/models/event.rb b/app/models/event.rb index 682eb6c8..c6de2d45 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -45,7 +45,7 @@ class Event < ActiveRecord::Base validates :max_attendees, numericality: { only_integer: true, greater_than_or_equal_to: 1, allow_nil: true } validate :max_attendees_no_more_than_room_size - validate :acceptable_track + validate :valid_track scope :confirmed, -> { where(state: 'confirmed') } scope :canceled, -> { where(state: 'canceled') } @@ -85,7 +85,7 @@ class Event < ActiveRecord::Base # ====Returns # * +true+ or +false+ def scheduled? - event_schedules.find_by(schedule_id: program.selected_schedule_id).present? + event_schedules.find_by(schedule_id: selected_schedule_id).present? end def registration_possible? @@ -243,14 +243,18 @@ class Event < ActiveRecord::Base def room # We use try(:selected_schedule_id) because this function is used for # validations so program could not be present there - event_schedules.find_by(schedule_id: program.try(:selected_schedule_id)).try(:room) + if track.try(:self_organized?) + track.room + else + event_schedules.find_by(schedule_id: program.try(:selected_schedule_id)).try(:room) + end end ## # Returns the start time at which this event is scheduled # def time - event_schedules.find_by(schedule_id: program.selected_schedule_id).try(:start_time) + event_schedules.find_by(schedule_id: selected_schedule_id).try(:start_time) end def conference @@ -306,9 +310,23 @@ class Event < ActiveRecord::Base end ## - # Allow only confirmed tracks that belong to the same program and are included in the cfp - def acceptable_track + # Allow only confirmed tracks that belong to the same program as the event + # + def valid_track return unless track && track.program && program - errors.add(:track, 'is invalid') unless track.confirmed? && track.cfp_active && track.program == program + errors.add(:track, 'is invalid') unless track.confirmed? && track.program == program + end + + ## + # Return the id of the selected schedule + # + # ====Returns + # * +Integer+ -> selected_schedule_id of self-organized track or program + def selected_schedule_id + if track.try(:self_organized?) + track.selected_schedule_id + else + program.selected_schedule_id + end end end diff --git a/app/models/event_schedule.rb b/app/models/event_schedule.rb index a7cc1ed1..57edbec0 100644 --- a/app/models/event_schedule.rb +++ b/app/models/event_schedule.rb @@ -12,8 +12,9 @@ class EventSchedule < ActiveRecord::Base validates :event, uniqueness: { scope: :schedule } validate :start_after_end_hour validate :start_before_start_hour - validate :room_of_track + validate :same_room_as_track validate :during_track + validate :valid_schedule scope :confirmed, -> { joins(:event).where('state = ?', 'confirmed') } scope :canceled, -> { joins(:event).where('state = ?', 'canceled') } @@ -56,24 +57,33 @@ class EventSchedule < ActiveRecord::Base end ## - # Validates that the event is scheduled in the same room as it's track + # Validates that the event is scheduled in the same room as its track # - def room_of_track - if event && event.track.try(:room) && event.track.room != room - errors.add(:room, "must be the same as the track's room (#{event.track.room.name})") + def same_room_as_track + return unless event.try(:track).try(:room) + errors.add(:room, "must be the same as the track's room (#{event.track.room.name})") unless event.track.room == room + end + + ## + # Validates that the event is scheduled within its track's time slot + # + def during_track + return unless event.try(:track) && start_time + + if event.track.try(:start_date) && event.track.start_date > start_time + errors.add(:start_time, "can't be before the track's start date (#{event.track.start_date})") + end + + if event.track.try(:end_date) && event.track.end_date + 1.day < end_time + errors.add(:end_time, "can't be after the track's end date (#{event.track.end_date})") end end ## - # Validates that the event is scheduled within it's track's time slot + # Validates that the event is scheduled in its self-organized tracks's schedules # - def during_track - if event && event.track.try(:start_date) && event.track.start_date > start_time - errors.add(:start_time, "can't be before the track's start date (#{event.track.start_date})") - end - - if event && event.track.try(:end_date) && event.track.end_date + 1.day < end_time - errors.add(:end_time, "can't be after the track's end date (#{event.track.end_date})") - end + def valid_schedule + return unless event.try(:track).try(:self_organized?) && schedule + errors.add(:schedule, "must be one of #{event.track.name} track's schedules") unless event.track.schedules.include?(schedule) end end diff --git a/app/models/program.rb b/app/models/program.rb index 49894cbd..612d8154 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -71,7 +71,11 @@ class Program < ActiveRecord::Base # Returns all event_schedules for the selected schedule ordered by start_time def selected_event_schedules - selected_schedule.event_schedules.order(start_time: :asc) if selected_schedule + 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 + end + event_schedules.sort_by(&:start_time) if event_schedules end ## diff --git a/app/models/schedule.rb b/app/models/schedule.rb index b14a978c..082d0af0 100644 --- a/app/models/schedule.rb +++ b/app/models/schedule.rb @@ -1,5 +1,6 @@ class Schedule < ActiveRecord::Base belongs_to :program + belongs_to :track has_many :event_schedules, dependent: :destroy has_many :events, through: :event_schedules diff --git a/app/models/track.rb b/app/models/track.rb index f2b1ac95..c3d02950 100644 --- a/app/models/track.rb +++ b/app/models/track.rb @@ -7,7 +7,9 @@ class Track < ActiveRecord::Base belongs_to :program belongs_to :submitter, class_name: 'User' belongs_to :room + belongs_to :selected_schedule, class_name: 'Schedule' has_many :events, dependent: :nullify + has_many :schedules has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } @@ -38,6 +40,7 @@ class Track < ActiveRecord::Base scope :accepted, -> { where(state: 'accepted') } scope :confirmed, -> { where(state: 'confirmed') } scope :cfp_active, -> { where(cfp_active: true) } + scope :self_organized, -> { where.not(submitter: nil) } state_machine initial: :pending do state :new @@ -102,7 +105,10 @@ class Track < ActiveRecord::Base submitter.add_role 'track_organizer', self end - # Revokes the track organizer role and removes the track from events that have it set + ## + # 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 + # def revoke_role_and_cleanup role = Role.find_by(name: 'track_organizer', resource: self) @@ -112,8 +118,14 @@ class Track < ActiveRecord::Base end end + self.selected_schedule_id = nil + save! + + schedules.each(&:destroy!) + events.each do |event| event.track = nil + event.state = 'new' event.save! end end diff --git a/app/views/admin/schedules/_day_tab.html.haml b/app/views/admin/schedules/_day_tab.html.haml index 7bfe3ec6..8a1dff60 100644 --- a/app/views/admin/schedules/_day_tab.html.haml +++ b/app/views/admin/schedules/_day_tab.html.haml @@ -5,7 +5,9 @@ - date_event_schedules = @event_schedules.select{ |e| e.start_time.to_date.eql? date } .row - @rooms.each do |room| - .col-md-2.col-xs-6 + - non_schedulable = room.tracks.self_organized.confirmed.any? do |track| + - !track.schedules.include?(@schedule) && (track.start_date..track.end_date).include?(date) + .col-md-2.col-xs-6{ class: ('non_schedulable' if non_schedulable) } .room-name - room_date_event_schedules = date_event_schedules.select{ |e| e.room == room } = room.name diff --git a/app/views/admin/schedules/_event.html.haml b/app/views/admin/schedules/_event.html.haml index 3a13323b..ff6fd0c3 100644 --- a/app/views/admin/schedules/_event.html.haml +++ b/app/views/admin/schedules/_event.html.haml @@ -7,12 +7,13 @@ / subtracting the padding before calculate the number of lines - lines = (height - 7) / 23 - color = event.track.try(:color).present? ? event.track.try(:color) : 'FFFFFF' +- non_schedulable = event_schedule_id && (EventSchedule.find(event_schedule_id).schedule != @schedule) .schedule-event{ style: "height: #{height}px; background-color: #{color}; color: #{contrast_color(color)}", | id: "event-#{event.id}", | event_id: event.id, | length: cells_length, | event_schedule_id: event_schedule_id, | - class: ('compact' if compact_grid) } + class: "#{'compact' if compact_grid} #{'non_schedulable' if non_schedulable}" } .schedule-event-text{ style: "-webkit-line-clamp: #{lines}; height: #{lines * 23}px;"} %span.schedule-event-delete-button{ onclick: "Schedule.remove(\'event-#{event.id}\');" } X = event.title diff --git a/app/views/admin/schedules/_form.html.haml b/app/views/admin/schedules/_form.html.haml new file mode 100644 index 00000000..8c6c091d --- /dev/null +++ b/app/views/admin/schedules/_form.html.haml @@ -0,0 +1,10 @@ +.row + .col-md-12 + .page-header + %h1 + New Track Schedule +.row + .col-md-12 + = semantic_form_for @schedule, url: admin_conference_schedules_path(@conference.short_title) do |f| + = f.input :track, collection: Track.accessible_by(current_ability).where(program: @program).self_organized.confirmed.pluck(:name, :id), include_blank: false + = f.action :submit, as: :button, button_html: { class: 'btn btn-primary' } diff --git a/app/views/admin/schedules/index.html.haml b/app/views/admin/schedules/index.html.haml index acfc79a8..29c1910e 100644 --- a/app/views/admin/schedules/index.html.haml +++ b/app/views/admin/schedules/index.html.haml @@ -4,42 +4,85 @@ %h1 Schedules %p.text-muted The schedules for your conference -.row - .col-md-12 - %table.table.table-hover#event_types - %thead - %th Schedule - %th Selected - %th Actions - %tbody - - @schedules.each do |schedule| - %tr - %td - Schedule - = schedule.id - %td - = selected_scheduled?(schedule) - %td - .btn-group{role: "group"} - = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule.id), - method: :get, class: 'btn btn-primary' - = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule.id), - method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } -.row - .col-md-12 - - if @venue.try(:rooms).present? - .text-right - = link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title), - method: :post, class: 'btn btn-primary' - - elsif @venue - .h3 - No Rooms! - %small - = link_to 'Create rooms', admin_conference_venue_rooms_path - before creating the schedule. - - else - .h3 - No Venue! - %small - = link_to 'Create a venue with rooms', new_admin_conference_venue_path - before creating the schedule. +.tabbable + %ul.nav.nav-tabs + %li.active + = link_to 'Conference schedules', '#conference', 'data-toggle' => 'tab' + %li + = link_to 'Track schedules', '#tracks', 'data-toggle' => 'tab' + .tab-content + .tab-pane.active#conference + .row + .col-md-12 + %table.table.table-hover + %thead + %th Schedule + %th Selected + %th Actions + %tbody + - @schedules.where(track: nil).each do |schedule| + %tr + %td + Schedule + = schedule.id + %td + = selected_scheduled?(schedule) + %td + .btn-group{role: "group"} + - if can? :show, schedule + = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule), + method: :get, class: 'btn btn-primary' + - if can? :destroy, schedule + = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule), + method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } + .row + .col-md-12 + - if @venue.try(:rooms).present? + .text-right + - if can? :create, @program.schedules.new + = link_to 'Add Schedule', admin_conference_schedules_path(@conference.short_title), + method: :post, class: 'btn btn-primary' + - elsif @venue + .h3 + No Rooms! + %small + = link_to 'Create rooms', admin_conference_venue_rooms_path + before creating the schedule. + - else + .h3 + No Venue! + %small + = link_to 'Create a venue with rooms', new_admin_conference_venue_path + before creating the schedule. + .tab-pane#tracks + .row + .col-md-12 + %table.table.table-hover + %thead + %th Schedule + %th Track + %th Selected + %th Actions + %tbody + - @schedules.where.not(track: nil).each do |schedule| + %tr + %td + Schedule + = schedule.id + %td + - track = schedule.track + = link_to track.name, admin_conference_program_track_path(@conference.short_title, track) + %td + = schedule == schedule.track.selected_schedule ? 'Yes' : 'No' + %td + .btn-group{role: "group"} + - if can? :show, schedule + = link_to 'Show', admin_conference_schedule_path(@conference.short_title, schedule), class: 'btn btn-primary' + - if can? :destroy, schedule + = link_to 'Delete', admin_conference_schedule_path(@conference.short_title, schedule), + method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete Schedule #{schedule.id}?" } + .row + .col-md-12 + .text-right + - if can? :new, @program.schedules.build(track: @program.tracks.new) + = link_to 'Add Track Schedule', new_admin_conference_schedule_path(@conference.short_title), class: 'btn btn-primary' diff --git a/app/views/admin/schedules/show.html.haml b/app/views/admin/schedules/show.html.haml index 9b2d3571..9009fcbb 100644 --- a/app/views/admin/schedules/show.html.haml +++ b/app/views/admin/schedules/show.html.haml @@ -11,8 +11,15 @@ .row .col-md-2 Selected schedule - = check_box_tag @conference.short_title, @schedule.id, (@schedule.id == @selected_schedule.try(:id)), - method: :patch, url: (admin_conference_program_path(@conference.short_title) + '?[program][selected_schedule_id]='), + :ruby + if @schedule.track + value = @schedule == @schedule.track.selected_schedule + url = update_selected_schedule_admin_conference_program_track_path(@conference.short_title, @schedule.track) + '?[track][selected_schedule_id]=' + else + value = @schedule.id == @selected_schedule.try(:id) + url = admin_conference_program_path(@conference.short_title) + '?[program][selected_schedule_id]=' + end + = check_box_tag @conference.short_title, @schedule.id, value, method: :patch, url: url, class: 'switch-checkbox-schedule', data: { size: 'small', off_color: 'warning', on_text: 'Yes', diff --git a/app/views/admin/tracks/index.html.haml b/app/views/admin/tracks/index.html.haml index 77a7cf02..48c0d1b3 100644 --- a/app/views/admin/tracks/index.html.haml +++ b/app/views/admin/tracks/index.html.haml @@ -63,9 +63,19 @@ .btn-group{role: "group"} - if can? :edit, track = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, track), class: 'btn btn-primary' + - special_style = true - if can? :destroy, track = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, track), method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete #{track.name}? Attention: This track will be removed from all Events that have it set" } + - if track.self_organized? + - if track.selected_schedule + - if can? :show, track.selected_schedule + = link_to 'Show Schedule', admin_conference_schedule_path(@conference.short_title, track.selected_schedule), + class: 'btn btn-default' + - elsif can? :create, @program.schedules.build(track: track) + = button_to 'Create Schedule', admin_conference_schedules_path(@conference.short_title), + form: { class: 'btn', style: 'padding: 0px 0px; margin-top: -1px;' }, class: 'btn btn-default', + style: ('border-top-left-radius: 0; border-bottom-left-radius: 0;' if special_style), params: { 'schedule[track_id]' => track.id } .row .col-md-12.text-right = link_to 'New Track', new_admin_conference_program_track_path(@conference.short_title), class: 'btn btn-success' diff --git a/app/views/admin/tracks/show.html.haml b/app/views/admin/tracks/show.html.haml index 78f8830e..b8c9773e 100644 --- a/app/views/admin/tracks/show.html.haml +++ b/app/views/admin/tracks/show.html.haml @@ -21,10 +21,20 @@ - if can? :edit, @track = link_to 'Edit', edit_admin_conference_program_track_path(@conference.short_title, @track), method: :get, class: 'btn btn-primary' + - special_style = true - if can? :destroy, @track = link_to 'Delete', admin_conference_program_track_path(@conference.short_title, @track), method: :delete, class: 'btn btn-danger', data: { confirm: "Do you really want to delete #{@track.name}? Attention: This track will be removed from all Events that have it set" } + - if @track.self_organized? + - if @track.selected_schedule + - if can? :show, @track.selected_schedule + = link_to 'Show Schedule', admin_conference_schedule_path(@conference.short_title, @track.selected_schedule), + class: 'btn btn-default' + - elsif can? :create, @program.schedules.build(track: @track) + = button_to 'Create Schedule', admin_conference_schedules_path(@conference.short_title), + form: { class: 'btn', style: 'padding: 0px 0px; margin-top: -1px;' }, class: 'btn btn-default', + style: ('border-top-left-radius: 0; border-bottom-left-radius: 0;' if special_style), params: { 'schedule[track_id]' => @track.id } .row .col-md-12 %table.table diff --git a/app/views/proposals/show.html.haml b/app/views/proposals/show.html.haml index 2d38d3a8..c181e283 100644 --- a/app/views/proposals/show.html.haml +++ b/app/views/proposals/show.html.haml @@ -114,7 +114,7 @@ %dl %dt Start Time: %dd - = event.program.selected_event_schedules.find_by(event: event).start_time.strftime("%Y %B %e %H:%M") + = event.program.selected_event_schedules.find { |es| es.event == event }.start_time.strftime("%Y %B %e %H:%M") %br %dt Room: %dd diff --git a/app/views/schedules/_carousel.html.haml b/app/views/schedules/_carousel.html.haml index ce28e909..dde7e72c 100644 --- a/app/views/schedules/_carousel.html.haml +++ b/app/views/schedules/_carousel.html.haml @@ -29,7 +29,7 @@ %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| (e.schedule_id == @conference.program.selected_schedule.id) && (e.end_time > start_time) && (e.start_time <= (start_time + hrs_per_slide.hour)) } + - 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)) } - (1..intervals).each do |i| - if span > 1 - span -= 1 diff --git a/app/views/tracks/index.html.haml b/app/views/tracks/index.html.haml index 31a9fe97..d17ee766 100644 --- a/app/views/tracks/index.html.haml +++ b/app/views/tracks/index.html.haml @@ -72,6 +72,8 @@ method: :patch, class: 'btn btn-mini btn-success', id: "resubmit_track_request_#{track.id}" - if can? :edit, track = link_to 'Edit', edit_conference_program_track_path(@conference.short_title, track), class: 'btn btn-default' + - if current_user.has_role? :track_organizer, track + = link_to 'Manage', admin_conference_program_track_path(@conference.short_title, track), class: 'btn btn-default' .row .col-md-12 diff --git a/app/views/tracks/show.html.haml b/app/views/tracks/show.html.haml index 6014f23e..c81d60ec 100644 --- a/app/views/tracks/show.html.haml +++ b/app/views/tracks/show.html.haml @@ -8,6 +8,8 @@ .btn-group.pull-right - if can? :edit, @track = link_to 'Edit Track request', edit_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-primary' + - if current_user.has_role? :track_organizer, @track + = link_to 'Manage', admin_conference_program_track_path(@conference.short_title, @track), class: 'btn btn-default' .row .col-md-8 %dl.dl-horizontal diff --git a/config/routes.rb b/config/routes.rb index ec55b8a3..0e1cad2e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -40,7 +40,7 @@ Osem::Application.routes.draw do resources :comments, only: [:index] resources :conferences do resource :contact, except: [:index, :new, :create, :show, :destroy] - resources :schedules, only: [:index, :create, :show, :update, :destroy] + resources :schedules, except: [:edit, :update] resources :event_schedules, only: [:create, :update, :destroy] get 'commercials/render_commercial' => 'commercials#render_commercial' resources :commercials, only: [:index, :create, :update, :destroy] @@ -88,6 +88,7 @@ Osem::Application.routes.draw do patch :to_reject patch :reject patch :cancel + patch :update_selected_schedule end end resources :event_types diff --git a/db/migrate/20170809120927_add_track_reference_to_schedule.rb b/db/migrate/20170809120927_add_track_reference_to_schedule.rb new file mode 100644 index 00000000..2c2d7f1c --- /dev/null +++ b/db/migrate/20170809120927_add_track_reference_to_schedule.rb @@ -0,0 +1,5 @@ +class AddTrackReferenceToSchedule < ActiveRecord::Migration + def change + add_reference :schedules, :track, index: true, foreign_key: true + end +end diff --git a/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb b/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb new file mode 100644 index 00000000..05b7d417 --- /dev/null +++ b/db/migrate/20170814174637_add_selected_schedule_to_tracks.rb @@ -0,0 +1,6 @@ +class AddSelectedScheduleToTracks < ActiveRecord::Migration + def change + add_column :tracks, :selected_schedule_id, :integer + add_index :tracks, :selected_schedule_id + end +end diff --git a/db/schema.rb b/db/schema.rb index d9949b1e..82f5923d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -431,9 +431,11 @@ ActiveRecord::Schema.define(version: 20170816203325) do t.integer "program_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "track_id" end add_index "schedules", ["program_id"], name: "index_schedules_on_program_id" + add_index "schedules", ["track_id"], name: "index_schedules_on_track_id" create_table "splashpages", force: :cascade do |t| t.integer "conference_id" @@ -520,24 +522,26 @@ ActiveRecord::Schema.define(version: 20170816203325) do end create_table "tracks", force: :cascade do |t| - t.string "guid", null: false - t.string "name", null: false + t.string "guid", null: false + t.string "name", null: false t.text "description" t.string "color" t.datetime "created_at" t.datetime "updated_at" t.integer "program_id" - t.string "short_name", null: false - t.string "state", default: "new", null: false - t.boolean "cfp_active", null: false + t.string "short_name", null: false + t.string "state", default: "new", null: false + t.boolean "cfp_active", null: false t.integer "submitter_id" t.integer "room_id" t.date "start_date" t.date "end_date" t.text "relevance" + t.integer "selected_schedule_id" end add_index "tracks", ["room_id"], name: "index_tracks_on_room_id" + add_index "tracks", ["selected_schedule_id"], name: "index_tracks_on_selected_schedule_id" add_index "tracks", ["submitter_id"], name: "index_tracks_on_submitter_id" create_table "users", force: :cascade do |t| diff --git a/spec/features/track_organizer_ability_spec.rb b/spec/features/track_organizer_ability_spec.rb index 69b4d3e5..67e0c8de 100644 --- a/spec/features/track_organizer_ability_spec.rb +++ b/spec/features/track_organizer_ability_spec.rb @@ -4,7 +4,7 @@ feature 'Has correct abilities' do let(:organization) { create(:organization) } let(:conference) { create(:full_conference, organization: organization) } - let(:self_organized_track) { create(:track, :self_organized, program: conference.program, state: 'confirmed', cfp_active: true) } + let(:self_organized_track) { create(:track, :self_organized, program: conference.program, state: 'confirmed') } let(:role_track_organizer) { Role.where(name: 'track_organizer', resource: self_organized_track).first_or_create } let(:user_track_organizer) { create(:user, role_ids: [role_track_organizer.id]) } @@ -32,7 +32,7 @@ feature 'Has correct abilities' do expect(page).to have_link('Tracks', href: "/admin/conferences/#{conference.short_title}/program/tracks") expect(page).to_not have_link('Event Types', href: "/admin/conferences/#{conference.short_title}/program/event_types") expect(page).to_not have_link('Difficulty Levels', href: "/admin/conferences/#{conference.short_title}/program/difficulty_levels") - expect(page).to_not have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") + expect(page).to have_link('Schedules', href: "/admin/conferences/#{conference.short_title}/schedules") expect(page).to have_link('Reports', href: "/admin/conferences/#{conference.short_title}/program/reports") expect(page).to_not have_link('Registrations', href: "/admin/conferences/#{conference.short_title}/registrations") expect(page).to_not have_link('Registration Period', href: "/admin/conferences/#{conference.short_title}/registration_period") @@ -130,12 +130,16 @@ feature 'Has correct abilities' do expect(current_path).to eq root_path visit admin_conference_schedules_path(conference.short_title) - expect(current_path).to eq root_path + expect(current_path).to eq admin_conference_schedules_path(conference.short_title) create(:schedule, program: conference.program) visit admin_conference_schedule_path(conference.short_title, conference.program.schedules.first) expect(current_path).to eq root_path + self_organized_track_schedule = create(:schedule, program: conference.program, track: self_organized_track) + visit admin_conference_schedule_path(conference.short_title, self_organized_track_schedule) + expect(current_path).to eq admin_conference_schedule_path(conference.short_title, self_organized_track_schedule) + visit admin_conference_program_reports_path(conference.short_title) expect(current_path).to eq admin_conference_program_reports_path(conference.short_title) diff --git a/spec/models/admin_ability_spec.rb b/spec/models/admin_ability_spec.rb index 126668fa..344f63ba 100644 --- a/spec/models/admin_ability_spec.rb +++ b/spec/models/admin_ability_spec.rb @@ -45,7 +45,7 @@ describe 'User with admin role' do let!(:my_event_schedule) { create(:event_schedule, schedule: my_schedule) } let!(:other_event_schedule) { create(:event_schedule, schedule: other_schedule) } - let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program, state: 'confirmed', cfp_active: true) } + let!(:my_self_organized_track) { create(:track, :self_organized, program: my_conference.program, state: 'confirmed') } context 'user #is_admin?' do let(:venue) { my_conference.venue } @@ -460,8 +460,12 @@ describe 'User with admin role' do let(:user) { create(:user, role_ids: [role.id]) } let(:new_track) { build(:track, program: my_conference.program) } let(:new_event) { build(:event, program: my_conference.program) } + let(:new_schedule) { build(:schedule, program: my_conference.program) } + let(:new_track_schedule) { build(:schedule, program: my_conference.program, track: new_track) } let(:my_self_organized_track_event) { create(:event, program: my_conference.program, track: my_self_organized_track) } let(:my_self_organized_track_event_commercial) { create(:commercial, commercialable: my_self_organized_track_event) } + let(:my_self_organized_track_schedule) { create(:schedule, program: my_conference.program, track: my_self_organized_track) } + let(:my_self_organized_track_event_schedule) { create(:event_schedule, event: my_self_organized_track_event, schedule: my_self_organized_track_schedule, room: my_self_organized_track.room) } it{ should_not be_able_to(:new, Conference.new) } it{ should_not be_able_to(:create, Conference.new) } @@ -532,6 +536,11 @@ describe 'User with admin role' do it{ should be_able_to(:manage, my_self_organized_track_event) } it{ should be_able_to(:manage, my_self_organized_track_event_commercial) } + it{ should be_able_to(:update, new_schedule) } + it{ should be_able_to(:new, new_track_schedule) } + it{ should be_able_to(:manage, my_self_organized_track_schedule) } + it{ should be_able_to(:manage, my_self_organized_track_event_schedule) } + it_behaves_like 'user with any role' it_behaves_like 'user with non-organizer role', 'track_organizer' end diff --git a/spec/models/event_schedule_spec.rb b/spec/models/event_schedule_spec.rb index 8e22430b..91db4b64 100644 --- a/spec/models/event_schedule_spec.rb +++ b/spec/models/event_schedule_spec.rb @@ -46,7 +46,7 @@ describe EventSchedule do end end - describe '#room_of_track' do + describe '#same_room_as_track' do before :each do conference = create(:conference) conference.venue = create(:venue) @@ -108,5 +108,39 @@ describe EventSchedule do end end end + + describe '#valid_schedule' do + before :each do + conference.venue = create(:venue) + @room = create(:room, venue: conference.venue) + track = create(:track, :self_organized, program: conference.program, room: @room, state: 'confirmed', name: 'My awesome track') + @event = create(:event, program: conference.program, track: track) + end + + context 'is valid' do + it 'when the event belongs to a self-organized track and is scheduled in one of its track\'s schedules' do + schedule = create(:schedule, program: conference.program, track: @event.track) + event_schedule = build(:event_schedule, event: @event, room: @room, schedule: schedule) + expect(event_schedule.valid?).to eq true + expect(event_schedule.errors[:schedule]).to eq [] + end + + it 'when the event doesn\'t belong to a self-organized track' do + @event.track = nil + @event.save! + event_schedule = build(:event_schedule, event: @event, room: @room) + expect(event_schedule.valid?).to eq true + expect(event_schedule.errors[:schedule]).to eq [] + end + end + + context 'is invalid' do + it 'when the event belongs to a self_organized track but isn\'t scheduled in one of its schedules' do + event_schedule = build(:event_schedule, event: @event, room: @room) + expect(event_schedule.valid?).to eq false + expect(event_schedule.errors[:schedule]).to eq ['must be one of My awesome track track\'s schedules'] + end + end + end end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 3ada61ca..0a84aedd 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -97,10 +97,10 @@ describe Event do end end - describe '#acceptable_track' do + describe '#valid_track' do context 'is valid' do - it 'when the track belong to the same program, is confirmed and is included in the cfp' do - track = create(:track, state: 'confirmed', cfp_active: true, program: conference.program) + it 'when the track belongs to the same program and is confirmed' do + track = create(:track, state: 'confirmed', program: conference.program) event = build(:event, program: conference.program, track: track) expect(event.valid?).to eq true end @@ -108,26 +108,19 @@ describe Event do context 'is invalid' do it 'when the track doesn\'t have the same program' do - track = create(:track, state: 'confirmed', cfp_active: true) + track = create(:track, state: 'confirmed') event = build(:event, program: conference.program, track: track) expect(event.valid?).to eq false expect(event.errors[:track]).to eq ['is invalid'] end it 'when the track is unconfirmed' do - track = create(:track, cfp_active: true, program: conference.program) + track = create(:track, program: conference.program) allow(track).to receive(:confirmed?).and_return(false) event = build(:event, program: conference.program, track: track) expect(event.valid?).to eq false expect(event.errors[:track]).to eq ['is invalid'] end - - it 'when the track isn\'t included in the cfp' do - track = create(:track, state: 'confirmed', cfp_active: false, program: conference.program) - event = build(:event, program: conference.program, track: track) - expect(event.valid?).to eq false - expect(event.errors[:track]).to eq ['is invalid'] - end end end end @@ -382,4 +375,30 @@ describe Event do expect(other_event.week).to eq 48 end end + + describe '#selected_schedule_id' do + before :each do + conference.program.selected_schedule = create(:schedule, program: conference.program) + end + + context 'returns the program\'s selected_schedule_id' do + it 'when it doesn\'t have a track' do + create(:event_schedule, event: event, schedule: conference.program.selected_schedule) + expect(event.send(:selected_schedule_id)).to eq conference.program.selected_schedule_id + end + + it 'when it belongs to a regular track' do + event.track = create(:track, program: conference.program) + expect(event.send(:selected_schedule_id)).to eq conference.program.selected_schedule_id + end + end + + context 'returns the track\'s selected_schedule_id' do + it 'when it belongs to a self-organized track' do + event.track = create(:track, :self_organized, program: conference.program, state: 'confirmed') + event.track.selected_schedule = create(:schedule, program: conference.program, track: event.track) + expect(event.send(:selected_schedule_id)).to eq event.track.selected_schedule_id + end + end + end end diff --git a/spec/models/schedule_spec.rb b/spec/models/schedule_spec.rb index 6d1f7346..2dc1b826 100644 --- a/spec/models/schedule_spec.rb +++ b/spec/models/schedule_spec.rb @@ -4,6 +4,7 @@ describe Schedule do describe 'association' do it { should belong_to(:program) } + it { should belong_to(:track) } it { should have_many(:event_schedules).dependent(:destroy) } it { should have_many(:events).through(:event_schedules) } end diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb index f9c53653..d356a2c7 100644 --- a/spec/models/track_spec.rb +++ b/spec/models/track_spec.rb @@ -9,7 +9,9 @@ describe Track do it { is_expected.to belong_to(:program) } it { is_expected.to belong_to(:submitter).class_name('User') } it { is_expected.to belong_to(:room) } + it { is_expected.to belong_to(:selected_schedule).class_name('Schedule') } it { is_expected.to have_many(:events) } + it { is_expected.to have_many(:schedules) } end describe 'validation' do @@ -145,20 +147,20 @@ describe Track do end context 'is valid' do - it 'when the tracks are in different rooms' do + it 'when the tracks are in different rooms at the same time' do other_room = create(:room, venue: @conference.venue) create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: other_room, start_date: Date.current, end_date: Date.current) track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) expect(track.valid?).to eq true end - it 'when it ends before the other tracks' do + it 'when it ends before the other tracks in the same room' do create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current - 1.day, end_date: Date.current - 1.day) expect(track.valid?).to eq true end - it 'when it starts after the other tracks' do + it 'when it starts after the other tracks in the same room' do create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current + 1.day, end_date: Date.current + 1.day) expect(track.valid?).to eq true @@ -166,7 +168,7 @@ describe Track do end context 'is invalid' do - it 'when it starts or ends with another track in the same room' do + it 'when it starts and/or ends with another track in the same room' do create(:track, :self_organized, state: 'confirmed', program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) track = build(:track, :self_organized, program: @conference.program, room: @room, start_date: Date.current, end_date: Date.current) expect(track.valid?).to eq false @@ -264,6 +266,24 @@ describe Track do expect(@program.tracks.cfp_active.include?(@non_cfp_active_track)).to eq false end end + + describe '#self_organized' do + before :each do + @program = create(:program) + track.program = @program + track.save! + self_organized_track.program = @program + self_organized_track.save! + end + + it 'includes self-organized tracks' do + expect(@program.tracks.self_organized.include?(self_organized_track)).to eq true + end + + it 'excludes regular tracks' do + expect(@program.tracks.self_organized.include?(track)).to eq false + end + end end describe '#self_organized?' do @@ -334,7 +354,8 @@ describe Track do self_organized_track.cfp_active = true self_organized_track.save! @a_track_organizer.add_role 'track_organizer', self_organized_track - @an_event_of_the_track = create(:event, program: self_organized_track.program, track: self_organized_track) + @event_of_self_organized_track = create(:event, program: self_organized_track.program, track: self_organized_track, state: 'confirmed') + @schedule_of_self_organized_track = create(:schedule, program: self_organized_track.program, track: self_organized_track) end it 'revokes the role of the track organizer' do @@ -343,11 +364,24 @@ describe Track do expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false end - it 'removes the track from the events that have it set' do - expect(@an_event_of_the_track.track).to eq self_organized_track + it 'destroys the track\'s schedules' do + expect(Schedule.find(@schedule_of_self_organized_track.id)).to eq @schedule_of_self_organized_track self_organized_track.revoke_role_and_cleanup - @an_event_of_the_track.reload - expect(@an_event_of_the_track.track).to eq nil + expect(Schedule.find_by(id: @schedule_of_self_organized_track.id)).to eq nil + end + + it 'removes the track from the events that have it set' do + expect(@event_of_self_organized_track.track).to eq self_organized_track + self_organized_track.revoke_role_and_cleanup + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil + end + + it 'sets the state of the track\'s events to new' do + expect(@event_of_self_organized_track.state).to eq 'confirmed' + self_organized_track.revoke_role_and_cleanup + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.state).to eq 'new' end it 'is executed when the track is canceled' do @@ -355,15 +389,15 @@ describe Track do self_organized_track.save! self_organized_track.cancel expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false - @an_event_of_the_track.reload - expect(@an_event_of_the_track.track).to eq nil + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil end it 'is executed when the track is withdrawn' do self_organized_track.withdraw expect(@a_track_organizer.has_role?(:track_organizer, self_organized_track)).to eq false - @an_event_of_the_track.reload - expect(@an_event_of_the_track.track).to eq nil + @event_of_self_organized_track.reload + expect(@event_of_self_organized_track.track).to eq nil end end