From 103bf4a72d61eb141a7dd55daeb3b68f41800109 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Sat, 26 Oct 2024 21:48:29 -0500 Subject: [PATCH 01/12] Added Basic Rooms Booking Structure --- app/controllers/room_bookings_controller.rb | 13 ++ app/helpers/room_bookings_helper.rb | 2 + app/models/room.rb | 1 + app/models/room_booking.rb | 4 + app/models/time_slot.rb | 2 + app/views/room_bookings/index.html.erb | 3 + config/routes.rb | 1 + .../20241027021034_create_room_bookings.rb | 12 ++ db/schema.rb | 139 +++++++++++------- .../room_bookings_controller_test.rb | 7 + test/factories/room_bookings.rb | 8 + test/models/room_booking_test.rb | 7 + 12 files changed, 146 insertions(+), 53 deletions(-) create mode 100644 app/controllers/room_bookings_controller.rb create mode 100644 app/helpers/room_bookings_helper.rb create mode 100644 app/models/room_booking.rb create mode 100644 app/views/room_bookings/index.html.erb create mode 100644 db/migrate/20241027021034_create_room_bookings.rb create mode 100644 test/controllers/room_bookings_controller_test.rb create mode 100644 test/factories/room_bookings.rb create mode 100644 test/models/room_booking_test.rb diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb new file mode 100644 index 0000000..b44f841 --- /dev/null +++ b/app/controllers/room_bookings_controller.rb @@ -0,0 +1,13 @@ +class RoomBookingsController < ApplicationController + def index + schedule_id = params[:schedule_id] + @schedule = Schedule.find(params[:schedule_id]) + @rooms = @schedule.rooms + @time_slots = TimeSlot.all + + # Fetch room bookings only for the specified schedule + @room_bookings = RoomBooking.joins(:room, :time_slot) + .where(rooms: { schedule_id: schedule_id }) + end +end + \ No newline at end of file diff --git a/app/helpers/room_bookings_helper.rb b/app/helpers/room_bookings_helper.rb new file mode 100644 index 0000000..43246d0 --- /dev/null +++ b/app/helpers/room_bookings_helper.rb @@ -0,0 +1,2 @@ +module RoomBookingsHelper +end diff --git a/app/models/room.rb b/app/models/room.rb index ff9d80c..fba3313 100644 --- a/app/models/room.rb +++ b/app/models/room.rb @@ -3,6 +3,7 @@ # Room Model class Room < ApplicationRecord belongs_to :schedule + has_many :room_bookings, dependent: :destroy enum :campus, { NONE: 0, CS: 1, GV: 2 } validates :building_code, presence: true diff --git a/app/models/room_booking.rb b/app/models/room_booking.rb new file mode 100644 index 0000000..721b30b --- /dev/null +++ b/app/models/room_booking.rb @@ -0,0 +1,4 @@ +class RoomBooking < ApplicationRecord + belongs_to :room + belongs_to :time_slot +end diff --git a/app/models/time_slot.rb b/app/models/time_slot.rb index d48c8dc..655e4ba 100644 --- a/app/models/time_slot.rb +++ b/app/models/time_slot.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class TimeSlot < ApplicationRecord + has_many :room_bookings, dependent: :destroy + validates :day, presence: true validates :start_time, presence: true validates :end_time, presence: true diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb new file mode 100644 index 0000000..7fdc8d9 --- /dev/null +++ b/app/views/room_bookings/index.html.erb @@ -0,0 +1,3 @@ +
+ We are where you at <%= @schedule.schedule_name %> room bookings. +
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index b0789da..0e588b8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,6 +35,7 @@ post :upload_instructors, 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..dc6b598 --- /dev/null +++ b/db/migrate/20241027021034_create_room_bookings.rb @@ -0,0 +1,12 @@ +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 44e9d84..8b3e384 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -12,65 +10,100 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 20_241_018_191_419) do - create_table 'instructors', force: :cascade do |t| - t.integer 'id_number' - t.string 'last_name' - t.string 'first_name' - t.string 'middle_name' - t.string 'email' - t.boolean 'before_9' - t.boolean 'after_3' - t.text 'beaware_of' - t.integer 'schedule_id' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false - t.index ['schedule_id'], name: 'index_instructors_on_schedule_id' +ActiveRecord::Schema[7.2].define(version: 2024_10_27_021034) do + create_table "courses", force: :cascade do |t| + t.string "course_number" + t.integer "max_seats" + t.string "lecture_type" + t.integer "num_labs" + t.integer "schedule_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + 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" + t.string "first_name" + t.string "middle_name" + t.string "email" + t.boolean "before_9" + t.boolean "after_3" + t.text "beaware_of" + t.integer "schedule_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + 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" + t.boolean "is_learning_studio" + t.boolean "is_lab" + t.string "building_code" + t.string "room_number" + t.integer "capacity" + t.boolean "is_active" + t.string "comments" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "schedule_id", default: -1, null: false + t.index ["schedule_id"], name: "index_rooms_on_schedule_id" end - create_table 'rooms', force: :cascade do |t| - t.integer 'campus' - t.boolean 'is_lecture_hall' - t.boolean 'is_learning_studio' - t.boolean 'is_lab' - t.string 'building_code' - t.string 'room_number' - t.integer 'capacity' - t.boolean 'is_active' - t.string 'comments' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false - t.integer 'schedule_id', default: -1, null: false - t.index ['schedule_id'], name: 'index_rooms_on_schedule_id' + create_table "schedules", force: :cascade do |t| + t.string "schedule_name" + t.string "semester_name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table 'schedules', force: :cascade do |t| - t.string 'schedule_name' - t.string 'semester_name' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false + create_table "sections", force: :cascade do |t| + t.string "section_number" + t.integer "seats_alloted" + t.integer "course_id", null: false + 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' - t.string 'end_time' - t.string 'slot_type' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false + create_table "time_slots", force: :cascade do |t| + t.string "day" + t.string "start_time" + t.string "end_time" + t.string "slot_type" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end - create_table 'users', force: :cascade do |t| - t.string 'email' - t.string 'first_name' - t.string 'last_name' - t.datetime 'created_at', null: false - t.datetime 'updated_at', null: false - t.string 'uid' - t.string 'provider' - t.index ['email'], name: 'index_users_on_email', unique: true + create_table "users", force: :cascade do |t| + t.string "email" + t.string "first_name" + t.string "last_name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "uid" + t.string "provider" + t.index ["email"], name: "index_users_on_email", unique: true end - add_foreign_key 'instructors', 'schedules' - add_foreign_key 'rooms', 'schedules' + 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/test/controllers/room_bookings_controller_test.rb b/test/controllers/room_bookings_controller_test.rb new file mode 100644 index 0000000..38f4905 --- /dev/null +++ b/test/controllers/room_bookings_controller_test.rb @@ -0,0 +1,7 @@ +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..465250c --- /dev/null +++ b/test/factories/room_bookings.rb @@ -0,0 +1,8 @@ +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..51e1a97 --- /dev/null +++ b/test/models/room_booking_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class RoomBookingTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 593fbbe870e19b6990f26558736610755bd95879 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Sat, 26 Oct 2024 22:02:24 -0500 Subject: [PATCH 02/12] Added navbar based on unique tabs available --- app/controllers/room_bookings_controller.rb | 3 +- app/views/room_bookings/index.html.erb | 31 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index b44f841..4882668 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -4,7 +4,8 @@ def index @schedule = Schedule.find(params[:schedule_id]) @rooms = @schedule.rooms @time_slots = TimeSlot.all - + @tabs = TimeSlot.distinct.pluck(:day) + @active_tab = params[:active_tab] || @tabs[0] # Fetch room bookings only for the specified schedule @room_bookings = RoomBooking.joins(:room, :time_slot) .where(rooms: { schedule_id: schedule_id }) diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 7fdc8d9..417a03e 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -1,3 +1,28 @@ -
- We are where you at <%= @schedule.schedule_name %> room bookings. -
\ No newline at end of file +
+
+ > +
+ <%= @schedule.schedule_name %> +
+
+
+ + +
+
+
+ \ No newline at end of file From 1a5461bc97d83f4d69e31a044f0b66d1f7aef3c3 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Sat, 26 Oct 2024 22:28:50 -0500 Subject: [PATCH 03/12] Basic View Added --- app/controllers/room_bookings_controller.rb | 10 ++++-- app/views/room_bookings/index.html.erb | 37 ++++++++++++++++++++- db/seeds.rb | 2 ++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index 4882668..a90fefc 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -3,12 +3,18 @@ def index schedule_id = params[:schedule_id] @schedule = Schedule.find(params[:schedule_id]) @rooms = @schedule.rooms - @time_slots = TimeSlot.all @tabs = TimeSlot.distinct.pluck(:day) @active_tab = params[:active_tab] || @tabs[0] + @time_slots = TimeSlot.where(time_slots: {day: @active_tab}) + # Fetch room bookings only for the specified schedule @room_bookings = RoomBooking.joins(:room, :time_slot) - .where(rooms: { schedule_id: schedule_id }) + .where(rooms: { schedule_id: schedule_id }, time_slots: {day: @active_tab}) + + # Organize room bookings in a hash with room_id and time_slot_id as keys + @bookings_matrix = @room_bookings.each_with_object({}) do |booking, hash| + hash[[booking.room_id, booking.time_slot_id]] = booking + end end end \ No newline at end of file diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 417a03e..4bed033 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -25,4 +25,39 @@ <% end %> - \ No newline at end of file + +
+ + + + + + <% @time_slots.each do |time_slot| %> + + <% end %> + + + + + <% @rooms.each do |room| %> + + + + <% @time_slots.each do |time_slot| %> + <% booking = @bookings_matrix[[room.id, time_slot.id]] %> + + + <% end %> + + <% end %> + +
Room / Time Slot<%= time_slot.start_time %> - <%= time_slot.end_time %>
<%= room.building_code %> <%= room.room_number %> + <% if booking %> + <%= booking.is_available ? 'Available' : 'Booked' %> | + <%= booking.is_lab ? 'Lab' : 'Lecture' %> + <% else %> + NA + <% end %> +
+ +
\ No newline at end of file diff --git a/db/seeds.rb b/db/seeds.rb index a35ad8f..0da9f4d 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' }, From 44a644ca5c5f74c66e1bf452bb52519b9970f16b Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Sat, 26 Oct 2024 22:32:42 -0500 Subject: [PATCH 04/12] Adding only active rooms --- app/controllers/room_bookings_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index a90fefc..ce83d87 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -2,7 +2,7 @@ class RoomBookingsController < ApplicationController def index schedule_id = params[:schedule_id] @schedule = Schedule.find(params[:schedule_id]) - @rooms = @schedule.rooms + @rooms = @schedule.rooms.where(is_active: true) @tabs = TimeSlot.distinct.pluck(:day) @active_tab = params[:active_tab] || @tabs[0] @time_slots = TimeSlot.where(time_slots: {day: @active_tab}) From 43886499e53ca4605f931af57a080bf211dad76e Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Sun, 27 Oct 2024 13:41:31 -0500 Subject: [PATCH 05/12] Routing Added --- app/controllers/room_bookings_controller.rb | 2 +- app/views/room_bookings/index.html.erb | 66 ++++++++++----------- app/views/shared/_insched_nav.html.erb | 4 +- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index ce83d87..deeb3e8 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -2,7 +2,7 @@ class RoomBookingsController < ApplicationController def index schedule_id = params[:schedule_id] @schedule = Schedule.find(params[:schedule_id]) - @rooms = @schedule.rooms.where(is_active: true) + @rooms = @schedule.rooms.where(is_active: true).where.not(building_code: 'ONLINE') @tabs = TimeSlot.distinct.pluck(:day) @active_tab = params[:active_tab] || @tabs[0] @time_slots = TimeSlot.where(time_slots: {day: @active_tab}) diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 4bed033..8a37b67 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -6,9 +6,7 @@
- + <%= link_to 'View Data', schedule_rooms_path(@schedule.id), class: 'col btn btn-secondary px-4 mx-1' %> @@ -26,38 +24,34 @@
-
- - - - - - <% @time_slots.each do |time_slot| %> - - <% end %> - - - - - <% @rooms.each do |room| %> - - - - <% @time_slots.each do |time_slot| %> - <% booking = @bookings_matrix[[room.id, time_slot.id]] %> - - + + + <% @rooms.each do |room| %> + + + + <% @time_slots.each do |time_slot| %> + <% booking = @bookings_matrix[[room.id, time_slot.id]] %> + + + <% end %> + + <% end %> + +
Room / Time Slot<%= time_slot.start_time %> - <%= time_slot.end_time %>
<%= room.building_code %> <%= room.room_number %> - <% if booking %> - <%= booking.is_available ? 'Available' : 'Booked' %> | - <%= booking.is_lab ? 'Lab' : 'Lecture' %> - <% else %> - NA +
+ + + + + <% @time_slots.each do |time_slot| %> + <% end %> - - <% end %> - - <% end %> - -
Room / Time Slot<%= time_slot.start_time %> - <%= time_slot.end_time %>
- +
<%= room.building_code %> <%= room.room_number %> + <% if booking %> + <%= booking.is_available ? 'Available' : 'Booked' %> | + <%= booking.is_lab ? 'Lab' : 'Lecture' %> + <% else %> + <% 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' %> From 36b9ce10b5809a0d6663cafae627989fcb87198b Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Mon, 28 Oct 2024 18:34:09 -0500 Subject: [PATCH 06/12] Added Column hover in the table --- app/assets/stylesheets/room_bookings.css | 4 ++++ app/javascript/room_bookings.js | 16 ++++++++++++++++ app/views/layouts/application.html.erb | 1 + app/views/room_bookings/index.html.erb | 17 +++++++++-------- 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 app/assets/stylesheets/room_bookings.css create mode 100644 app/javascript/room_bookings.js diff --git a/app/assets/stylesheets/room_bookings.css b/app/assets/stylesheets/room_bookings.css new file mode 100644 index 0000000..e2d9e95 --- /dev/null +++ b/app/assets/stylesheets/room_bookings.css @@ -0,0 +1,4 @@ +.is-hover { + --bs-bg-opacity: 0.1; + background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; +} \ No newline at end of file diff --git a/app/javascript/room_bookings.js b/app/javascript/room_bookings.js new file mode 100644 index 0000000..543cadb --- /dev/null +++ b/app/javascript/room_bookings.js @@ -0,0 +1,16 @@ +document.addEventListener("DOMContentLoaded", function () { + const tds = document.querySelectorAll(".grid-view-matrix td"); + + tds.forEach((td) => { + const table = td.closest("table"); + const colIndex = td.cellIndex; + const cols = table.querySelectorAll(`td:nth-child(${colIndex + 1})`); + + td.addEventListener("mouseover", () => { + cols.forEach((hover) => hover.classList.add("is-hover")); + }); + td.addEventListener("mouseleave", () => { + cols.forEach((hover) => hover.classList.remove("is-hover")); + }); + }); +}) \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 3a2bbb0..01d75c5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -17,6 +17,7 @@ <%= stylesheet_link_tag "https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css", integrity: "sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65", crossorigin: "anonymous" %> <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= javascript_importmap_tags %> + <%= javascript_include_tag 'room_bookings' %> diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 8a37b67..3459aa0 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -25,21 +25,22 @@
- - +
+ - - <% @time_slots.each do |time_slot| %> - + + <% @rooms.each do |room| %> + + <% end %> - <% @rooms.each do |room| %> + <% @time_slots.each do |time_slot| %> - + - <% @time_slots.each do |time_slot| %> + <% @rooms.each do |room| %> <% booking = @bookings_matrix[[room.id, time_slot.id]] %>
Room / Time Slot<%= time_slot.start_time %> - <%= time_slot.end_time %>Times \ Rooms<%= room.building_code %> <%= room.room_number %>
<%= room.building_code %> <%= room.room_number %><%= time_slot.start_time %> - <%= time_slot.end_time %> From 2550c5096b1e21d38046d9941fcc0d1ada34dc18 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 15:42:10 -0500 Subject: [PATCH 07/12] robocop fixes --- app/controllers/room_bookings_controller.rb | 33 ++-- app/controllers/schedules_controller.rb | 2 +- app/controllers/welcome_controller.rb | 1 + app/helpers/room_bookings_helper.rb | 2 + app/models/room_booking.rb | 2 + app/models/time_slot.rb | 2 +- app/services/csv_handler.rb | 125 +++++++------- .../20241027021034_create_room_bookings.rb | 2 + db/schema.rb | 162 +++++++++--------- spec/services/csv_handler_spec.rb | 146 ++++++++-------- .../room_bookings_controller_test.rb | 4 +- test/factories/room_bookings.rb | 2 + test/models/room_booking_test.rb | 4 +- 13 files changed, 249 insertions(+), 238 deletions(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index deeb3e8..dd0b275 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -1,20 +1,21 @@ +# frozen_string_literal: true + class RoomBookingsController < ApplicationController - def index - schedule_id = params[:schedule_id] - @schedule = Schedule.find(params[:schedule_id]) - @rooms = @schedule.rooms.where(is_active: true).where.not(building_code: 'ONLINE') - @tabs = TimeSlot.distinct.pluck(:day) - @active_tab = params[:active_tab] || @tabs[0] - @time_slots = TimeSlot.where(time_slots: {day: @active_tab}) - - # Fetch room bookings only for the specified schedule - @room_bookings = RoomBooking.joins(:room, :time_slot) - .where(rooms: { schedule_id: schedule_id }, time_slots: {day: @active_tab}) + def index + schedule_id = params[:schedule_id] + @schedule = Schedule.find(params[:schedule_id]) + @rooms = @schedule.rooms.where(is_active: true).where.not(building_code: 'ONLINE') + @tabs = TimeSlot.distinct.pluck(:day) + @active_tab = params[:active_tab] || @tabs[0] + @time_slots = TimeSlot.where(time_slots: { day: @active_tab }) + + # Fetch room bookings only for the specified schedule + @room_bookings = RoomBooking.joins(:room, :time_slot) + .where(rooms: { schedule_id: schedule_id }, time_slots: { day: @active_tab }) - # Organize room bookings in a hash with room_id and time_slot_id as keys - @bookings_matrix = @room_bookings.each_with_object({}) do |booking, hash| - hash[[booking.room_id, booking.time_slot_id]] = booking - end + # Organize room bookings in a hash with room_id and time_slot_id as keys + @bookings_matrix = @room_bookings.each_with_object({}) do |booking, hash| + hash[[booking.room_id, booking.time_slot_id]] = booking end + end end - \ No newline at end of file diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index d6dfce7..da2bf0f 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -65,7 +65,7 @@ def upload_instructors csv_handler = CsvHandler.new csv_handler.upload(params[:instructor_file]) flash_result = csv_handler.parse_instructor_csv(@schedule.id) - flash[flash_result.keys.first] = flash_result.values.first + flash[flash_result.keys.first] = flash_result.values.first else flash[:alert] = 'Please upload a CSV file.' end diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index 7e1beda..d8b6877 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -9,6 +9,7 @@ class WelcomeController < ApplicationController def index return unless logged_in? + redirect_to schedules_path end end diff --git a/app/helpers/room_bookings_helper.rb b/app/helpers/room_bookings_helper.rb index 43246d0..781d7ce 100644 --- a/app/helpers/room_bookings_helper.rb +++ b/app/helpers/room_bookings_helper.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + module RoomBookingsHelper end diff --git a/app/models/room_booking.rb b/app/models/room_booking.rb index 721b30b..850e9a8 100644 --- a/app/models/room_booking.rb +++ b/app/models/room_booking.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RoomBooking < ApplicationRecord belongs_to :room belongs_to :time_slot diff --git a/app/models/time_slot.rb b/app/models/time_slot.rb index 655e4ba..2887c4c 100644 --- a/app/models/time_slot.rb +++ b/app/models/time_slot.rb @@ -2,7 +2,7 @@ class TimeSlot < ApplicationRecord has_many :room_bookings, dependent: :destroy - + validates :day, presence: true validates :start_time, presence: true validates :end_time, presence: true diff --git a/app/services/csv_handler.rb b/app/services/csv_handler.rb index a1f1cd2..3b75b49 100644 --- a/app/services/csv_handler.rb +++ b/app/services/csv_handler.rb @@ -8,82 +8,75 @@ def upload(file) end def parse_room_csv(schedule_id) - begin - ActiveRecord::Base.transaction do - room_data = CSV.parse(@file.read, headers: true) - room_data.each do |row| - Room.create!( - schedule_id: schedule_id, - campus: row['campus'], - building_code: row['building_code'], - room_number: row['room_number'], - capacity: row['capacity'].to_i, - is_lecture_hall: row['is_lecture_hall'] == 'True', - is_learning_studio: row['is_learning_studio'] == 'True', - is_lab: row['is_lab'] == 'True', - is_active: row['is_active'] == 'True', - comments: row['comments'] - ) - end + ActiveRecord::Base.transaction do + room_data = CSV.parse(@file.read, headers: true) + room_data.each do |row| + Room.create!( + schedule_id: schedule_id, + campus: row['campus'], + building_code: row['building_code'], + room_number: row['room_number'], + capacity: row['capacity'].to_i, + is_lecture_hall: row['is_lecture_hall'] == 'True', + is_learning_studio: row['is_learning_studio'] == 'True', + is_lab: row['is_lab'] == 'True', + is_active: row['is_active'] == 'True', + comments: row['comments'] + ) end - # Flash data to be received by controller - return { notice: 'Rooms successfully uploaded.' } - rescue StandardError => e - return { alert: "There was an error uploading the CSV file: #{e.message}" } end + # Flash data to be received by controller + { notice: 'Rooms successfully uploaded.' } + rescue StandardError => e + { alert: "There was an error uploading the CSV file: #{e.message}" } end def parse_instructor_csv(schedule_id) - begin - ActiveRecord::Base.transaction do - instructor_data = CSV.parse(@file.read) - actual_headers = instructor_data[1] - required_headers = [ - 'anonimized ID', - 'First Name', - 'Last Name', - 'Email', - 'Teaching before 9:00 am.', - 'Teaching after 3:00 pm.', - 'Middle Name', - 'Is there anything else we should be aware of regarding your teaching load (special course reduction, ...)' # Optional: Include if needed - ] + ActiveRecord::Base.transaction do + instructor_data = CSV.parse(@file.read) + actual_headers = instructor_data[1] + required_headers = [ + 'anonimized ID', + 'First Name', + 'Last Name', + 'Email', + 'Teaching before 9:00 am.', + 'Teaching after 3:00 pm.', + 'Middle Name', + 'Is there anything else we should be aware of regarding your teaching load (special course reduction, ...)' # Optional: Include if needed + ] - missing_headers = required_headers - actual_headers - unless missing_headers.empty? - return {alert: "Missing required headers: #{missing_headers.join(', ')}"} - end + missing_headers = required_headers - actual_headers + return { alert: "Missing required headers: #{missing_headers.join(', ')}" } unless missing_headers.empty? - instructor_data[2..].each do |row| - # Extracting values and checking for nulls - id_number = row[actual_headers.index('anonimized ID')] - first_name = row[actual_headers.index('First Name')] - last_name = row[actual_headers.index('Last Name')] - middle_name = row[actual_headers.index('Middle Name')] - email = row[actual_headers.index('Email')] - before_9 = row[actual_headers.index('Teaching before 9:00 am.')] - after_3 = row[actual_headers.index('Teaching after 3:00 pm.')] - beaware_of = row[actual_headers.index('Is there anything else we should be aware of regarding your teaching load (special course reduction, ...)')] + instructor_data[2..].each do |row| + # Extracting values and checking for nulls + id_number = row[actual_headers.index('anonimized ID')] + first_name = row[actual_headers.index('First Name')] + last_name = row[actual_headers.index('Last Name')] + middle_name = row[actual_headers.index('Middle Name')] + email = row[actual_headers.index('Email')] + before_9 = row[actual_headers.index('Teaching before 9:00 am.')] + after_3 = row[actual_headers.index('Teaching after 3:00 pm.')] + beaware_of = row[actual_headers.index('Is there anything else we should be aware of regarding your teaching load (special course reduction, ...)')] - instructor_data = { - schedule_id: schedule_id, - id_number: id_number, - first_name: first_name, - last_name: last_name, - middle_name: middle_name, - email: email, - before_9: before_9, - after_3: after_3, - beaware_of: beaware_of - } + instructor_data = { + schedule_id: schedule_id, + id_number: id_number, + first_name: first_name, + last_name: last_name, + middle_name: middle_name, + email: email, + before_9: before_9, + after_3: after_3, + beaware_of: beaware_of + } - Instructor.create(instructor_data) - end + Instructor.create(instructor_data) end - return {notice: 'Instructors successfully uploaded.'} - rescue StandardError => e - return {alert: "There was an error uploading the CSV file: #{e.message}"} end + { notice: 'Instructors successfully uploaded.' } + rescue StandardError => e + { alert: "There was an error uploading the CSV file: #{e.message}" } end - end diff --git a/db/migrate/20241027021034_create_room_bookings.rb b/db/migrate/20241027021034_create_room_bookings.rb index dc6b598..5e5164c 100644 --- a/db/migrate/20241027021034_create_room_bookings.rb +++ b/db/migrate/20241027021034_create_room_bookings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateRoomBookings < ActiveRecord::Migration[7.2] def change create_table :room_bookings do |t| diff --git a/db/schema.rb b/db/schema.rb index 8b3e384..1314eff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -10,100 +12,100 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_10_27_021034) do - create_table "courses", force: :cascade do |t| - t.string "course_number" - t.integer "max_seats" - t.string "lecture_type" - t.integer "num_labs" - t.integer "schedule_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["schedule_id"], name: "index_courses_on_schedule_id" +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' + t.string 'lecture_type' + t.integer 'num_labs' + t.integer 'schedule_id', null: false + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + 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" - t.string "first_name" - t.string "middle_name" - t.string "email" - t.boolean "before_9" - t.boolean "after_3" - t.text "beaware_of" - t.integer "schedule_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["schedule_id"], name: "index_instructors_on_schedule_id" + create_table 'instructors', force: :cascade do |t| + t.integer 'id_number' + t.string 'last_name' + t.string 'first_name' + t.string 'middle_name' + t.string 'email' + t.boolean 'before_9' + t.boolean 'after_3' + t.text 'beaware_of' + t.integer 'schedule_id' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + 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" + 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" - t.boolean "is_learning_studio" - t.boolean "is_lab" - t.string "building_code" - t.string "room_number" - t.integer "capacity" - t.boolean "is_active" - t.string "comments" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "schedule_id", default: -1, null: false - t.index ["schedule_id"], name: "index_rooms_on_schedule_id" + create_table 'rooms', force: :cascade do |t| + t.integer 'campus' + t.boolean 'is_lecture_hall' + t.boolean 'is_learning_studio' + t.boolean 'is_lab' + t.string 'building_code' + t.string 'room_number' + t.integer 'capacity' + t.boolean 'is_active' + t.string 'comments' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.integer 'schedule_id', default: -1, null: false + t.index ['schedule_id'], name: 'index_rooms_on_schedule_id' end - create_table "schedules", force: :cascade do |t| - t.string "schedule_name" - t.string "semester_name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + create_table 'schedules', force: :cascade do |t| + t.string 'schedule_name' + t.string 'semester_name' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false end - create_table "sections", force: :cascade do |t| - t.string "section_number" - t.integer "seats_alloted" - t.integer "course_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["course_id"], name: "index_sections_on_course_id" + create_table 'sections', force: :cascade do |t| + t.string 'section_number' + t.integer 'seats_alloted' + t.integer 'course_id', null: false + 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" - t.string "end_time" - t.string "slot_type" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + create_table 'time_slots', force: :cascade do |t| + t.string 'day' + t.string 'start_time' + t.string 'end_time' + t.string 'slot_type' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false end - create_table "users", force: :cascade do |t| - t.string "email" - t.string "first_name" - t.string "last_name" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "uid" - t.string "provider" - t.index ["email"], name: "index_users_on_email", unique: true + create_table 'users', force: :cascade do |t| + t.string 'email' + t.string 'first_name' + t.string 'last_name' + t.datetime 'created_at', null: false + t.datetime 'updated_at', null: false + t.string 'uid' + t.string 'provider' + 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" + 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/spec/services/csv_handler_spec.rb b/spec/services/csv_handler_spec.rb index 1a1b1fd..7250da7 100644 --- a/spec/services/csv_handler_spec.rb +++ b/spec/services/csv_handler_spec.rb @@ -1,72 +1,74 @@ -# spec/services/csv_handler_spec.rb -require 'rails_helper' - -RSpec.describe CsvHandler do - let(:schedule) { create(:schedule) } - let(:valid_room_csv) { File.read(Rails.root.join('spec', 'fixtures', 'rooms', 'rooms_valid.csv')) } - let(:invalid_room_csv) { File.read(Rails.root.join('spec', 'fixtures', 'rooms', 'rooms_invalid.csv')) } - let(:valid_instructor_csv) { File.read(Rails.root.join('spec', 'fixtures', 'instructors', 'instructors_valid.csv')) } - let(:invalid_instructor_csv) { File.read(Rails.root.join('spec', 'fixtures', 'instructors', 'instructors_missing_headers.csv')) } - - describe '#initialize' do - it 'initializes with a file' do - handler = CsvHandler.new - handler.upload(valid_room_csv) - expect(handler.instance_variable_get(:@file)).to eq(valid_room_csv) - end - end - - describe '#parse_room_csv' do - context 'with valid data' do - it 'creates room records' do - handler = CsvHandler.new - handler.upload(StringIO.new(valid_room_csv)) - handler.parse_room_csv(schedule.id) - expect(Room.count).to eq(10) - end - - it 'returns a success message' do - handler = CsvHandler.new - handler.upload(StringIO.new(valid_room_csv)) - result = handler.parse_room_csv(schedule.id) - expect(result[:notice]).to eq('Rooms successfully uploaded.') - end - end - - context 'with invalid data' do - it 'prints an alert' do - handler = CsvHandler.new - handler.upload(StringIO.new(invalid_room_csv)) - result = handler.parse_room_csv(schedule.id) - expect(result[:alert]).to include('There was an error uploading the CSV file') - end - end - end - - describe '#parse_instructor_csv' do - context 'with valid data' do - it 'creates instructor records' do - handler = CsvHandler.new - handler.upload(StringIO.new(valid_instructor_csv)) - handler.parse_instructor_csv(schedule.id) - expect(Instructor.count).to eq(4) - end - - it 'returns a success message' do - handler = CsvHandler.new - handler.upload(StringIO.new(valid_instructor_csv)) - result = handler.parse_instructor_csv(schedule.id) - expect(result[:notice]).to eq('Instructors successfully uploaded.') - end - end - - context 'with missing header data' do - it 'prints an alert' do - handler = CsvHandler.new - handler.upload(StringIO.new(invalid_instructor_csv)) - result = handler.parse_instructor_csv(schedule.id) - expect(result[:alert]).to include('Missing required headers') - end - end - end -end \ No newline at end of file +# frozen_string_literal: true + +# spec/services/csv_handler_spec.rb +require 'rails_helper' + +RSpec.describe CsvHandler do + let(:schedule) { create(:schedule) } + let(:valid_room_csv) { File.read(Rails.root.join('spec', 'fixtures', 'rooms', 'rooms_valid.csv')) } + let(:invalid_room_csv) { File.read(Rails.root.join('spec', 'fixtures', 'rooms', 'rooms_invalid.csv')) } + let(:valid_instructor_csv) { File.read(Rails.root.join('spec', 'fixtures', 'instructors', 'instructors_valid.csv')) } + let(:invalid_instructor_csv) { File.read(Rails.root.join('spec', 'fixtures', 'instructors', 'instructors_missing_headers.csv')) } + + describe '#initialize' do + it 'initializes with a file' do + handler = CsvHandler.new + handler.upload(valid_room_csv) + expect(handler.instance_variable_get(:@file)).to eq(valid_room_csv) + end + end + + describe '#parse_room_csv' do + context 'with valid data' do + it 'creates room records' do + handler = CsvHandler.new + handler.upload(StringIO.new(valid_room_csv)) + handler.parse_room_csv(schedule.id) + expect(Room.count).to eq(10) + end + + it 'returns a success message' do + handler = CsvHandler.new + handler.upload(StringIO.new(valid_room_csv)) + result = handler.parse_room_csv(schedule.id) + expect(result[:notice]).to eq('Rooms successfully uploaded.') + end + end + + context 'with invalid data' do + it 'prints an alert' do + handler = CsvHandler.new + handler.upload(StringIO.new(invalid_room_csv)) + result = handler.parse_room_csv(schedule.id) + expect(result[:alert]).to include('There was an error uploading the CSV file') + end + end + end + + describe '#parse_instructor_csv' do + context 'with valid data' do + it 'creates instructor records' do + handler = CsvHandler.new + handler.upload(StringIO.new(valid_instructor_csv)) + handler.parse_instructor_csv(schedule.id) + expect(Instructor.count).to eq(4) + end + + it 'returns a success message' do + handler = CsvHandler.new + handler.upload(StringIO.new(valid_instructor_csv)) + result = handler.parse_instructor_csv(schedule.id) + expect(result[:notice]).to eq('Instructors successfully uploaded.') + end + end + + context 'with missing header data' do + it 'prints an alert' do + handler = CsvHandler.new + handler.upload(StringIO.new(invalid_instructor_csv)) + result = handler.parse_instructor_csv(schedule.id) + expect(result[:alert]).to include('Missing required headers') + end + end + end +end diff --git a/test/controllers/room_bookings_controller_test.rb b/test/controllers/room_bookings_controller_test.rb index 38f4905..b579650 100644 --- a/test/controllers/room_bookings_controller_test.rb +++ b/test/controllers/room_bookings_controller_test.rb @@ -1,4 +1,6 @@ -require "test_helper" +# frozen_string_literal: true + +require 'test_helper' class RoomBookingsControllerTest < ActionDispatch::IntegrationTest # test "the truth" do diff --git a/test/factories/room_bookings.rb b/test/factories/room_bookings.rb index 465250c..5e0f740 100644 --- a/test/factories/room_bookings.rb +++ b/test/factories/room_bookings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :room_booking do room { nil } diff --git a/test/models/room_booking_test.rb b/test/models/room_booking_test.rb index 51e1a97..cb1a846 100644 --- a/test/models/room_booking_test.rb +++ b/test/models/room_booking_test.rb @@ -1,4 +1,6 @@ -require "test_helper" +# frozen_string_literal: true + +require 'test_helper' class RoomBookingTest < ActiveSupport::TestCase # test "the truth" do From 0afff1350e3b9cbbd82f19304022cee1c11621fd Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 15:49:13 -0500 Subject: [PATCH 08/12] Added correct coverage --- config/routes.rb | 1 - db/schema.rb | 12 ++---------- spec/controllers/courses_controller_spec.rb | 20 +++++++++----------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index e05e51b..c5aa2a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -34,7 +34,6 @@ resources :instructors, only: [:index] post :upload_instructors, on: :member - resources :courses, only: [:index] post :upload_courses, on: :member diff --git a/db/schema.rb b/db/schema.rb index 0197920..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' @@ -81,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' @@ -108,12 +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/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 From 2aa9f1b055c128f9e442f4dbe0b4cf46cae58b29 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 16:46:27 -0500 Subject: [PATCH 09/12] Rspec Tests Added --- .../room_bookings_controller_spec.rb | 49 +++++++++++++++++++ spec/controllers/schedules_controller_spec.rb | 34 +++++++++++++ spec/factories.rb | 14 ++++++ test/factories/room_bookings.rb | 12 ++--- 4 files changed, 103 insertions(+), 6 deletions(-) create mode 100644 spec/controllers/room_bookings_controller_spec.rb diff --git a/spec/controllers/room_bookings_controller_spec.rb b/spec/controllers/room_bookings_controller_spec.rb new file mode 100644 index 0000000..154fc08 --- /dev/null +++ b/spec/controllers/room_bookings_controller_spec.rb @@ -0,0 +1,49 @@ +# 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..9b2cd13 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/factories/room_bookings.rb b/test/factories/room_bookings.rb index 5e0f740..5ea3109 100644 --- a/test/factories/room_bookings.rb +++ b/test/factories/room_bookings.rb @@ -1,10 +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 + # factory :room_booking do + # room { nil } + # time_slot { nil } + # is_available { false } + # is_lab { false } + # end end From 8fe7320602063155128ca0311b3933fba9d17735 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 18:38:25 -0500 Subject: [PATCH 10/12] Cucumber Added --- app/views/room_bookings/index.html.erb | 16 ++-- features/room_bookings.feature | 44 ++++++++++ .../step_definitions/room_bookings_steps.rb | 22 +++++ .../room_bookings_controller_spec.rb | 86 ++++++++++--------- spec/factories.rb | 8 +- 5 files changed, 125 insertions(+), 51 deletions(-) create mode 100644 features/room_bookings.feature create mode 100644 features/step_definitions/room_bookings_steps.rb diff --git a/app/views/room_bookings/index.html.erb b/app/views/room_bookings/index.html.erb index 3459aa0..7b7b254 100644 --- a/app/views/room_bookings/index.html.erb +++ b/app/views/room_bookings/index.html.erb @@ -6,13 +6,16 @@
- <%= link_to 'View Data', schedule_rooms_path(@schedule.id), class: 'col btn btn-secondary px-4 mx-1' %> - + <%= link_to 'View Data', schedule_rooms_path(@schedule.id), class: 'col-5 btn btn-secondary px-4 mx-1' %> + <% if !@rooms.empty? %> + + <% end %>
+<% if !@rooms.empty? %>
-
\ No newline at end of file + +<% else %> +

No rooms added to this schedule, click on View Data to Add Rooms!

+<% end %> \ No newline at end of file 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/room_bookings_controller_spec.rb b/spec/controllers/room_bookings_controller_spec.rb index 154fc08..cec0ca3 100644 --- a/spec/controllers/room_bookings_controller_spec.rb +++ b/spec/controllers/room_bookings_controller_spec.rb @@ -1,49 +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 + 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 - 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 + it 'renders the index template' do + expect(response).to render_template(:index) + end end + end end diff --git a/spec/factories.rb b/spec/factories.rb index 9b2cd13..60d7914 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -38,9 +38,9 @@ end factory :time_slot do - day { "Monday" } - start_time { "09:00" } - end_time { "10:00" } - slot_type { "Lecture" } + day { 'Monday' } + start_time { '09:00' } + end_time { '10:00' } + slot_type { 'Lecture' } end end From 5715ccb691dd678cff6a136dec4cbf0088f79c19 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 18:42:19 -0500 Subject: [PATCH 11/12] Autocorrection Added --- .rubocop.yml | 2 ++ app/controllers/courses_controller.rb | 1 + app/controllers/room_bookings_controller.rb | 1 + app/controllers/time_slots_controller.rb | 1 + app/helpers/courses_helper.rb | 1 + app/helpers/room_bookings_helper.rb | 1 + app/helpers/time_slots_helper.rb | 1 + 7 files changed, 8 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 5f6a612..f9fac95 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,3 +10,5 @@ Metrics/ClassLength: Max: 250 AllCops: NewCops: enable +Documentation: + Enabled: false \ No newline at end of file diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index 0b9facf..70b91ea 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# Courses Controller class CoursesController < ApplicationController helper_method :sort_column, :sort_direction before_action :set_schedule, only: [:index] diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index dd0b275..af6c2e1 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# Room Bookings Controller class RoomBookingsController < ApplicationController def index schedule_id = params[:schedule_id] diff --git a/app/controllers/time_slots_controller.rb b/app/controllers/time_slots_controller.rb index 5f19249..14d49db 100644 --- a/app/controllers/time_slots_controller.rb +++ b/app/controllers/time_slots_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# Time Slot Controller class TimeSlotsController < ApplicationController def filter @schedule = Schedule.find(params[:schedule_id]) diff --git a/app/helpers/courses_helper.rb b/app/helpers/courses_helper.rb index 46830e8..6fe3c67 100644 --- a/app/helpers/courses_helper.rb +++ b/app/helpers/courses_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# Courses Helper module CoursesHelper end diff --git a/app/helpers/room_bookings_helper.rb b/app/helpers/room_bookings_helper.rb index 781d7ce..75f8f52 100644 --- a/app/helpers/room_bookings_helper.rb +++ b/app/helpers/room_bookings_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# Room Bookings Helper module RoomBookingsHelper end diff --git a/app/helpers/time_slots_helper.rb b/app/helpers/time_slots_helper.rb index 7e0ff7b..c213472 100644 --- a/app/helpers/time_slots_helper.rb +++ b/app/helpers/time_slots_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# Time Slot Helper module TimeSlotsHelper end From 662964733001de85c49b22aa053f572987573e31 Mon Sep 17 00:00:00 2001 From: Wahib Kapdi Date: Tue, 29 Oct 2024 19:51:29 -0500 Subject: [PATCH 12/12] Sorting Added to time slots --- app/controllers/room_bookings_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/room_bookings_controller.rb b/app/controllers/room_bookings_controller.rb index af6c2e1..2fadc82 100644 --- a/app/controllers/room_bookings_controller.rb +++ b/app/controllers/room_bookings_controller.rb @@ -8,7 +8,8 @@ def index @rooms = @schedule.rooms.where(is_active: true).where.not(building_code: 'ONLINE') @tabs = TimeSlot.distinct.pluck(:day) @active_tab = params[:active_tab] || @tabs[0] - @time_slots = TimeSlot.where(time_slots: { day: @active_tab }) + @time_slots = TimeSlot.where(time_slots: { day: @active_tab }).to_a + @time_slots.sort_by! { |ts| Time.parse(ts.start_time) } # Fetch room bookings only for the specified schedule @room_bookings = RoomBooking.joins(:room, :time_slot)