Include access metadata for OPEN_LEARNER with is_accessible always true
Keeps the same response shape as STUDENT while skipping sequential locks; progress fields are still populated for completion UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
79851d31b3
commit
0ad7f094cf
|
|
@ -1,7 +1,8 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
// LMSEntityAccess describes learner gating for a program, course, module, or lesson.
|
// LMSEntityAccess describes learner gating for a program, course, module, or lesson.
|
||||||
// It is omitted (nil) for non-learner roles in API responses.
|
// Included for STUDENT and OPEN_LEARNER; omitted (nil) for staff roles in API responses.
|
||||||
|
// OPEN_LEARNER always has is_accessible true; STUDENT may be false when prerequisites are unmet.
|
||||||
// Progress fields count completed lessons vs total lessons in that entity’s scope (lesson: 0 or 1 of 1).
|
// Progress fields count completed lessons vs total lessons in that entity’s scope (lesson: 0 or 1 of 1).
|
||||||
type LMSEntityAccess struct {
|
type LMSEntityAccess struct {
|
||||||
IsAccessible bool `json:"is_accessible"`
|
IsAccessible bool `json:"is_accessible"`
|
||||||
|
|
|
||||||
|
|
@ -144,16 +144,13 @@ func (s *Service) CanAccessLesson(ctx context.Context, userID, lessonID int64) (
|
||||||
return true, "", nil
|
return true, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyAccessProgram sets p.Access for a learner. Non-learners: clears Access to omit from JSON.
|
// ApplyAccessProgram sets p.Access for learner roles. Staff roles omit Access from JSON.
|
||||||
|
// STUDENT: is_accessible reflects sequential prerequisites; OPEN_LEARNER: always true.
|
||||||
func (s *Service) ApplyAccessProgram(ctx context.Context, role domain.Role, userID int64, p *domain.Program) error {
|
func (s *Service) ApplyAccessProgram(ctx context.Context, role domain.Role, userID int64, p *domain.Program) error {
|
||||||
if !role.UsesLMSSequentialGating() {
|
if !role.IsCustomerLearnerRole() {
|
||||||
p.Access = nil
|
p.Access = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ok, reason, err := s.CanAccessProgram(ctx, userID, p.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
done, err := s.store.LmsUserHasProgramProgress(ctx, userID, p.ID)
|
done, err := s.store.LmsUserHasProgramProgress(ctx, userID, p.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -162,24 +159,23 @@ func (s *Service) ApplyAccessProgram(ctx context.Context, role domain.Role, user
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c, t, pct := lmsProgressCounts(comp, tot, done)
|
ok, reason := true, ""
|
||||||
p.Access = &domain.LMSEntityAccess{
|
if role.UsesLMSSequentialGating() {
|
||||||
IsAccessible: ok, IsCompleted: done, Reason: reasonIf(ok, reason),
|
ok, reason, err = s.CanAccessProgram(ctx, userID, p.ID)
|
||||||
CompletedCount: c, TotalCount: t, ProgressPercent: pct,
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
p.Access = buildLMSEntityAccess(ok, reason, done, comp, tot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyAccessCourse sets c.Access for a learner.
|
// ApplyAccessCourse sets c.Access for learner roles.
|
||||||
func (s *Service) ApplyAccessCourse(ctx context.Context, role domain.Role, userID int64, c *domain.Course) error {
|
func (s *Service) ApplyAccessCourse(ctx context.Context, role domain.Role, userID int64, c *domain.Course) error {
|
||||||
if !role.UsesLMSSequentialGating() {
|
if !role.IsCustomerLearnerRole() {
|
||||||
c.Access = nil
|
c.Access = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ok, reason, err := s.CanAccessCourse(ctx, userID, c.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
done, err := s.store.LmsUserHasCourseProgress(ctx, userID, c.ID)
|
done, err := s.store.LmsUserHasCourseProgress(ctx, userID, c.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -188,24 +184,23 @@ func (s *Service) ApplyAccessCourse(ctx context.Context, role domain.Role, userI
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cc, tt, pct := lmsProgressCounts(comp, tot, done)
|
ok, reason := true, ""
|
||||||
c.Access = &domain.LMSEntityAccess{
|
if role.UsesLMSSequentialGating() {
|
||||||
IsAccessible: ok, IsCompleted: done, Reason: reasonIf(ok, reason),
|
ok, reason, err = s.CanAccessCourse(ctx, userID, c.ID)
|
||||||
CompletedCount: cc, TotalCount: tt, ProgressPercent: pct,
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
c.Access = buildLMSEntityAccess(ok, reason, done, comp, tot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyAccessModule sets m.Access for a learner.
|
// ApplyAccessModule sets m.Access for learner roles.
|
||||||
func (s *Service) ApplyAccessModule(ctx context.Context, role domain.Role, userID int64, m *domain.Module) error {
|
func (s *Service) ApplyAccessModule(ctx context.Context, role domain.Role, userID int64, m *domain.Module) error {
|
||||||
if !role.UsesLMSSequentialGating() {
|
if !role.IsCustomerLearnerRole() {
|
||||||
m.Access = nil
|
m.Access = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ok, reason, err := s.CanAccessModule(ctx, userID, m.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
done, err := s.store.LmsUserHasModuleProgress(ctx, userID, m.ID)
|
done, err := s.store.LmsUserHasModuleProgress(ctx, userID, m.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -214,24 +209,23 @@ func (s *Service) ApplyAccessModule(ctx context.Context, role domain.Role, userI
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cc, tt, pct := lmsProgressCounts(comp, tot, done)
|
ok, reason := true, ""
|
||||||
m.Access = &domain.LMSEntityAccess{
|
if role.UsesLMSSequentialGating() {
|
||||||
IsAccessible: ok, IsCompleted: done, Reason: reasonIf(ok, reason),
|
ok, reason, err = s.CanAccessModule(ctx, userID, m.ID)
|
||||||
CompletedCount: cc, TotalCount: tt, ProgressPercent: pct,
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
m.Access = buildLMSEntityAccess(ok, reason, done, comp, tot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyAccessLesson sets l.Access for a learner.
|
// ApplyAccessLesson sets l.Access for learner roles.
|
||||||
func (s *Service) ApplyAccessLesson(ctx context.Context, role domain.Role, userID int64, les *domain.Lesson) error {
|
func (s *Service) ApplyAccessLesson(ctx context.Context, role domain.Role, userID int64, les *domain.Lesson) error {
|
||||||
if !role.UsesLMSSequentialGating() {
|
if !role.IsCustomerLearnerRole() {
|
||||||
les.Access = nil
|
les.Access = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ok, reason, err := s.CanAccessLesson(ctx, userID, les.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
done, err := s.store.LmsUserHasLessonProgress(ctx, userID, les.ID)
|
done, err := s.store.LmsUserHasLessonProgress(ctx, userID, les.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -242,14 +236,29 @@ func (s *Service) ApplyAccessLesson(ctx context.Context, role domain.Role, userI
|
||||||
} else {
|
} else {
|
||||||
comp, tot = 0, 1
|
comp, tot = 0, 1
|
||||||
}
|
}
|
||||||
c, t, pct := lmsProgressCounts(comp, tot, done)
|
ok, reason := true, ""
|
||||||
les.Access = &domain.LMSEntityAccess{
|
if role.UsesLMSSequentialGating() {
|
||||||
IsAccessible: ok, IsCompleted: done, Reason: reasonIf(ok, reason),
|
ok, reason, err = s.CanAccessLesson(ctx, userID, les.ID)
|
||||||
CompletedCount: c, TotalCount: t, ProgressPercent: pct,
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
les.Access = buildLMSEntityAccess(ok, reason, done, comp, tot)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildLMSEntityAccess(ok bool, reason string, done bool, completed, total int32) *domain.LMSEntityAccess {
|
||||||
|
c, t, pct := lmsProgressCounts(completed, total, done)
|
||||||
|
return &domain.LMSEntityAccess{
|
||||||
|
IsAccessible: ok,
|
||||||
|
IsCompleted: done,
|
||||||
|
Reason: reasonIf(ok, reason),
|
||||||
|
CompletedCount: c,
|
||||||
|
TotalCount: t,
|
||||||
|
ProgressPercent: pct,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// lmsProgressCounts maps DB lesson completion counts to UI fields. Percent is 0–100; completed
|
// lmsProgressCounts maps DB lesson completion counts to UI fields. Percent is 0–100; completed
|
||||||
// and total are aligned with isCompleted when the entity is fully done.
|
// and total are aligned with isCompleted when the entity is fully done.
|
||||||
func lmsProgressCounts(completed, total int32, isCompleted bool) (c, t, pct int) {
|
func lmsProgressCounts(completed, total int32, isCompleted bool) (c, t, pct int) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user