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