No rooms added to this schedule, click on View Data to Add Rooms!
+<% end %>
\ No newline at end of file
diff --git a/app/views/shared/_insched_nav.html.erb b/app/views/shared/_insched_nav.html.erb
index fca31fa..f616970 100644
--- a/app/views/shared/_insched_nav.html.erb
+++ b/app/views/shared/_insched_nav.html.erb
@@ -6,9 +6,7 @@
-
+ <%= link_to 'Add Predefined Courses', schedule_room_bookings_path(@schedule), class: 'col btn btn-secondary px-4 mx-1' %>
diff --git a/config/routes.rb b/config/routes.rb
index 64608f4..c5aa2a4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -34,13 +34,13 @@
resources :instructors, only: [:index]
post :upload_instructors, on: :member
-
resources :courses, only: [:index]
post :upload_courses, on: :member
post 'time_slots', to: 'time_slots#filter', as: 'filter_time_slots'
get '/time_slots', to: 'time_slots#index'
+ resources :room_bookings, only: [:index]
end
# Show Time Slot View
diff --git a/db/migrate/20241027021034_create_room_bookings.rb b/db/migrate/20241027021034_create_room_bookings.rb
new file mode 100644
index 0000000..5e5164c
--- /dev/null
+++ b/db/migrate/20241027021034_create_room_bookings.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class CreateRoomBookings < ActiveRecord::Migration[7.2]
+ def change
+ create_table :room_bookings do |t|
+ t.references :room, null: false, foreign_key: true
+ t.references :time_slot, null: false, foreign_key: true
+ t.boolean :is_available
+ t.boolean :is_lab
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8ebee1f..1314eff 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,9 +10,9 @@
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
-# It's strongly recommended that you check this file into your version control syste
+# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 20_241_023_233_115) do
+ActiveRecord::Schema[7.2].define(version: 20_241_027_021_034) do
create_table 'courses', force: :cascade do |t|
t.string 'course_number'
t.integer 'max_seats'
@@ -24,7 +24,6 @@
t.index ['schedule_id'], name: 'index_courses_on_schedule_id'
end
-
create_table 'instructors', force: :cascade do |t|
t.integer 'id_number'
t.string 'last_name'
@@ -40,6 +39,17 @@
t.index ['schedule_id'], name: 'index_instructors_on_schedule_id'
end
+ create_table 'room_bookings', force: :cascade do |t|
+ t.integer 'room_id', null: false
+ t.integer 'time_slot_id', null: false
+ t.boolean 'is_available'
+ t.boolean 'is_lab'
+ t.datetime 'created_at', null: false
+ t.datetime 'updated_at', null: false
+ t.index ['room_id'], name: 'index_room_bookings_on_room_id'
+ t.index ['time_slot_id'], name: 'index_room_bookings_on_time_slot_id'
+ end
+
create_table 'rooms', force: :cascade do |t|
t.integer 'campus'
t.boolean 'is_lecture_hall'
@@ -70,13 +80,8 @@
t.datetime 'created_at', null: false
t.datetime 'updated_at', null: false
t.index ['course_id'], name: 'index_sections_on_course_id'
-
end
-
-
-
-
create_table 'time_slots', force: :cascade do |t|
t.string 'day'
t.string 'start_time'
@@ -97,10 +102,10 @@
t.index ['email'], name: 'index_users_on_email', unique: true
end
-
add_foreign_key 'courses', 'schedules'
add_foreign_key 'instructors', 'schedules'
+ add_foreign_key 'room_bookings', 'rooms'
+ add_foreign_key 'room_bookings', 'time_slots'
add_foreign_key 'rooms', 'schedules'
add_foreign_key 'sections', 'courses'
-
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 058a583..b81c98d 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -10,6 +10,8 @@
# MovieGenre.find_or_create_by!(name: genre_name)
# end
+TimeSlot.delete_all
+
TimeSlot.create([
{ day: 'MWF', start_time: '8:00', end_time: '8:50', slot_type: 'LEC' },
{ day: 'MWF', start_time: '9:10', end_time: '10:00', slot_type: 'LEC' },
diff --git a/features/room_bookings.feature b/features/room_bookings.feature
new file mode 100644
index 0000000..ba8d905
--- /dev/null
+++ b/features/room_bookings.feature
@@ -0,0 +1,44 @@
+Feature: Rooms Page
+ Scenario: Checking a room booking
+ Given I am logged in as a user with first name "Test"
+ And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024"
+ And the following rooms exist for that schedule:
+ | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall |
+ | CS | BLDG1 | 101 | 30 | true | true | true | true |
+ | GV | BLDG2 | 102 | 50 | true | true | true | true |
+ | CS | BLDG3 | 102 | 50 | false | true | true | true |
+ And the following time slots exist for that schedule:
+ | day | start_time | end_time | slot_type |
+ | MWF | 09:00 | 10:00 | "LEC" |
+ | MW | 08:00 | 10:00 | "LEC" |
+ When I visit the room bookings page for "Sched 1"
+ Then I should see "View Data"
+ And I should see "09:00 - 10:00"
+ And I should see "BLDG1 101"
+
+ Scenario: Checking a room booking and changing tab
+ Given I am logged in as a user with first name "Test"
+ And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024"
+ And the following rooms exist for that schedule:
+ | campus | building_code | room_number | capacity | is_active | is_lab | is_learning_studio | is_lecture_hall |
+ | CS | BLDG1 | 101 | 30 | true | true | true | true |
+ | GV | BLDG2 | 102 | 50 | true | true | true | true |
+ | CS | BLDG3 | 102 | 50 | false | true | true | true |
+ And the following time slots exist for that schedule:
+ | day | start_time | end_time | slot_type |
+ | MWF | 09:00 | 10:00 | "LEC" |
+ | MW | 08:00 | 10:00 | "LEC" |
+ When I visit the room bookings page for "Sched 1"
+ And I click "MW"
+ Then I should see "View Data"
+ And I should see "08:00 - 10:00"
+ And I should not see "09:00 - 10:00"
+ And I should see "BLDG1 101"
+
+ Scenario: Checking empty room bookings
+ Given I am logged in as a user with first name "Test"
+ And a schedule exists with the schedule name "Sched 1" and semester name "Fall 2024"
+ When I visit the room bookings page for "Sched 1"
+ Then I should see "View Data"
+ And I should not see "Generate Remaining"
+ And I should see "No rooms added to this schedule, click on View Data to Add Rooms!"
\ No newline at end of file
diff --git a/features/step_definitions/room_bookings_steps.rb b/features/step_definitions/room_bookings_steps.rb
new file mode 100644
index 0000000..d14318e
--- /dev/null
+++ b/features/step_definitions/room_bookings_steps.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+Given(/I am on the room bookings page for "(.*)"/) do |schedule_name|
+ @schedule = Schedule.find(schedule_name: schedule_name)
+ visit schedule_room_bookings_path(@schedule)
+end
+
+When(/^I visit the room bookings page for "(.*)"$/) do |schedule_name|
+ @schedule = Schedule.where(schedule_name: schedule_name)[0]
+ visit schedule_room_bookings_path(schedule_id: @schedule.id)
+end
+
+Given('the following time slots exist for that schedule:') do |table|
+ table.hashes.each do |time_slot|
+ TimeSlot.create!(
+ day: time_slot['day'],
+ start_time: time_slot['start_time'],
+ end_time: time_slot['end_time'],
+ slot_type: time_slot['slot_type']
+ )
+ end
+end
diff --git a/spec/controllers/courses_controller_spec.rb b/spec/controllers/courses_controller_spec.rb
index 244a9f9..6e32bfe 100644
--- a/spec/controllers/courses_controller_spec.rb
+++ b/spec/controllers/courses_controller_spec.rb
@@ -60,7 +60,6 @@
let!(:section31) do
create(:section, section_number: '500', seats_alloted: 150, course_id: course3.id)
end
-
context 'without any sorting' do
it 'assigns all courses to @courses' do
@@ -69,15 +68,14 @@
end
end
-
context 'with sorting by course_number' do
it 'assigns courses sorted by course_number ascending to @courses' do
get :index, params: { schedule_id: schedule.id, sort: 'course_number', direction: 'asc' }
expect(assigns(:courses)).to eq([course1, course2, course3])
end
it 'assigns courses sorted by course_number descending to @courses' do
- get :index, params: { schedule_id: schedule.id, sort: 'course_number', direction: 'desc' }
- expect(assigns(:courses)).to eq([course3, course2, course1])
+ get :index, params: { schedule_id: schedule.id, sort: 'course_number', direction: 'desc' }
+ expect(assigns(:courses)).to eq([course3, course2, course1])
end
end
@@ -87,19 +85,19 @@
expect(assigns(:courses)).to eq([course1, course2, course3])
end
it 'assigns courses sorted by max_seats descending to @courses' do
- get :index, params: { schedule_id: schedule.id, sort: 'max_seats', direction: 'desc' }
- expect(assigns(:courses)).to eq([course3, course2, course1])
+ get :index, params: { schedule_id: schedule.id, sort: 'max_seats', direction: 'desc' }
+ expect(assigns(:courses)).to eq([course3, course2, course1])
end
end
-
+
context 'with sorting by lecture_type' do
it 'assigns courses sorted by lecture_type ascending to @courses' do
get :index, params: { schedule_id: schedule.id, sort: 'lecture_type', direction: 'asc' }
expect(assigns(:courses)).to eq([course3, course1, course2])
end
it 'assigns courses sorted by lecture_type descending to @courses' do
- get :index, params: { schedule_id: schedule.id, sort: 'lecture_type', direction: 'desc' }
- expect(assigns(:courses)).to eq([course2, course1, course3])
+ get :index, params: { schedule_id: schedule.id, sort: 'lecture_type', direction: 'desc' }
+ expect(assigns(:courses)).to eq([course2, course1, course3])
end
end
@@ -109,8 +107,8 @@
expect(assigns(:courses)).to eq([course3, course2, course1])
end
it 'assigns courses sorted by num_labs descending to @courses' do
- get :index, params: { schedule_id: schedule.id, sort: 'num_labs', direction: 'desc' }
- expect(assigns(:courses)).to eq([course1, course2, course3])
+ get :index, params: { schedule_id: schedule.id, sort: 'num_labs', direction: 'desc' }
+ expect(assigns(:courses)).to eq([course1, course2, course3])
end
end
end
diff --git a/spec/controllers/room_bookings_controller_spec.rb b/spec/controllers/room_bookings_controller_spec.rb
new file mode 100644
index 0000000..cec0ca3
--- /dev/null
+++ b/spec/controllers/room_bookings_controller_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+# spec/controllers/room_bookings_controller_spec.rb
+require 'rails_helper'
+
+RSpec.describe RoomBookingsController, type: :controller do
+ let!(:schedule) { create(:schedule) }
+ let!(:room1) { create(:room, schedule: schedule) }
+ let!(:room2) { create(:room, schedule: schedule) }
+ let!(:time_slot1) { create(:time_slot, day: 'Monday', start_time: '09:00', end_time: '10:00') }
+ let!(:time_slot2) { create(:time_slot, day: 'Monday', start_time: '10:00', end_time: '11:00') }
+ let!(:room_booking1) { create(:room_booking, room: room1, time_slot: time_slot1, is_available: true) }
+ let!(:room_booking2) { create(:room_booking, room: room2, time_slot: time_slot2, is_available: false) }
+
+ before do
+ @user = User.create!(uid: '12345', provider: 'google_oauth2', email: 'test@example.com', first_name: 'John',
+ last_name: 'Doe')
+ allow(controller).to receive(:logged_in?).and_return(true)
+ controller.instance_variable_set(:@current_user, @user)
+ end
+
+ describe 'GET #index' do
+ context 'when room_booking already exists' do
+ before do
+ get :index, params: { schedule_id: schedule.id }
+ end
+
+ it 'returns a successful response' do
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'assigns @rooms' do
+ expect(assigns(:rooms)).to match_array([room1, room2])
+ end
+
+ it 'assigns @time_slots' do
+ expect(assigns(:time_slots)).to match_array([time_slot1, time_slot2])
+ end
+
+ it 'assigns @bookings_matrix with room_booking data' do
+ bookings_matrix = assigns(:bookings_matrix)
+ expect(bookings_matrix[[room1.id, time_slot1.id]]).to eq(room_booking1)
+ expect(bookings_matrix[[room2.id, time_slot2.id]]).to eq(room_booking2)
+ end
+
+ it 'renders the index template' do
+ expect(response).to render_template(:index)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/schedules_controller_spec.rb b/spec/controllers/schedules_controller_spec.rb
index d0db2f9..f04c322 100644
--- a/spec/controllers/schedules_controller_spec.rb
+++ b/spec/controllers/schedules_controller_spec.rb
@@ -130,6 +130,7 @@
end
end
end
+
describe 'POST #upload_instructors' do
let(:file_valid) { fixture_file_upload(Rails.root.join('spec/fixtures/instructors/instructors_valid.csv'), 'text/csv') }
let(:file_invalid) { fixture_file_upload(Rails.root.join('spec/fixtures/rooms/rooms_invalid.csv'), 'text/csv') }
@@ -162,4 +163,37 @@
end
end
end
+
+ describe 'POST #upload_courses' do
+ let(:file_valid) { fixture_file_upload(Rails.root.join('spec/fixtures/courses/Course_list_valid.csv'), 'text/csv') }
+ let(:file_invalid) { fixture_file_upload(Rails.root.join('spec/fixtures/courses/Course_list_invalid.csv'), 'text/csv') }
+ let!(:schedule1) { create(:schedule) }
+
+ context 'with a valid CSV file' do
+ it "processes the CSV file, sets a success flash, and redirects to the user's page" do
+ post :upload_courses, params: { id: schedule1.id, course_file: file_valid }
+
+ expect(response).to have_http_status(:redirect)
+ expect(response).to redirect_to(schedule_path(schedule1)) # Redirect to the current user's page
+ expect(flash[:notice]).to eq('Courses successfully uploaded.')
+ end
+ end
+
+ context 'when invalid CSV file is selected' do
+ it "sets an error flash and redirects to the user's page" do
+ post :upload_courses, params: { id: schedule1.id, course_file: file_invalid }
+ expect(flash[:alert]).to include('Missing required headers:')
+ end
+ end
+
+ context 'when no CSV file is selected' do
+ it "sets an error flash and redirects to the user's page" do
+ post :upload_courses, params: { id: schedule1.id, room_file: nil }
+
+ expect(response).to have_http_status(:redirect)
+ expect(response).to redirect_to(schedule_path(schedule1)) # Redirect to the current user's page
+ expect(flash[:alert]).to eq('Please upload a CSV file.')
+ end
+ end
+ end
end
diff --git a/spec/factories.rb b/spec/factories.rb
index 6be7ba9..60d7914 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -16,6 +16,7 @@
is_learning_studio { false }
is_lecture_hall { false }
comments { 'A large lecture hall.' }
+ association :schedule
end
factory :instructor do
@@ -29,4 +30,17 @@
beaware_of { 'Some notes or warnings.' } # Default text
association :schedule # Associate with a Schedule, assuming you have a Schedule factory as well
end
+
+ factory :room_booking do
+ association :room
+ association :time_slot
+ is_available { true }
+ end
+
+ factory :time_slot do
+ day { 'Monday' }
+ start_time { '09:00' }
+ end_time { '10:00' }
+ slot_type { 'Lecture' }
+ end
end
diff --git a/test/controllers/room_bookings_controller_test.rb b/test/controllers/room_bookings_controller_test.rb
new file mode 100644
index 0000000..b579650
--- /dev/null
+++ b/test/controllers/room_bookings_controller_test.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class RoomBookingsControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/factories/room_bookings.rb b/test/factories/room_bookings.rb
new file mode 100644
index 0000000..5ea3109
--- /dev/null
+++ b/test/factories/room_bookings.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ # factory :room_booking do
+ # room { nil }
+ # time_slot { nil }
+ # is_available { false }
+ # is_lab { false }
+ # end
+end
diff --git a/test/models/room_booking_test.rb b/test/models/room_booking_test.rb
new file mode 100644
index 0000000..cb1a846
--- /dev/null
+++ b/test/models/room_booking_test.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class RoomBookingTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end