mumuki/mumuki-domain

View on GitHub
app/models/guide.rb

Summary

Maintainability
A
0 mins
Test Coverage
class Guide < Content
  BASIC_RESOURCE_FIELDS = %i(
    authors beta collaborators corollary
    custom_expectations expectations extra id_format
    learn_more private settings sources teacher_info type)

  include WithStats,
          WithExpectations,
          WithLanguage,
          WithAssignmentsBatch

  markdown_on :corollary, :sources, :learn_more, :teacher_info

  numbered :exercises
  has_many :exercises, -> { order(number: :asc) }, dependent: :destroy

  serialize :settings, Hash

  self.inheritance_column = nil

  enum type: [:learning, :practice]

  alias_method :structural_children, :exercises

  def clear_progress!(user, organization=Organization.current)
    transaction do
      exercises.each do |exercise|
        exercise.find_assignment_for(user, organization)&.destroy!
      end
    end
  end

  def lesson
    usage_in_organization_of_type Lesson
  end

  def chapter
    lesson.try(:chapter) #FIXME temporary
  end

  def exercises_count
    exercises.count
  end

  def next_exercise(user)
    if user.present?
      user.next_exercise_at(self)
    else
      first_exercise
    end
  end

  def pending_exercises(user)
    Exercise.with_pending_assignments_for(user, exercises)
  end

  def first_exercise
    exercises.first
  end

  def search_tags
    exercises.flat_map(&:search_tags).uniq
  end

  def done_for?(user)
    stats_for(user).done?
  end

  # Finds an exercise by bibliotheca_id within this guide
  def locate_exercise!(bibliotheca_id)
    exercises.find_by!(bibliotheca_id: bibliotheca_id)
  end

  def import_from_resource_h!(resource_h)
    dirty_progress_if_structural_children_changed! do
      self.assign_attributes whitelist_attributes(resource_h)
      self.language = Language.for_name(resource_h.dig(:language, :name))
      self.save!

      resource_h[:exercises]&.each_with_index do |e, i|
        exercise = Exercise.find_by(guide_id: self.id, bibliotheca_id: e[:id])
        exercise_type = e[:type] || 'problem'

        exercise = exercise ?
            exercise.ensure_type!(exercise_type.as_module_name) :
            exercise_type.as_module.new(guide_id: self.id, bibliotheca_id: e[:id])

        exercise.import_from_resource_h! (i+1), e
      end

      new_ids = resource_h[:exercises].map { |it| it[:id] }
      self.exercises.where.not(bibliotheca_id: new_ids).destroy_all

      reload
    end
  end

  # Keep this list up to date with
  # Mumuki::Domain::Store::Github::GuideSchema
  def to_expanded_resource_h(options={})
    as_json(only: BASIC_RESOURCE_FIELDS)
      .symbolize_keys
      .merge(super)
      .merge(exercises: exercises.map { |it| it.to_resource_h(options) })
      .merge(language: language.to_embedded_resource_h)
      .tap { |it| it.markdownified!(:corollary, :description, :teacher_info) if options[:markdownified] }
  end

  def to_markdownified_resource_h
    to_resource_h(markdownified: true)
  end

  def as_lesson_of(topic)
    topic.lessons.find_by(guide_id: id) || Lesson.new(guide: self, topic: topic)
  end

  def as_complement_of(book) #FIXME duplication
    book.complements.find_by(guide_id: id) || Complement.new(guide: self, book: book)
  end

  def resettable?
    usage_in_organization.resettable?
  end

  ## Forking

  def fork_children_into!(dup, _organization, _syncer)
    dup.exercises = exercises.map(&:dup)
  end
end