Strategy To Track Progress For Completed Nested Lessons
I have models for Curriculums, Chapters and Lessons, and would like to track Progress for the user.
Curriculum -> has_many :chapters
Chapter -> has_many :lessons, belongs_to :curriculum
Lesson -> belongs_to :chapter
Progress -> belongs_to :curriculum, belongs_to :chapter, belongs_to :lesson, belongs_to :user
The Curriculums, Chapters and Lessons models have an ordinal for sorting but it's relative to its parent. To calculate percentage complete for a Lesson I'd need an index relative to Curriculum, aka its grandparent.
So I was thinking of storing position in Lesson, setting this value by loops through all Chapters and Lessons for a Toolkit, but now have a couple of questions.
1) If a Lesson is added or removed, or ordinal for Lesson is changed, how should I update the position for all Lessons? For example avoid getting in infinite loop using after_save in Lesson to trigger updating on Curriculum.
2) To set Progress completion, is it better to do:
lesson_index = curriculum.lessons.index(lesson) progress.completion = (lesson_index.to_f / curriculum.lessons.count.to_f * 100).ceil
progress.completion = (lesson.position.to_f / curriculum.lessons.count.to_f * 100).ceil
# calculate and store completion inside each Lesson to avoid calculation progress.completion = lesson.completion
Note, a Lesson is changed rarely where as a Progress is saved a lot, so want to make sure it's performant to avoid unnecessary database updates.
Is there a pattern or strategy for maintaining a grandchild's position like this?
It seems to me that you could break lessons into another discrete model, which then gets a boolean for completion. That would solve your ordinal problem because the percentage completed of Curriculum would just be the total number of those discrete components completed, regardless of where they sit on the tree. The count for Curriculum could also be essentially static, computed only when you update something yourself; that would make the on-the-fly overall calculation less expensive.
I think this would work best in particular if lessons don't need to be completed in a linear fashion.
Thanks Sean, this helps a lot!
With this approach, I don't care about the overall position in the tree at all, only the number of completed Lessons divided by total number of Lessons.
This way Lessons being re-ordered won't affect the Progress of existing users, the main factor for Progress is simplify the number of completed Lessons.
This is exactly what I was looking for and helps avoid a lot of unnecessary complexity. 🙏