From d9d182bc946b29891b0c83ea544dfa3e3707e93f Mon Sep 17 00:00:00 2001 From: Joshua Coles Date: Sun, 1 Oct 2023 19:44:20 +0100 Subject: [PATCH] Update lecture properties and various migration changes Modified code to remove 'status' field from lecture's table and added 'event_uuid' field. Also refined the 'start_time' field in recordings' table to handle datetime instead of strings. Further, made minor changes in scraping jobs and seeding logic. Implemented these changes to allow lectures to be associated with calendar events and streamlined various fields. --- Gemfile | 1 + Gemfile.lock | 33 +++++++++++ app/dashboards/course_dashboard.rb | 5 +- app/dashboards/lecture_dashboard.rb | 14 ++--- app/jobs/scrape_calendar_job.rb | 56 ++++++++++++++++++- app/jobs/scrape_panopto_job.rb | 23 +++++--- app/models/lecture.rb | 9 --- app/views/attendance_tracker/index.html.erb | 2 +- config/routes.rb | 11 ++-- ...0231001180954_update_lecture_properties.rb | 8 +++ ...183712_change_recording_start_time_type.rb | 5 ++ db/schema.rb | 6 +- db/seeds.rb | 16 ++++++ 13 files changed, 153 insertions(+), 36 deletions(-) create mode 100644 db/migrate/20231001180954_update_lecture_properties.rb create mode 100644 db/migrate/20231001183712_change_recording_start_time_type.rb diff --git a/Gemfile b/Gemfile index 2aeddf2..482e533 100644 --- a/Gemfile +++ b/Gemfile @@ -77,3 +77,4 @@ gem "httparty" gem "sidekiq" gem "icalendar" +gem "administrate" diff --git a/Gemfile.lock b/Gemfile.lock index 9761cc4..64f7d59 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -68,6 +68,14 @@ GEM tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) + administrate (0.19.0) + actionpack (>= 5.0) + actionview (>= 5.0) + activerecord (>= 5.0) + jquery-rails (>= 4.0) + kaminari (>= 1.0) + sassc-rails (~> 2.1) + selectize-rails (~> 0.6) bindex (0.8.1) bootsnap (1.16.0) msgpack (~> 1.2) @@ -112,6 +120,22 @@ GEM jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) + jquery-rails (4.6.0) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + kaminari (1.2.2) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) + actionview + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) + activerecord + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) loofah (2.21.3) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -190,6 +214,13 @@ GEM rubyzip (2.3.2) sassc (2.4.0) ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + selectize-rails (0.12.6) selenium-webdriver (4.10.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -212,6 +243,7 @@ GEM tailwindcss-rails (2.0.30-arm64-darwin) railties (>= 6.0.0) thor (1.2.2) + tilt (2.3.0) timeout (0.4.0) turbo-rails (1.4.0) actionpack (>= 6.0.0) @@ -240,6 +272,7 @@ PLATFORMS arm64-darwin-22 DEPENDENCIES + administrate bootsnap capybara debug diff --git a/app/dashboards/course_dashboard.rb b/app/dashboards/course_dashboard.rb index cab4f24..b812a63 100644 --- a/app/dashboards/course_dashboard.rb +++ b/app/dashboards/course_dashboard.rb @@ -11,6 +11,7 @@ class CourseDashboard < Administrate::BaseDashboard id: Field::Number, lectures: Field::HasMany, panopto_folders: Field::String.with_options(searchable: false), + semester_start_date: Field::Date, title: Field::String, toggl_project: Field::Number, unit_code: Field::String, @@ -27,7 +28,7 @@ class CourseDashboard < Administrate::BaseDashboard id lectures panopto_folders - title + semester_start_date ].freeze # SHOW_PAGE_ATTRIBUTES @@ -36,6 +37,7 @@ class CourseDashboard < Administrate::BaseDashboard id lectures panopto_folders + semester_start_date title toggl_project unit_code @@ -49,6 +51,7 @@ class CourseDashboard < Administrate::BaseDashboard FORM_ATTRIBUTES = %i[ lectures panopto_folders + semester_start_date title toggl_project unit_code diff --git a/app/dashboards/lecture_dashboard.rb b/app/dashboards/lecture_dashboard.rb index c182d23..cda6db2 100644 --- a/app/dashboards/lecture_dashboard.rb +++ b/app/dashboards/lecture_dashboard.rb @@ -11,9 +11,9 @@ class LectureDashboard < Administrate::BaseDashboard id: Field::Number, attendance: Field::HasOne, course: Field::BelongsTo, - recording_id: Field::String, + event_uuid: Field::String, + recording: Field::BelongsTo, start_time: Field::DateTime, - status: Field::Select.with_options(searchable: false, collection: ->(field) { field.resource.class.send(field.attribute.to_s.pluralize).keys }), title: Field::String, created_at: Field::DateTime, updated_at: Field::DateTime, @@ -28,7 +28,7 @@ class LectureDashboard < Administrate::BaseDashboard id attendance course - recording_id + event_uuid ].freeze # SHOW_PAGE_ATTRIBUTES @@ -37,9 +37,9 @@ class LectureDashboard < Administrate::BaseDashboard id attendance course - recording_id + event_uuid + recording start_time - status title created_at updated_at @@ -51,9 +51,9 @@ class LectureDashboard < Administrate::BaseDashboard FORM_ATTRIBUTES = %i[ attendance course - recording_id + event_uuid + recording start_time - status title ].freeze diff --git a/app/jobs/scrape_calendar_job.rb b/app/jobs/scrape_calendar_job.rb index 229d32b..84ce2cf 100644 --- a/app/jobs/scrape_calendar_job.rb +++ b/app/jobs/scrape_calendar_job.rb @@ -1,21 +1,71 @@ class ScrapeCalendarJob < ApplicationJob queue_as :default + def clean_up_lecture_title(unit_code, short_lecture_title) end + def perform(*args) ics_file = HTTParty.get("https://mytimetable.bath.ac.uk/ical?6519757b&group=false&timetable=!MjAyMyFzdHVkZW50c2V0ITRDRjQ5MjlGRTg1M0Q4N0MyMDZENTVDNUQ3QTJFNzk0&eu=amMzMDkxQGJhdGguYWMudWs=&h=MiuDbRiudE_Yf7B25v2SfEuFCtmYGkFb5sAUI3yGmtY=") calendars = Icalendar::Calendar.parse(ics_file) calendar = calendars.first - calendar.events.each do |event| + unit_codes = Course.all.map(&:unit_code) + + events = calendar.events.map do |event| summary = event.summary match = summary.split('-') # Handle odd events we don't care about - return if match.length != 2 + next if match.length != 2 unit_code = match[0] short_lecture_title = match[1] - start_time = event.dtstart + start_time = event.dtstart.to_time + event_uuid = event.uid.to_s + + next if unit_code.nil? || + short_lecture_title.nil? || + event_uuid.nil? || + start_time.nil? || + !unit_code.in?(unit_codes) + + { + unit_code:, + short_lecture_title:, + start_time:, + event_uuid:, + } + end + + events + .compact + .filter { |attrs| attrs[:unit_code].in? unit_codes } + .group_by { |attrs| attrs[:unit_code] } + .map do |unit_code, course_events| + course = Course.find_by(unit_code: unit_code) + + lecture_counter = 0 + + course_events.each do |event| + # Naive check to see if we've already created this lecture + if course.lectures.find_by(event_uuid: event[:event_uuid]).present? || course.lectures.find_by( + start_time: (event[:start_time].beginning_of_hour + 5.minutes)..(event[:start_time].beginning_of_hour + 1.hour + 5.minutes) + ).present? + next + end + + if event[:short_lecture_title].starts_with? "Lec" + lecture_counter += 1 + title = "Lecture #{lecture_counter}" + else + title = event[:short_lecture_title] + end + + course.lectures.create!( + title: title, + start_time: event[:start_time], + event_uuid: event[:event_uuid], + ) + end end end end diff --git a/app/jobs/scrape_panopto_job.rb b/app/jobs/scrape_panopto_job.rb index a6d1270..62d911e 100644 --- a/app/jobs/scrape_panopto_job.rb +++ b/app/jobs/scrape_panopto_job.rb @@ -27,13 +27,22 @@ class ScrapePanoptoJob < ApplicationJob created.each do |new_recording_attrs| new_recording = Recording.new(new_recording_attrs) - Lecture.create!( - title: new_recording.nice_title, - start_time: new_recording.start_time, - course_id: new_recording.course_id, - status: :undetermined, - recording_id: new_recording.id - ) + + # Find existing lecture to associate with this recording + if (lecture = course.lectures.find_by( + start_time: new_recording.start_time.beginning_of_hour + 15.minutes, + recording_id: nil, + )) + lecture.update!(recording_id: new_recording.id) + else + # Else create a new lecture + Lecture.create!( + title: new_recording.nice_title, + start_time: new_recording.start_time, + course_id: new_recording.course_id, + recording_id: new_recording.id + ) + end end end end diff --git a/app/models/lecture.rb b/app/models/lecture.rb index ad8fb54..f5fa816 100644 --- a/app/models/lecture.rb +++ b/app/models/lecture.rb @@ -3,15 +3,6 @@ class Lecture < ApplicationRecord has_one :attendance belongs_to :recording, optional: true - enum :status, [ - :undetermined, - :future, - :happening_now, - :attended_in_person, - :watched_recording, - :missed, - ], default: :undetermined - def week_number ((start_time.beginning_of_week - course.semester_start_date.to_time) / 1.week).floor + 1 end diff --git a/app/views/attendance_tracker/index.html.erb b/app/views/attendance_tracker/index.html.erb index 14dec1b..497e672 100644 --- a/app/views/attendance_tracker/index.html.erb +++ b/app/views/attendance_tracker/index.html.erb @@ -28,7 +28,7 @@ <% style = if lecture.start_time.future? then 'background: repeating-linear-gradient(45deg, #f3f4f6, #f3f4f6 10px, white 10px, white 20px);' else '' end %> - <%= lecture.recording.nice_title %> + <%= lecture.title %> <%= lecture.start_time.to_fs(:dmy) %> diff --git a/config/routes.rb b/config/routes.rb index 9f3894f..ab1ab25 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,11 @@ Rails.application.routes.draw do namespace :admin do - resources :attendances - resources :courses - resources :lectures + resources :attendances + resources :courses + resources :lectures + + root to: "attendances#index" + end - root to: "attendances#index" - end root controller: :attendance_tracker, action: :index end diff --git a/db/migrate/20231001180954_update_lecture_properties.rb b/db/migrate/20231001180954_update_lecture_properties.rb new file mode 100644 index 0000000..f45ec23 --- /dev/null +++ b/db/migrate/20231001180954_update_lecture_properties.rb @@ -0,0 +1,8 @@ +class UpdateLectureProperties < ActiveRecord::Migration[7.0] + def change + remove_column :lectures, :status + + # Optional association with an event in the calendar + add_column :lectures, :event_uuid, :string, null: true + end +end diff --git a/db/migrate/20231001183712_change_recording_start_time_type.rb b/db/migrate/20231001183712_change_recording_start_time_type.rb new file mode 100644 index 0000000..319d2f6 --- /dev/null +++ b/db/migrate/20231001183712_change_recording_start_time_type.rb @@ -0,0 +1,5 @@ +class ChangeRecordingStartTimeType < ActiveRecord::Migration[7.0] + def change + change_column :recordings, :start_time, :datetime, using: 'start_time::timestamp' + end +end diff --git a/db/schema.rb b/db/schema.rb index 8014af4..5dfc295 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_10_01_165128) do +ActiveRecord::Schema[7.0].define(version: 2023_10_01_183712) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -36,18 +36,18 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_01_165128) do create_table "lectures", force: :cascade do |t| t.string "title", null: false t.datetime "start_time", null: false - t.integer "status", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.bigint "course_id" t.bigint "recording_id" + t.string "event_uuid" t.index ["course_id"], name: "index_lectures_on_course_id" t.index ["recording_id"], name: "index_lectures_on_recording_id" end create_table "recordings", force: :cascade do |t| t.string "title", null: false - t.string "start_time", null: false + t.datetime "start_time", null: false t.string "recording_uuid", null: false t.bigint "course_id", null: false t.datetime "created_at", null: false diff --git a/db/seeds.rb b/db/seeds.rb index bc25fce..4156dda 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,19 @@ # # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) # Character.create(name: "Luke", movie: movies.first) + +Course.create!( + title: 'General Relativity', + panopto_folders: ["5a77867a-236b-478f-9b0d-af2400ed0989"], + toggl_project: 189467492, + unit_code: 'PH30101', + semester_start_date: Date.parse('2023-02-06'), +) + +Course.create!( + title: 'Continuum mechanics', + panopto_folders: ["dca2c510-49b4-4a24-9c91-b07701022d03"], + toggl_project: 195482340, + unit_code: 'MA30253', + semester_start_date: START_OF_YEAR_5_SEMESTER_1.to_date, +)