Yimaru-BackEnd/internal/domain/practice.go
Yared Yemane 71bc09a638 Make practice title optional on create.
POST /practices and exam-prep practice create accept missing or null title; persist as empty string. Refresh OpenAPI and document the behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 04:11:09 -07:00

85 lines
3.6 KiB
Go

package domain
import (
"strings"
"time"
)
// ParentKind identifies which hierarchy entity owns a practice (exactly one).
type ParentKind string
const (
ParentKindCourse ParentKind = "COURSE"
ParentKindModule ParentKind = "MODULE"
ParentKindLesson ParentKind = "LESSON"
)
// PracticePublishStatus controls learner visibility for a practice shell (independent of question_set.status).
type PracticePublishStatus string
const (
PracticePublishDraft PracticePublishStatus = "DRAFT"
PracticePublishPublished PracticePublishStatus = "PUBLISHED"
)
// ParsePracticePublishStatusInput maps API input. Empty or unknown values default to PUBLISHED for backward compatibility.
func ParsePracticePublishStatusInput(raw string) PracticePublishStatus {
switch strings.TrimSpace(strings.ToUpper(raw)) {
case string(PracticePublishDraft):
return PracticePublishDraft
case string(PracticePublishPublished):
return PracticePublishPublished
default:
return PracticePublishPublished
}
}
// PracticePublishStatusFromDB maps persisted values into the domain type.
func PracticePublishStatusFromDB(raw string) PracticePublishStatus {
return ParsePracticePublishStatusInput(raw)
}
// Practice is question-set content (story, persona, tips) scoped to a course, module, or lesson.
type Practice struct {
ID int64 `json:"id"`
ParentKind ParentKind `json:"parent_kind"`
ParentID int64 `json:"parent_id"`
Title string `json:"title"`
StoryDescription *string `json:"story_description,omitempty"`
StoryImage *string `json:"story_image,omitempty"`
PersonaID *int64 `json:"persona_id,omitempty"`
QuestionSetID int64 `json:"question_set_id"`
PublishStatus PracticePublishStatus `json:"publish_status"`
QuickTips *string `json:"quick_tips,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
// VisibleToLearners is true when the practice shell should appear in subscribed learner catalogs and progression.
func (p Practice) VisibleToLearners() bool {
return p.PublishStatus == PracticePublishPublished
}
type CreatePracticeInput struct {
ParentKind ParentKind `json:"parent_kind" validate:"required,oneof=COURSE MODULE LESSON"`
ParentID int64 `json:"parent_id" validate:"required,gt=0"`
Title *string `json:"title,omitempty"`
StoryDescription *string `json:"story_description,omitempty"`
StoryImage *string `json:"story_image,omitempty"`
PersonaID *int64 `json:"persona_id,omitempty"`
QuestionSetID int64 `json:"question_set_id" validate:"required,gt=0"`
QuickTips *string `json:"quick_tips,omitempty"`
// Omit or empty for backward compatibility defaults to PUBLISHED; set DRAFT to save hidden from learners until published.
PublishStatus string `json:"publish_status,omitempty" validate:"omitempty,oneof=DRAFT draft PUBLISHED published"`
}
type UpdatePracticeInput struct {
Title *string `json:"title,omitempty"`
StoryDescription *string `json:"story_description,omitempty"`
StoryImage *string `json:"story_image,omitempty"`
PersonaID *int64 `json:"persona_id,omitempty"`
QuestionSetID *int64 `json:"question_set_id,omitempty"`
QuickTips *string `json:"quick_tips,omitempty"`
PublishStatus *string `json:"publish_status,omitempty" validate:"omitempty,oneof=DRAFT draft PUBLISHED published"`
}