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.
This commit is contained in:
Joshua Coles 2023-10-01 19:44:20 +01:00
parent 070816f0a2
commit d9d182bc94
13 changed files with 153 additions and 36 deletions

View File

@ -77,3 +77,4 @@ gem "httparty"
gem "sidekiq"
gem "icalendar"
gem "administrate"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 %>
<tr style="<%= style %>">
<td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
<%= lecture.recording.nice_title %>
<%= lecture.title %>
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
<%= lecture.start_time.to_fs(:dmy) %>

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
class ChangeRecordingStartTimeType < ActiveRecord::Migration[7.0]
def change
change_column :recordings, :start_time, :datetime, using: 'start_time::timestamp'
end
end

6
db/schema.rb generated
View File

@ -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

View File

@ -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,
)