lib/actors/teacher_timetable_slot_transformer.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'celluloid'
require 'actors/etl_producer'
require 'actors/etl_consumer'
require 'sirius/time_converter'
require 'roles/planned_timetable_slot'
require 'day'
require 'sirius/faculty_semester_weeks_generator'

# A convertor which receives TeacherTimetableSlots loaded from KOSapi and plans them into
# Events according to semester parameters and semester periods.
class TeacherTimetableSlotTransformer
  include Celluloid
  include ETLProducer
  include ETLConsumer

  def initialize(input, output, semester)
    self.input = input
    self.output = output
    @semester = semester
    @semester_weeks_dates = Sirius::FacultySemesterWeeksGenerator.generate_semester_weeks_dates(@semester)
    @events = nil
  end

  # @param row [Array(KOSapiClient::Entity::TeacherTimetableSlot, String)] teacher timetable slot
  #   together with related teacher username
  # @return [Array<Event>] planned events
  def process_row(row)
    slot, teacher = *row
    plan_events(slot, teacher)
  end

  # @return [Array<Event>] generated events that were not yet synced with the database
  def generate_row
    if processed_row
      pop_processed_row
    else
      raise EndOfData
    end
  end

  # @param slot [KOSapiClient::Entity::TeacherTimetableSlot] teacher timetable slot loaded from KOSapi
  # @param teacher [String] teacher username for the timetable slot
  # @return [Array<Event>] planned events
  def plan_events(slot, teacher)
    periods_query = @semester.semester_periods_dataset
      .where(type: ['teaching', 'exams'])
      .order(:starts_at)
    periods = periods_query.map { |p| PlannedSemesterPeriod.new(p) }
    time_converter = Sirius::TimeConverter.new(
      hour_starts: @semester.hour_starts,
      hour_length: @semester.hour_duration
    )
    # in case the duration is not set, use default duration of 2 hours
    slot.duration = 2 if !slot.duration && !slot.end_time
    slot.parity = slot.parity.to_s

    unless slot.weeks.blank?
      slot.weeks = parse_weeks(slot.weeks)
      slot.parity = nil
    end

    events = periods.flat_map do |period|
      PlannedTimetableSlot.new(slot, time_converter).generate_events(@semester, period, @semester_weeks_dates)
    end

    events.each_with_index do |e, i|
      e.event_type = 'teacher_timetable_slot'
      e.source_type = 'teacher_timetable_slot'
      e.source_id = slot.id
      e.teacher_ids = [teacher]
      e.student_ids = []
      e.absolute_sequence_number = i + 1
      e.name = Sequel.hstore({cs: slot.title})
    end

    events
  end

  private

  # FIXME: Copy-pasted from the ConvertTTS interactor.
  def parse_weeks(weeks)
    weeks.split(',').flat_map { |interval|
      if interval.include?('-')
        from, to = interval.split('-', 2)
        (Integer(from)..Integer(to)).to_a
      else
        Integer(interval)
      end
    }.sort.uniq
  end
end