Marking a course as complete automatically if all sub modules are marked as complete rails 5
Asked Answered
L

3

1

Edit #1

Here is what I have now

<% if course.complete? %>
  <%= link_to "Completed", course, class: "block text-lg w-full text-center text-white px-4 py-2 bg-green hover:bg-green-dark border-2 border-green-dark leading-none no-underline" %>
<% else %>
  <%= link_to "View Modules", course, class: "block text-lg w-full text-center text-grey-dark hover:text-darker px-4 py-2 border-2 border-grey leading-none no-underline hover:border-2 hover:border-grey-dark" %>
<% end %>

course_module.rb

class CourseModule < ApplicationRecord
  extend FriendlyId
  friendly_id :title, use: :slugged

  belongs_to :course
  has_many :course_exercises

  validates :title, :course_id, presence: true

  scope :completed, -> { where(complete: true) }

  after_save :course_completed

  private

  def course_completed
    course = course_module.course
    course.update(complete: true) if course.course_modules.all?(&:complete?)
  end
end

The names match so no issues there, however, how would I go about calling the model method?

Original Question

Currently, I have courses and within the course I have modules both have complete columns which are a boolean, currently, students can complete modules by clicking on the tick like so

image

I have the courses database like so

course

and the modules table like so

modules

But I want the functionality to work so when the last module is marked as complete, the course itself gets marked as complete in the database, currently in the course_modules_controller.rb I have

def complete
  @course_module = CourseModule.friendly.find(params[:id])
  @course_module.complete = true
  @course_module.save
  redirect_to courses_path, notice: 'Module completed, congratulations!'
end

This works for the modules, but I want to mark the course as complete if the last task has been marked as complete.

Lucia answered 22/8, 2018 at 15:2 Comment(0)
H
2

You can do this in your controller or in an after_save in your model, but it'll look something like this:

course = course_module.course
course.update(complete: true) if course.course_modules.all?(&:complete?)

May require minor edits if the names don't quite match your schema.

Hallo answered 22/8, 2018 at 15:7 Comment(3)
It would be better to use a database query instead of all? but the direction is right.Massarelli
See edit #1 of the original question, just to make sure I'm doing this correctly :)Lucia
@B.J.B you changed the question drastically with this "update"Bard
B
1

Seems this logic would belong in the CourseModule not the Course since the CourseModule being completed is the trigger.

Something akin to

class Course < ApplicationRecord 

  def complete!
    update_attribute(:complete,true) unless complete?
  end
end 

class CourseModule < ApplicationRecord
  scope :completed, -> {where(complete: true) }

  after_save :update_course, if: :complete?

  private
    def update_course
      course.complete! if course.course_modules.count == course.course_modules.completed.count
    end
end 

If a CourseModule can become incomplete after being completed then you will have to change a bit of the logic here.

Bard answered 22/8, 2018 at 15:48 Comment(0)
L
0

So the answer to this question is a combination of both @elliotcm and @engineersmnky's answers.

The final answer is this:

course.rb

class Course < ApplicationRecord
  has_many :course_modules

  def complete!
    update_attribute(:complete, true)
  end
end

course_module.rb

class CourseModule < ApplicationRecord
  belongs_to :course

  scope :completed, -> { where(complete: true) }
  scope :course_completed, -> { where(complete: true) }

  after_save :update_course, if: :complete?

  private

  def update_course
    course.complete! if course.course_modules.all?(&:complete?)
  end
end

Thank you both for your help in this!

Update

Above solution will run the callback on every save of course_module after it is complete. And will update the course.complete every time (if all its course modules are complete) even if it was already marked complete. Avoid all that by modifying the callbacks a bit:

class CourseModule < ApplicationRecord
  after_save :update_course, if: -> { complete_changed? && complete? }

  private

  def update_course
    return if course.complete? || CourseModule.where(course_id: course_id, complete: false).exists?
    course.complete!
  end
end

Note: You also need to handle the case where a course_module get incompleted and so, its corresponding course also needs to be marked incomplete.

Lucia answered 22/8, 2018 at 15:57 Comment(9)
You have duplicate scopes with exact same criteria in model CourseModule.Keelboat
@BJB please see my edit. Hope it improves the code without adding any bugs. #NotTestedKeelboat
@JagdeepSingh why would you not just post an answer?Bard
Yeah here's something I didn't think about. The course and modules are being marked as completed for all users, is there a way to do this by the user id?Lucia
@B.J.B you offered no insight into Course -> User Relationships so we cannot assist with this without understanding that relationship as wellBard
@Bard gyazo.com/e9d28bed78599d34695e79e4c5e4a917 gyazo.com/c91de147481d1ad803f5a2c632bdc049 a course is able to has_many :users and a user has_many :coursesLucia
@B.J.B this should technically be a HABTM relationship in this case which will involve a join table. Also it means a Course can never be "complete" as completion is a state of the relationship not the Course itself. You will need to rethink your design a bit to make this work appropriatelyBard
@Bard So only the modules can be complete? Would it be worth creating a new question here to go about joining the table? Because I've never dealt with a HABTM relationship before.Lucia
So only the modules can be complete? No nothing can be "complete". Would it be worth creating a new question here to go about joining the table? Yes as this strays completely from the current question and to be honest the answers provided here will not really help you where you are going.Bard

© 2022 - 2024 — McMap. All rights reserved.