304 lines
7.5 KiB
Go
304 lines
7.5 KiB
Go
package assessment
|
|
|
|
import (
|
|
dbgen "Yimaru-Backend/gen/db"
|
|
"Yimaru-Backend/internal/domain"
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
func (s *Service) CreateQuestion(
|
|
ctx context.Context,
|
|
input domain.CreateAssessmentQuestionInput,
|
|
) error {
|
|
repo := s.initialAssessmentStore
|
|
|
|
// 1. Create Question
|
|
question, err := repo.CreateAssessmentQuestion(
|
|
ctx,
|
|
dbgen.CreateAssessmentQuestionParams{
|
|
Title: input.Title,
|
|
Description: func() pgtype.Text {
|
|
ns := toNullString(input.Description)
|
|
return pgtype.Text{String: ns.String, Valid: ns.Valid}
|
|
}(),
|
|
QuestionType: string(input.QuestionType),
|
|
DifficultyLevel: pgtype.Text{String: input.DifficultyLevel},
|
|
Points: input.Points,
|
|
IsActive: input.IsActive,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 2. Branch by Question Type
|
|
switch input.QuestionType {
|
|
|
|
case domain.MultipleChoice:
|
|
if len(input.Options) == 0 {
|
|
return errors.New("multiple choice question requires options")
|
|
}
|
|
|
|
for _, opt := range input.Options {
|
|
_, err := repo.CreateQuestionOption(
|
|
ctx,
|
|
dbgen.CreateQuestionOptionParams{
|
|
QuestionID: question.ID,
|
|
OptionText: opt.Text,
|
|
OptionOrder: opt.Order,
|
|
IsCorrect: opt.IsCorrect,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
case domain.TrueFalse:
|
|
// TRUE
|
|
if _, err := repo.CreateQuestionOption(
|
|
ctx,
|
|
dbgen.CreateQuestionOptionParams{
|
|
QuestionID: question.ID,
|
|
OptionText: "True",
|
|
OptionOrder: 1,
|
|
IsCorrect: true,
|
|
},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
// FALSE
|
|
if _, err := repo.CreateQuestionOption(
|
|
ctx,
|
|
dbgen.CreateQuestionOptionParams{
|
|
QuestionID: question.ID,
|
|
OptionText: "False",
|
|
OptionOrder: 2,
|
|
IsCorrect: false,
|
|
},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
case domain.ShortAnswer:
|
|
if input.CorrectAnswer == nil || *input.CorrectAnswer == "" {
|
|
return errors.New("short answer question requires correct_answer")
|
|
}
|
|
|
|
_, err := repo.CreateShortAnswer(
|
|
ctx,
|
|
dbgen.CreateShortAnswerParams{
|
|
QuestionID: question.ID,
|
|
CorrectAnswer: *input.CorrectAnswer,
|
|
},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
default:
|
|
return errors.New("unsupported question type")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) ListQuestions(ctx context.Context) ([]domain.QuestionWithDetails, error) {
|
|
// repo := s.initialAssessmentStore
|
|
|
|
questions, err := s.initialAssessmentStore.GetActiveAssessmentQuestions(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make([]domain.QuestionWithDetails, 0, len(questions))
|
|
for _, q := range questions {
|
|
item := domain.QuestionWithDetails{Question: q}
|
|
|
|
switch domain.QuestionType(q.QuestionType) {
|
|
case domain.MultipleChoice, domain.TrueFalse:
|
|
opts, err := s.initialAssessmentStore.GetQuestionOptions(ctx, q.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, opt := range opts {
|
|
tempOpt := domain.QuestionOption{
|
|
QuestionID: opt.ID,
|
|
OptionText: opt.OptionText,
|
|
}
|
|
item.Options = append(item.Options, tempOpt)
|
|
}
|
|
|
|
// case domain.ShortAnswer:
|
|
// sa, err := s.initialAssessmentStore.GetShortAnswerByQuestionID(ctx, q.ID)
|
|
// if err != nil {
|
|
// return nil, err
|
|
// }
|
|
// item.ShortAnswer = &sa
|
|
// }
|
|
|
|
out = append(out, item)
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Service) GetQuestionByID(ctx context.Context, id int64) (domain.QuestionWithDetails, error) {
|
|
repo := s.initialAssessmentStore
|
|
|
|
q, err := repo.GetAssessmentQuestionByID(ctx, id)
|
|
if err != nil {
|
|
return domain.QuestionWithDetails{}, err
|
|
}
|
|
|
|
item := domain.QuestionWithDetails{Question: q}
|
|
switch domain.QuestionType(q.QuestionType) {
|
|
case domain.MultipleChoice, domain.TrueFalse:
|
|
opts, err := repo.GetQuestionOptions(ctx, q.ID)
|
|
if err != nil {
|
|
return domain.QuestionWithDetails{}, err
|
|
}
|
|
for _, opt := range opts {
|
|
tempOpt := domain.QuestionOption{
|
|
QuestionID: opt.ID,
|
|
OptionText: opt.OptionText,
|
|
}
|
|
item.Options = append(item.Options, tempOpt)
|
|
}
|
|
// case domain.ShortAnswer:
|
|
// sa, err := repo.GetShortAnswerByQuestionID(ctx, q.ID)
|
|
// if err != nil {
|
|
// return QuestionWithDetails{}, err
|
|
// }
|
|
// item.ShortAnswer = &sa
|
|
}
|
|
|
|
return item, nil
|
|
}
|
|
|
|
// func (s *Service) UpdateQuestion(ctx context.Context, id int64, input domain.UpdateAssessmentQuestionInput) error {
|
|
// repo := s.initialAssessmentStore
|
|
|
|
// // fetch existing
|
|
// existing, err := repo.GetAssessmentQuestionByID(ctx, id)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// // update base question
|
|
// _, err = repo.UpdateAssessmentQuestion(
|
|
// ctx,
|
|
// dbgen.UpdateAssessmentQuestionParams{
|
|
// ID: id,
|
|
// Title: input.Title,
|
|
// Description: func() pgtype.Text {
|
|
// ns := toNullString(input.Description)
|
|
// return pgtype.Text{String: ns.String, Valid: ns.Valid}
|
|
// }(),
|
|
// QuestionType: string(input.QuestionType),
|
|
// DifficultyLevel: pgtype.Text{String: input.DifficultyLevel},
|
|
// Points: input.Points,
|
|
// IsActive: input.IsActive,
|
|
// },
|
|
// )
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// // remove previous dependents (safe to remove regardless of new type)
|
|
// // try delete options and short answer; ignore not-found errors if repo returns them
|
|
// if err := repo.DeleteQuestionOptionsByQuestionID(ctx, id); err != nil {
|
|
// return err
|
|
// }
|
|
// if err := repo.DeleteShortAnswerByQuestionID(ctx, id); err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// // create dependents for new type
|
|
// switch input.QuestionType {
|
|
// case domain.MultipleChoice:
|
|
// if len(input.Options) == 0 {
|
|
// return errors.New("multiple choice question requires options")
|
|
// }
|
|
// for _, opt := range input.Options {
|
|
// if _, err := repo.CreateQuestionOption(
|
|
// ctx,
|
|
// dbgen.CreateQuestionOptionParams{
|
|
// QuestionID: id,
|
|
// OptionText: opt.Text,
|
|
// OptionOrder: opt.Order,
|
|
// IsCorrect: opt.IsCorrect,
|
|
// },
|
|
// ); err != nil {
|
|
// return err
|
|
// }
|
|
// }
|
|
// case domain.TrueFalse:
|
|
// if _, err := repo.CreateQuestionOption(ctx, dbgen.CreateQuestionOptionParams{
|
|
// QuestionID: id,
|
|
// OptionText: "True",
|
|
// OptionOrder: 1,
|
|
// IsCorrect: true,
|
|
// }); err != nil {
|
|
// return err
|
|
// }
|
|
// if _, err := repo.CreateQuestionOption(ctx, dbgen.CreateQuestionOptionParams{
|
|
// QuestionID: id,
|
|
// OptionText: "False",
|
|
// OptionOrder: 2,
|
|
// IsCorrect: false,
|
|
// }); err != nil {
|
|
// return err
|
|
// }
|
|
// case domain.ShortAnswer:
|
|
// if input.CorrectAnswer == nil || *input.CorrectAnswer == "" {
|
|
// return errors.New("short answer question requires correct_answer")
|
|
// }
|
|
// if _, err := repo.CreateShortAnswer(ctx, dbgen.CreateShortAnswerParams{
|
|
// QuestionID: id,
|
|
// CorrectAnswer: *input.CorrectAnswer,
|
|
// }); err != nil {
|
|
// return err
|
|
// }
|
|
// default:
|
|
// return errors.New("unsupported question type")
|
|
// }
|
|
|
|
// _ = existing
|
|
// return nil
|
|
// }
|
|
|
|
// func (s *Service) DeleteQuestion(ctx context.Context, id int64) error {
|
|
// repo := s.initialAssessmentStore
|
|
|
|
// q, err := repo.GetAssessmentQuestionByID(ctx, id)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// // delete dependents by existing type
|
|
// switch domain.QuestionType(q.QuestionType) {
|
|
// case domain.MultipleChoice, domain.TrueFalse:
|
|
// if err := repo.DeleteQuestionOptionsByQuestionID(ctx, id); err != nil {
|
|
// return err
|
|
// }
|
|
// case domain.ShortAnswer:
|
|
// if err := repo.DeleteShortAnswerByQuestionID(ctx, id); err != nil {
|
|
// return err
|
|
// }
|
|
// }
|
|
|
|
// if err := repo.DeleteAssessmentQuestion(ctx, id); err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// return nil
|
|
// }
|
|
|
|
// ...existing code...
|