819 lines
24 KiB
Go
819 lines
24 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
dbgen "Yimaru-Backend/gen/db"
|
|
"Yimaru-Backend/internal/domain"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
func toPgText(s *string) pgtype.Text {
|
|
if s == nil {
|
|
return pgtype.Text{Valid: false}
|
|
}
|
|
return pgtype.Text{String: *s, Valid: true}
|
|
}
|
|
|
|
func fromPgText(t pgtype.Text) *string {
|
|
if !t.Valid {
|
|
return nil
|
|
}
|
|
return &t.String
|
|
}
|
|
|
|
func fromPgInt4(i pgtype.Int4) *int32 {
|
|
if !i.Valid {
|
|
return nil
|
|
}
|
|
return &i.Int32
|
|
}
|
|
|
|
func fromPgInt8(i pgtype.Int8) *int64 {
|
|
if !i.Valid {
|
|
return nil
|
|
}
|
|
return &i.Int64
|
|
}
|
|
|
|
func toPgInt4(i *int32) pgtype.Int4 {
|
|
if i == nil {
|
|
return pgtype.Int4{Valid: false}
|
|
}
|
|
return pgtype.Int4{Int32: *i, Valid: true}
|
|
}
|
|
|
|
func toPgInt8(i *int64) pgtype.Int8 {
|
|
if i == nil {
|
|
return pgtype.Int8{Valid: false}
|
|
}
|
|
return pgtype.Int8{Int64: *i, Valid: true}
|
|
}
|
|
|
|
func timePtr(t pgtype.Timestamptz) *time.Time {
|
|
if !t.Valid {
|
|
return nil
|
|
}
|
|
return &t.Time
|
|
}
|
|
|
|
func questionToDomain(q dbgen.Question) domain.Question {
|
|
return domain.Question{
|
|
ID: q.ID,
|
|
QuestionText: q.QuestionText,
|
|
QuestionType: q.QuestionType,
|
|
DifficultyLevel: fromPgText(q.DifficultyLevel),
|
|
Points: q.Points,
|
|
Explanation: fromPgText(q.Explanation),
|
|
Tips: fromPgText(q.Tips),
|
|
VoicePrompt: fromPgText(q.VoicePrompt),
|
|
SampleAnswerVoicePrompt: fromPgText(q.SampleAnswerVoicePrompt),
|
|
ImageURL: fromPgText(q.ImageUrl),
|
|
Status: q.Status,
|
|
CreatedAt: q.CreatedAt.Time,
|
|
UpdatedAt: timePtr(q.UpdatedAt),
|
|
}
|
|
}
|
|
|
|
func questionOptionToDomain(o dbgen.QuestionOption) domain.QuestionOption {
|
|
return domain.QuestionOption{
|
|
ID: o.ID,
|
|
QuestionID: o.QuestionID,
|
|
OptionText: o.OptionText,
|
|
OptionOrder: o.OptionOrder,
|
|
IsCorrect: o.IsCorrect,
|
|
CreatedAt: o.CreatedAt.Time,
|
|
}
|
|
}
|
|
|
|
func questionShortAnswerToDomain(a dbgen.QuestionShortAnswer) domain.QuestionShortAnswer {
|
|
return domain.QuestionShortAnswer{
|
|
ID: a.ID,
|
|
QuestionID: a.QuestionID,
|
|
AcceptableAnswer: a.AcceptableAnswer,
|
|
MatchType: a.MatchType,
|
|
CreatedAt: a.CreatedAt.Time,
|
|
}
|
|
}
|
|
|
|
func questionAudioAnswerToDomain(a dbgen.QuestionAudioAnswer) domain.QuestionAudioAnswer {
|
|
return domain.QuestionAudioAnswer{
|
|
ID: a.ID,
|
|
QuestionID: a.QuestionID,
|
|
CorrectAnswerText: a.CorrectAnswerText,
|
|
CreatedAt: a.CreatedAt.Time,
|
|
}
|
|
}
|
|
|
|
func questionSetToDomain(qs dbgen.QuestionSet) domain.QuestionSet {
|
|
return domain.QuestionSet{
|
|
ID: qs.ID,
|
|
Title: qs.Title,
|
|
Description: fromPgText(qs.Description),
|
|
SetType: qs.SetType,
|
|
OwnerType: fromPgText(qs.OwnerType),
|
|
OwnerID: fromPgInt8(qs.OwnerID),
|
|
BannerImage: fromPgText(qs.BannerImage),
|
|
Persona: fromPgText(qs.Persona),
|
|
TimeLimitMinutes: fromPgInt4(qs.TimeLimitMinutes),
|
|
PassingScore: fromPgInt4(qs.PassingScore),
|
|
ShuffleQuestions: qs.ShuffleQuestions,
|
|
Status: qs.Status,
|
|
SubCourseVideoID: fromPgInt8(qs.SubCourseVideoID),
|
|
CreatedAt: qs.CreatedAt.Time,
|
|
UpdatedAt: timePtr(qs.UpdatedAt),
|
|
}
|
|
}
|
|
|
|
func questionSetItemToDomain(i dbgen.QuestionSetItem) domain.QuestionSetItem {
|
|
return domain.QuestionSetItem{
|
|
ID: i.ID,
|
|
SetID: i.SetID,
|
|
QuestionID: i.QuestionID,
|
|
DisplayOrder: i.DisplayOrder,
|
|
CreatedAt: i.CreatedAt.Time,
|
|
}
|
|
}
|
|
|
|
func (s *Store) CreateQuestion(ctx context.Context, input domain.CreateQuestionInput) (domain.Question, error) {
|
|
q, tx, err := s.BeginTx(ctx)
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
var points interface{}
|
|
if input.Points != nil {
|
|
points = *input.Points
|
|
}
|
|
var status interface{}
|
|
if input.Status != nil {
|
|
status = *input.Status
|
|
}
|
|
|
|
question, err := q.CreateQuestion(ctx, dbgen.CreateQuestionParams{
|
|
QuestionText: input.QuestionText,
|
|
QuestionType: input.QuestionType,
|
|
DifficultyLevel: toPgText(input.DifficultyLevel),
|
|
Column4: points,
|
|
Explanation: toPgText(input.Explanation),
|
|
Tips: toPgText(input.Tips),
|
|
VoicePrompt: toPgText(input.VoicePrompt),
|
|
SampleAnswerVoicePrompt: toPgText(input.SampleAnswerVoicePrompt),
|
|
ImageUrl: toPgText(input.ImageURL),
|
|
Column10: status,
|
|
})
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
|
|
for _, opt := range input.Options {
|
|
var order interface{}
|
|
if opt.OptionOrder != nil {
|
|
order = *opt.OptionOrder
|
|
}
|
|
_, err = q.CreateQuestionOption(ctx, dbgen.CreateQuestionOptionParams{
|
|
QuestionID: question.ID,
|
|
OptionText: opt.OptionText,
|
|
Column3: order,
|
|
Column4: opt.IsCorrect,
|
|
})
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
}
|
|
|
|
for _, sa := range input.ShortAnswers {
|
|
var matchType interface{}
|
|
if sa.MatchType != nil {
|
|
matchType = *sa.MatchType
|
|
}
|
|
_, err = q.CreateQuestionShortAnswer(ctx, dbgen.CreateQuestionShortAnswerParams{
|
|
QuestionID: question.ID,
|
|
AcceptableAnswer: sa.AcceptableAnswer,
|
|
Column3: matchType,
|
|
})
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
}
|
|
|
|
if input.AudioCorrectAnswerText != nil && *input.AudioCorrectAnswerText != "" {
|
|
_, err = q.CreateQuestionAudioAnswer(ctx, dbgen.CreateQuestionAudioAnswerParams{
|
|
QuestionID: question.ID,
|
|
CorrectAnswerText: *input.AudioCorrectAnswerText,
|
|
})
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
}
|
|
|
|
if err = tx.Commit(ctx); err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
|
|
return questionToDomain(question), nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionByID(ctx context.Context, id int64) (domain.Question, error) {
|
|
q, err := s.queries.GetQuestionByID(ctx, id)
|
|
if err != nil {
|
|
return domain.Question{}, err
|
|
}
|
|
return questionToDomain(q), nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionWithDetails(ctx context.Context, id int64) (domain.QuestionWithDetails, error) {
|
|
q, err := s.queries.GetQuestionByID(ctx, id)
|
|
if err != nil {
|
|
return domain.QuestionWithDetails{}, err
|
|
}
|
|
|
|
opts, err := s.queries.GetOptionsByQuestionID(ctx, id)
|
|
if err != nil {
|
|
return domain.QuestionWithDetails{}, err
|
|
}
|
|
|
|
shortAnswers, err := s.queries.GetShortAnswersByQuestionID(ctx, id)
|
|
if err != nil {
|
|
return domain.QuestionWithDetails{}, err
|
|
}
|
|
|
|
options := make([]domain.QuestionOption, len(opts))
|
|
for i, o := range opts {
|
|
options[i] = questionOptionToDomain(o)
|
|
}
|
|
|
|
answers := make([]domain.QuestionShortAnswer, len(shortAnswers))
|
|
for i, a := range shortAnswers {
|
|
answers[i] = questionShortAnswerToDomain(a)
|
|
}
|
|
|
|
var audioAnswer *domain.QuestionAudioAnswer
|
|
aa, err := s.queries.GetAudioAnswerByQuestionID(ctx, id)
|
|
if err == nil {
|
|
mapped := questionAudioAnswerToDomain(aa)
|
|
audioAnswer = &mapped
|
|
}
|
|
|
|
return domain.QuestionWithDetails{
|
|
Question: questionToDomain(q),
|
|
Options: options,
|
|
ShortAnswers: answers,
|
|
AudioAnswer: audioAnswer,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Store) ListQuestions(ctx context.Context, questionType, difficulty, status *string, limit, offset int32) ([]domain.Question, int64, error) {
|
|
var qType, diff, stat string
|
|
if questionType != nil {
|
|
qType = *questionType
|
|
}
|
|
if difficulty != nil {
|
|
diff = *difficulty
|
|
}
|
|
if status != nil {
|
|
stat = *status
|
|
}
|
|
|
|
rows, err := s.queries.ListQuestions(ctx, dbgen.ListQuestionsParams{
|
|
Column1: qType,
|
|
Column2: diff,
|
|
Column3: stat,
|
|
Limit: pgtype.Int4{Int32: limit, Valid: true},
|
|
Offset: pgtype.Int4{Int32: offset, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
var totalCount int64
|
|
questions := make([]domain.Question, len(rows))
|
|
for i, r := range rows {
|
|
if i == 0 {
|
|
totalCount = r.TotalCount
|
|
}
|
|
questions[i] = domain.Question{
|
|
ID: r.ID,
|
|
QuestionText: r.QuestionText,
|
|
QuestionType: r.QuestionType,
|
|
DifficultyLevel: fromPgText(r.DifficultyLevel),
|
|
Points: r.Points,
|
|
Explanation: fromPgText(r.Explanation),
|
|
Tips: fromPgText(r.Tips),
|
|
VoicePrompt: fromPgText(r.VoicePrompt),
|
|
SampleAnswerVoicePrompt: fromPgText(r.SampleAnswerVoicePrompt),
|
|
ImageURL: fromPgText(r.ImageUrl),
|
|
Status: r.Status,
|
|
CreatedAt: r.CreatedAt.Time,
|
|
UpdatedAt: timePtr(r.UpdatedAt),
|
|
}
|
|
}
|
|
|
|
return questions, totalCount, nil
|
|
}
|
|
|
|
func (s *Store) SearchQuestions(ctx context.Context, query string, limit, offset int32) ([]domain.Question, int64, error) {
|
|
rows, err := s.queries.SearchQuestions(ctx, dbgen.SearchQuestionsParams{
|
|
Column1: pgtype.Text{String: query, Valid: true},
|
|
Limit: pgtype.Int4{Int32: limit, Valid: true},
|
|
Offset: pgtype.Int4{Int32: offset, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
var totalCount int64
|
|
questions := make([]domain.Question, len(rows))
|
|
for i, r := range rows {
|
|
if i == 0 {
|
|
totalCount = r.TotalCount
|
|
}
|
|
questions[i] = domain.Question{
|
|
ID: r.ID,
|
|
QuestionText: r.QuestionText,
|
|
QuestionType: r.QuestionType,
|
|
DifficultyLevel: fromPgText(r.DifficultyLevel),
|
|
Points: r.Points,
|
|
Explanation: fromPgText(r.Explanation),
|
|
Tips: fromPgText(r.Tips),
|
|
VoicePrompt: fromPgText(r.VoicePrompt),
|
|
SampleAnswerVoicePrompt: fromPgText(r.SampleAnswerVoicePrompt),
|
|
ImageURL: fromPgText(r.ImageUrl),
|
|
Status: r.Status,
|
|
CreatedAt: r.CreatedAt.Time,
|
|
UpdatedAt: timePtr(r.UpdatedAt),
|
|
}
|
|
}
|
|
|
|
return questions, totalCount, nil
|
|
}
|
|
|
|
func (s *Store) UpdateQuestion(ctx context.Context, id int64, input domain.CreateQuestionInput) error {
|
|
var points int32
|
|
if input.Points != nil {
|
|
points = *input.Points
|
|
}
|
|
var status string
|
|
if input.Status != nil {
|
|
status = *input.Status
|
|
}
|
|
|
|
err := s.queries.UpdateQuestion(ctx, dbgen.UpdateQuestionParams{
|
|
ID: id,
|
|
QuestionText: input.QuestionText,
|
|
QuestionType: input.QuestionType,
|
|
DifficultyLevel: toPgText(input.DifficultyLevel),
|
|
Points: points,
|
|
Explanation: toPgText(input.Explanation),
|
|
Tips: toPgText(input.Tips),
|
|
VoicePrompt: toPgText(input.VoicePrompt),
|
|
SampleAnswerVoicePrompt: toPgText(input.SampleAnswerVoicePrompt),
|
|
ImageUrl: toPgText(input.ImageURL),
|
|
Status: status,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if input.AudioCorrectAnswerText != nil {
|
|
_ = s.queries.DeleteAudioAnswerByQuestionID(ctx, id)
|
|
if *input.AudioCorrectAnswerText != "" {
|
|
_, err = s.queries.CreateQuestionAudioAnswer(ctx, dbgen.CreateQuestionAudioAnswerParams{
|
|
QuestionID: id,
|
|
CorrectAnswerText: *input.AudioCorrectAnswerText,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) ArchiveQuestion(ctx context.Context, id int64) error {
|
|
return s.queries.ArchiveQuestion(ctx, id)
|
|
}
|
|
|
|
func (s *Store) DeleteQuestion(ctx context.Context, id int64) error {
|
|
return s.queries.DeleteQuestion(ctx, id)
|
|
}
|
|
|
|
func (s *Store) CreateQuestionOption(ctx context.Context, questionID int64, optionText string, optionOrder *int32, isCorrect bool) (domain.QuestionOption, error) {
|
|
var order interface{}
|
|
if optionOrder != nil {
|
|
order = *optionOrder
|
|
}
|
|
|
|
opt, err := s.queries.CreateQuestionOption(ctx, dbgen.CreateQuestionOptionParams{
|
|
QuestionID: questionID,
|
|
OptionText: optionText,
|
|
Column3: order,
|
|
Column4: isCorrect,
|
|
})
|
|
if err != nil {
|
|
return domain.QuestionOption{}, err
|
|
}
|
|
return questionOptionToDomain(opt), nil
|
|
}
|
|
|
|
func (s *Store) GetOptionsByQuestionID(ctx context.Context, questionID int64) ([]domain.QuestionOption, error) {
|
|
opts, err := s.queries.GetOptionsByQuestionID(ctx, questionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionOption, len(opts))
|
|
for i, o := range opts {
|
|
result[i] = questionOptionToDomain(o)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) UpdateQuestionOption(ctx context.Context, id int64, optionText *string, optionOrder *int32, isCorrect *bool) error {
|
|
var text string
|
|
if optionText != nil {
|
|
text = *optionText
|
|
}
|
|
var order int32
|
|
if optionOrder != nil {
|
|
order = *optionOrder
|
|
}
|
|
var correct bool
|
|
if isCorrect != nil {
|
|
correct = *isCorrect
|
|
}
|
|
|
|
return s.queries.UpdateQuestionOption(ctx, dbgen.UpdateQuestionOptionParams{
|
|
ID: id,
|
|
OptionText: text,
|
|
OptionOrder: order,
|
|
IsCorrect: correct,
|
|
})
|
|
}
|
|
|
|
func (s *Store) DeleteQuestionOption(ctx context.Context, id int64) error {
|
|
return s.queries.DeleteQuestionOption(ctx, id)
|
|
}
|
|
|
|
func (s *Store) DeleteOptionsByQuestionID(ctx context.Context, questionID int64) error {
|
|
return s.queries.DeleteOptionsByQuestionID(ctx, questionID)
|
|
}
|
|
|
|
func (s *Store) CreateQuestionShortAnswer(ctx context.Context, questionID int64, acceptableAnswer string, matchType *string) (domain.QuestionShortAnswer, error) {
|
|
var mt interface{}
|
|
if matchType != nil {
|
|
mt = *matchType
|
|
}
|
|
|
|
sa, err := s.queries.CreateQuestionShortAnswer(ctx, dbgen.CreateQuestionShortAnswerParams{
|
|
QuestionID: questionID,
|
|
AcceptableAnswer: acceptableAnswer,
|
|
Column3: mt,
|
|
})
|
|
if err != nil {
|
|
return domain.QuestionShortAnswer{}, err
|
|
}
|
|
return questionShortAnswerToDomain(sa), nil
|
|
}
|
|
|
|
func (s *Store) GetShortAnswersByQuestionID(ctx context.Context, questionID int64) ([]domain.QuestionShortAnswer, error) {
|
|
answers, err := s.queries.GetShortAnswersByQuestionID(ctx, questionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionShortAnswer, len(answers))
|
|
for i, a := range answers {
|
|
result[i] = questionShortAnswerToDomain(a)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) UpdateQuestionShortAnswer(ctx context.Context, id int64, acceptableAnswer, matchType *string) error {
|
|
var answer, mt string
|
|
if acceptableAnswer != nil {
|
|
answer = *acceptableAnswer
|
|
}
|
|
if matchType != nil {
|
|
mt = *matchType
|
|
}
|
|
|
|
return s.queries.UpdateQuestionShortAnswer(ctx, dbgen.UpdateQuestionShortAnswerParams{
|
|
ID: id,
|
|
AcceptableAnswer: answer,
|
|
MatchType: mt,
|
|
})
|
|
}
|
|
|
|
func (s *Store) DeleteQuestionShortAnswer(ctx context.Context, id int64) error {
|
|
return s.queries.DeleteQuestionShortAnswer(ctx, id)
|
|
}
|
|
|
|
func (s *Store) DeleteShortAnswersByQuestionID(ctx context.Context, questionID int64) error {
|
|
return s.queries.DeleteShortAnswersByQuestionID(ctx, questionID)
|
|
}
|
|
|
|
func (s *Store) CreateQuestionSet(ctx context.Context, input domain.CreateQuestionSetInput) (domain.QuestionSet, error) {
|
|
var shuffleQuestions interface{}
|
|
if input.ShuffleQuestions != nil {
|
|
shuffleQuestions = *input.ShuffleQuestions
|
|
}
|
|
var status interface{}
|
|
if input.Status != nil {
|
|
status = *input.Status
|
|
}
|
|
|
|
qs, err := s.queries.CreateQuestionSet(ctx, dbgen.CreateQuestionSetParams{
|
|
Title: input.Title,
|
|
Description: toPgText(input.Description),
|
|
SetType: input.SetType,
|
|
OwnerType: toPgText(input.OwnerType),
|
|
OwnerID: toPgInt8(input.OwnerID),
|
|
BannerImage: toPgText(input.BannerImage),
|
|
Persona: toPgText(input.Persona),
|
|
TimeLimitMinutes: toPgInt4(input.TimeLimitMinutes),
|
|
PassingScore: toPgInt4(input.PassingScore),
|
|
Column10: shuffleQuestions,
|
|
Column11: status,
|
|
SubCourseVideoID: toPgInt8(input.SubCourseVideoID),
|
|
})
|
|
if err != nil {
|
|
return domain.QuestionSet{}, err
|
|
}
|
|
return questionSetToDomain(qs), nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionSetByID(ctx context.Context, id int64) (domain.QuestionSet, error) {
|
|
qs, err := s.queries.GetQuestionSetByID(ctx, id)
|
|
if err != nil {
|
|
return domain.QuestionSet{}, err
|
|
}
|
|
return questionSetToDomain(qs), nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionSetsByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.QuestionSet, error) {
|
|
sets, err := s.queries.GetQuestionSetsByOwner(ctx, dbgen.GetQuestionSetsByOwnerParams{
|
|
OwnerType: pgtype.Text{String: ownerType, Valid: true},
|
|
OwnerID: pgtype.Int8{Int64: ownerID, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionSet, len(sets))
|
|
for i, qs := range sets {
|
|
result[i] = questionSetToDomain(qs)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionSetsByType(ctx context.Context, setType string, limit, offset int32) ([]domain.QuestionSet, int64, error) {
|
|
rows, err := s.queries.GetQuestionSetsByType(ctx, dbgen.GetQuestionSetsByTypeParams{
|
|
SetType: setType,
|
|
Limit: pgtype.Int4{Int32: limit, Valid: true},
|
|
Offset: pgtype.Int4{Int32: offset, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
var totalCount int64
|
|
result := make([]domain.QuestionSet, len(rows))
|
|
for i, r := range rows {
|
|
if i == 0 {
|
|
totalCount = r.TotalCount
|
|
}
|
|
result[i] = domain.QuestionSet{
|
|
ID: r.ID,
|
|
Title: r.Title,
|
|
Description: fromPgText(r.Description),
|
|
SetType: r.SetType,
|
|
OwnerType: fromPgText(r.OwnerType),
|
|
OwnerID: fromPgInt8(r.OwnerID),
|
|
BannerImage: fromPgText(r.BannerImage),
|
|
Persona: fromPgText(r.Persona),
|
|
TimeLimitMinutes: fromPgInt4(r.TimeLimitMinutes),
|
|
PassingScore: fromPgInt4(r.PassingScore),
|
|
ShuffleQuestions: r.ShuffleQuestions,
|
|
Status: r.Status,
|
|
SubCourseVideoID: fromPgInt8(r.SubCourseVideoID),
|
|
CreatedAt: r.CreatedAt.Time,
|
|
UpdatedAt: timePtr(r.UpdatedAt),
|
|
}
|
|
}
|
|
return result, totalCount, nil
|
|
}
|
|
|
|
func (s *Store) GetPublishedQuestionSetsByOwner(ctx context.Context, ownerType string, ownerID int64) ([]domain.QuestionSet, error) {
|
|
sets, err := s.queries.GetPublishedQuestionSetsByOwner(ctx, dbgen.GetPublishedQuestionSetsByOwnerParams{
|
|
OwnerType: pgtype.Text{String: ownerType, Valid: true},
|
|
OwnerID: pgtype.Int8{Int64: ownerID, Valid: true},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionSet, len(sets))
|
|
for i, qs := range sets {
|
|
result[i] = questionSetToDomain(qs)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) GetInitialAssessmentSet(ctx context.Context) (domain.QuestionSet, error) {
|
|
qs, err := s.queries.GetInitialAssessmentSet(ctx)
|
|
if err != nil {
|
|
return domain.QuestionSet{}, err
|
|
}
|
|
return questionSetToDomain(qs), nil
|
|
}
|
|
|
|
func (s *Store) UpdateQuestionSet(ctx context.Context, id int64, input domain.CreateQuestionSetInput) error {
|
|
var shuffleQuestions bool
|
|
if input.ShuffleQuestions != nil {
|
|
shuffleQuestions = *input.ShuffleQuestions
|
|
}
|
|
var status string
|
|
if input.Status != nil {
|
|
status = *input.Status
|
|
}
|
|
|
|
return s.queries.UpdateQuestionSet(ctx, dbgen.UpdateQuestionSetParams{
|
|
ID: id,
|
|
Title: input.Title,
|
|
Description: toPgText(input.Description),
|
|
BannerImage: toPgText(input.BannerImage),
|
|
Persona: toPgText(input.Persona),
|
|
TimeLimitMinutes: toPgInt4(input.TimeLimitMinutes),
|
|
PassingScore: toPgInt4(input.PassingScore),
|
|
ShuffleQuestions: shuffleQuestions,
|
|
Status: status,
|
|
SubCourseVideoID: toPgInt8(input.SubCourseVideoID),
|
|
})
|
|
}
|
|
|
|
func (s *Store) ArchiveQuestionSet(ctx context.Context, id int64) error {
|
|
return s.queries.ArchiveQuestionSet(ctx, id)
|
|
}
|
|
|
|
func (s *Store) DeleteQuestionSet(ctx context.Context, id int64) error {
|
|
return s.queries.DeleteQuestionSet(ctx, id)
|
|
}
|
|
|
|
func (s *Store) AddQuestionToSet(ctx context.Context, setID, questionID int64, displayOrder *int32) (domain.QuestionSetItem, error) {
|
|
var order interface{}
|
|
if displayOrder != nil {
|
|
order = *displayOrder
|
|
}
|
|
|
|
item, err := s.queries.AddQuestionToSet(ctx, dbgen.AddQuestionToSetParams{
|
|
SetID: setID,
|
|
QuestionID: questionID,
|
|
Column3: order,
|
|
})
|
|
if err != nil {
|
|
return domain.QuestionSetItem{}, err
|
|
}
|
|
return questionSetItemToDomain(item), nil
|
|
}
|
|
|
|
func (s *Store) GetQuestionSetItems(ctx context.Context, setID int64) ([]domain.QuestionSetItemWithQuestion, error) {
|
|
rows, err := s.queries.GetQuestionSetItems(ctx, setID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionSetItemWithQuestion, len(rows))
|
|
for i, r := range rows {
|
|
result[i] = domain.QuestionSetItemWithQuestion{
|
|
QuestionSetItem: domain.QuestionSetItem{
|
|
ID: r.ID,
|
|
SetID: r.SetID,
|
|
QuestionID: r.QuestionID,
|
|
DisplayOrder: r.DisplayOrder,
|
|
},
|
|
QuestionText: r.QuestionText,
|
|
QuestionType: r.QuestionType,
|
|
DifficultyLevel: fromPgText(r.DifficultyLevel),
|
|
Points: r.Points,
|
|
Explanation: fromPgText(r.Explanation),
|
|
Tips: fromPgText(r.Tips),
|
|
VoicePrompt: fromPgText(r.VoicePrompt),
|
|
ImageURL: fromPgText(r.ImageUrl),
|
|
QuestionStatus: r.QuestionStatus,
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) GetPublishedQuestionsInSet(ctx context.Context, setID int64) ([]domain.QuestionSetItemWithQuestion, error) {
|
|
rows, err := s.queries.GetPublishedQuestionsInSet(ctx, setID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionSetItemWithQuestion, len(rows))
|
|
for i, r := range rows {
|
|
result[i] = domain.QuestionSetItemWithQuestion{
|
|
QuestionSetItem: domain.QuestionSetItem{
|
|
ID: r.ID,
|
|
SetID: r.SetID,
|
|
QuestionID: r.QuestionID,
|
|
DisplayOrder: r.DisplayOrder,
|
|
},
|
|
QuestionText: r.QuestionText,
|
|
QuestionType: r.QuestionType,
|
|
DifficultyLevel: fromPgText(r.DifficultyLevel),
|
|
Points: r.Points,
|
|
Explanation: fromPgText(r.Explanation),
|
|
Tips: fromPgText(r.Tips),
|
|
VoicePrompt: fromPgText(r.VoicePrompt),
|
|
ImageURL: fromPgText(r.ImageUrl),
|
|
QuestionStatus: "PUBLISHED",
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) RemoveQuestionFromSet(ctx context.Context, setID, questionID int64) error {
|
|
return s.queries.RemoveQuestionFromSet(ctx, dbgen.RemoveQuestionFromSetParams{
|
|
SetID: setID,
|
|
QuestionID: questionID,
|
|
})
|
|
}
|
|
|
|
func (s *Store) UpdateQuestionOrder(ctx context.Context, setID, questionID int64, displayOrder int32) error {
|
|
return s.queries.UpdateQuestionOrder(ctx, dbgen.UpdateQuestionOrderParams{
|
|
SetID: setID,
|
|
QuestionID: questionID,
|
|
DisplayOrder: displayOrder,
|
|
})
|
|
}
|
|
|
|
func (s *Store) CountQuestionsInSet(ctx context.Context, setID int64) (int64, error) {
|
|
return s.queries.CountQuestionsInSet(ctx, setID)
|
|
}
|
|
|
|
func (s *Store) GetQuestionSetsContainingQuestion(ctx context.Context, questionID int64) ([]domain.QuestionSet, error) {
|
|
sets, err := s.queries.GetQuestionSetsContainingQuestion(ctx, questionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.QuestionSet, len(sets))
|
|
for i, qs := range sets {
|
|
result[i] = questionSetToDomain(qs)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// User Persona methods for question sets
|
|
|
|
func (s *Store) AddUserPersonaToQuestionSet(ctx context.Context, questionSetID, userID int64, displayOrder int32) error {
|
|
_, err := s.queries.AddUserPersonaToQuestionSet(ctx, dbgen.AddUserPersonaToQuestionSetParams{
|
|
QuestionSetID: questionSetID,
|
|
UserID: userID,
|
|
Column3: displayOrder,
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (s *Store) RemoveUserPersonaFromQuestionSet(ctx context.Context, questionSetID, userID int64) error {
|
|
return s.queries.RemoveUserPersonaFromQuestionSet(ctx, dbgen.RemoveUserPersonaFromQuestionSetParams{
|
|
QuestionSetID: questionSetID,
|
|
UserID: userID,
|
|
})
|
|
}
|
|
|
|
func (s *Store) GetUserPersonasByQuestionSetID(ctx context.Context, questionSetID int64) ([]domain.UserPersona, error) {
|
|
rows, err := s.queries.GetUserPersonasByQuestionSetID(ctx, questionSetID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.UserPersona, len(rows))
|
|
for i, r := range rows {
|
|
result[i] = domain.UserPersona{
|
|
ID: r.ID,
|
|
FirstName: fromPgText(r.FirstName),
|
|
LastName: fromPgText(r.LastName),
|
|
NickName: fromPgText(r.NickName),
|
|
ProfilePictureURL: fromPgText(r.ProfilePictureUrl),
|
|
Role: r.Role,
|
|
DisplayOrder: r.DisplayOrder.Int32,
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *Store) ReorderQuestionSets(ctx context.Context, ids []int64, positions []int32) error {
|
|
return s.queries.ReorderQuestionSets(ctx, dbgen.ReorderQuestionSetsParams{
|
|
Ids: ids,
|
|
Positions: positions,
|
|
})
|
|
}
|