common/repository/question/question.go

488 lines
12 KiB
Go
Raw Normal View History

2024-02-19 16:33:15 +00:00
package question
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/lib/pq"
2024-02-19 17:43:47 +00:00
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
2024-04-16 11:35:25 +00:00
"sort"
2024-02-19 16:33:15 +00:00
"strings"
"sync"
"time"
)
type Deps struct {
Queries *sqlcgen.Queries
Pool *sql.DB
}
type QuestionRepository struct {
queries *sqlcgen.Queries
pool *sql.DB
}
func NewQuestionRepository(deps Deps) *QuestionRepository {
return &QuestionRepository{
queries: deps.Queries,
pool: deps.Pool,
}
}
// test +
func (r *QuestionRepository) CreateQuestion(ctx context.Context, record *model.Question) error {
params := sqlcgen.InsertQuestionParams{
QuizID: int64(record.QuizId),
Title: record.Title,
Description: sql.NullString{String: record.Description, Valid: true},
Questiontype: record.Type,
Required: sql.NullBool{Bool: record.Required, Valid: true},
Page: sql.NullInt16{Int16: int16(record.Page), Valid: true},
Content: sql.NullString{String: record.Content, Valid: true},
ParentIds: record.ParentIds,
UpdatedAt: sql.NullTime{Time: time.Now(), Valid: true},
}
data, err := r.queries.InsertQuestion(ctx, params)
if err != nil {
return err
}
record.Id = uint64(data.ID)
record.CreatedAt = data.CreatedAt.Time
record.UpdatedAt = data.UpdatedAt.Time
return nil
}
// test +
// GetQuestionList function for get data page from db
func (r *QuestionRepository) GetQuestionList(
ctx context.Context,
limit, offset, from, to, quizId uint64,
deleted, required bool,
search, qType string) ([]model.Question, uint64, error) {
query := `
SELECT que.* FROM question as que JOIN quiz as qui on que.quiz_id = ANY(qui.parent_ids) or que.quiz_id = qui.id
%s
ORDER BY que.page, que.created_at ASC
LIMIT $%d OFFSET $%d;
`
queryCnt := `SELECT count(1) FROM question as que JOIN quiz as qui on que.quiz_id = ANY(qui.parent_ids) or que.quiz_id = qui.id %s;`
var (
whereClause []string
data []interface{}
)
if quizId != 0 {
data = append(data, quizId)
whereClause = append(whereClause, fmt.Sprintf("qui.id = $%d", len(data)))
}
if from != 0 {
data = append(data, from)
whereClause = append(whereClause, fmt.Sprintf("que.created_at >= to_timestamp($%d)", len(data)))
}
if to != 0 {
data = append(data, to)
whereClause = append(whereClause, fmt.Sprintf("que.created_at <= to_timestamp($%d)", len(data)))
}
if deleted {
whereClause = append(whereClause, fmt.Sprintf("que.deleted = true"))
} else {
whereClause = append(whereClause, fmt.Sprintf("que.deleted = false"))
}
if required {
whereClause = append(whereClause, fmt.Sprintf("que.required = true"))
}
if qType != "" {
data = append(data, qType)
whereClause = append(whereClause, fmt.Sprintf("que.questiontype = $%d", len(data)))
}
if search != "" {
data = append(data, search)
whereClause = append(whereClause, fmt.Sprintf("to_tsvector('russian', que.title) @@ to_tsquery('russian', $%d)", len(data)))
}
data = append(data, limit, offset)
if len(whereClause) != 0 {
query = fmt.Sprintf(query,
fmt.Sprintf(" WHERE %s ", strings.Join(whereClause, " AND ")),
len(data)-1, len(data))
queryCnt = fmt.Sprintf(queryCnt, fmt.Sprintf(" WHERE %s ", strings.Join(whereClause, " AND ")))
} else {
query = fmt.Sprintf(query, "", 1, 2)
queryCnt = fmt.Sprintf(queryCnt, "")
}
var (
qerr, cerr error
count uint64
result []model.Question
)
fmt.Println("QUESTIONS", queryCnt, query, data, data[:len(data)-2])
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
rows, err := r.pool.QueryContext(ctx, query, data...)
if err != nil {
qerr = err
return
}
defer rows.Close()
var piece model.Question
pIds := pq.Int32Array{}
for rows.Next() {
if err := rows.Scan(
&piece.Id,
&piece.QuizId,
&piece.Title,
&piece.Description,
&piece.Type,
&piece.Required,
&piece.Deleted,
&piece.Page,
&piece.Content,
&piece.Version,
&pIds,
&piece.CreatedAt,
&piece.UpdatedAt,
); err != nil {
qerr = err
return
}
piece.ParentIds = pIds
result = append(result, piece)
}
}()
go func() {
defer wg.Done()
var (
err error
rows *sql.Rows
)
if len(data) == 2 {
rows, err = r.pool.QueryContext(ctx, queryCnt)
} else {
rows, err = r.pool.QueryContext(ctx, queryCnt, data[:len(data)-2]...)
}
if err != nil {
cerr = err
return
}
defer rows.Close()
if ok := rows.Next(); !ok {
cerr = errors.New("can not next count")
}
if err := rows.Scan(&count); err != nil {
cerr = err
}
}()
wg.Wait()
if cerr != nil {
return nil, 0, cerr
}
if qerr != nil {
return nil, 0, qerr
}
return result, count, nil
}
// test +
// UpdateQuestion set new data for question
func (r *QuestionRepository) UpdateQuestion(ctx context.Context, record model.Question) error {
2024-04-16 19:53:12 +00:00
query := `UPDATE question SET`
var params []interface{}
2024-02-19 16:33:15 +00:00
if record.Title != "" {
query += ` title = $%d,`
2024-04-16 19:53:12 +00:00
params = append(params, record.Title)
2024-02-19 16:33:15 +00:00
}
if record.Description != "" {
query += ` description = $%d::text,`
2024-04-16 19:53:12 +00:00
params = append(params, record.Description)
2024-02-19 16:33:15 +00:00
}
if record.Type != "" {
query += ` questiontype = $%d,`
2024-04-16 19:53:12 +00:00
params = append(params, record.Type)
2024-02-19 16:33:15 +00:00
}
if record.Content != "" {
query += ` content = $%d::text,`
2024-04-16 19:53:12 +00:00
params = append(params, record.Content)
2024-02-19 16:33:15 +00:00
}
if record.Page != -1 {
query += ` page = $%d,`
2024-04-16 19:53:12 +00:00
params = append(params, record.Page)
2024-02-19 16:33:15 +00:00
}
query += ` required = $%d, version = $%d WHERE id = $%d`
2024-04-16 19:53:12 +00:00
params = append(params, record.Required, record.Version, record.Id)
var placeholders []any
for i := 1; i <= len(params); i++ {
placeholders = append(placeholders, i)
}
query = fmt.Sprintf(query, placeholders...)
2024-04-16 19:53:12 +00:00
_, err := r.pool.ExecContext(ctx, query, params...)
2024-02-19 16:33:15 +00:00
return err
}
// test +
// DeleteQuestion set question deleted and return deleted question
func (r *QuestionRepository) DeleteQuestion(ctx context.Context, id uint64) (model.Question, error) {
row, err := r.queries.DeleteQuestion(ctx, int64(id))
if err != nil {
return model.Question{}, err
}
result := model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
Title: row.Title,
Description: row.Description.String,
Type: string(row.Questiontype.([]byte)),
Required: row.Required.Bool,
Deleted: row.Deleted.Bool,
Page: int(row.Page.Int16),
Content: row.Content.String,
Version: int(row.Version.Int16),
ParentIds: row.ParentIds,
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
return result, nil
}
// test +
// MoveToHistoryQuestion insert deleted duplicate of question
func (r *QuestionRepository) MoveToHistoryQuestion(ctx context.Context, id uint64) (model.Question, error) {
row, err := r.queries.MoveToHistory(ctx, int64(id))
if err != nil {
return model.Question{}, err
}
result := model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
ParentIds: row.ParentIds,
}
return result, nil
}
// test +
// CopyQuestion method for duplication of question or to copy question to another quiz
func (r *QuestionRepository) CopyQuestion(ctx context.Context, id, quizId uint64) (model.Question, error) {
var record model.Question
if quizId == 0 {
row, err := r.queries.DuplicateQuestion(ctx, int64(id))
if err != nil {
return model.Question{}, err
}
record = model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
} else {
row, err := r.queries.CopyQuestion(ctx, sqlcgen.CopyQuestionParams{
QuizID: int64(quizId),
ID: int64(id),
})
if err != nil {
return model.Question{}, err
}
record = model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
}
return record, nil
}
// test +
// QuestionHistory method for obtaining question history from the database
func (r *QuestionRepository) QuestionHistory(ctx context.Context, id, limit, offset uint64) ([]model.Question, error) {
rows, err := r.queries.GetQuestionHistory(ctx, sqlcgen.GetQuestionHistoryParams{
ID: int64(id),
Limit: int32(limit),
Offset: int32(offset),
})
if err != nil {
return nil, err
}
var result []model.Question
for _, row := range rows {
record := model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
Title: row.Title,
Description: row.Description.String,
Type: string(row.Questiontype.([]byte)),
Required: row.Required.Bool,
Deleted: row.Deleted.Bool,
Page: int(row.Page.Int16),
Content: row.Content.String,
Version: int(row.Version.Int16),
ParentIds: row.ParentIds,
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
result = append(result, record)
}
return result, nil
}
2024-04-16 11:35:25 +00:00
func (r *QuestionRepository) GetMapQuestions(ctx context.Context, allAnswers []model.ResultAnswer) (map[uint64]string, []model.ResultAnswer, error) {
2024-02-19 16:33:15 +00:00
questionMap := make(map[uint64]string)
2024-04-16 11:35:25 +00:00
var questions []QueTitleResp
2024-02-19 16:33:15 +00:00
for _, answer := range allAnswers {
2024-04-16 11:35:25 +00:00
questionData, err := r.GetQuestionTitleByID(ctx, answer.QuestionID)
2024-02-19 16:33:15 +00:00
if err != nil {
2024-04-16 11:35:25 +00:00
return nil, []model.ResultAnswer{}, err
}
if questionData.Type != model.TypeResult {
2024-04-16 12:39:23 +00:00
questionData.AnswerID = answer.AnswerID
2024-04-16 11:35:25 +00:00
questions = append(questions, questionData)
questionMap[answer.AnswerID] = questionData.Title
2024-02-19 16:33:15 +00:00
}
2024-04-16 11:35:25 +00:00
}
sort.Slice(questions, func(i, j int) bool {
return questions[i].Page < questions[j].Page
})
// TODO O2 REFACTOR
var sortedAllAnswers []model.ResultAnswer
for _, que := range questions {
for _, answer := range allAnswers {
if que.AnswerID == answer.AnswerID {
sortedAllAnswers = append(sortedAllAnswers, answer)
}
2024-02-19 16:33:15 +00:00
}
}
2024-04-16 11:35:25 +00:00
return questionMap, sortedAllAnswers, nil
}
type QueTitleResp struct {
Title string
Type string
Page int
AnswerID uint64
2024-02-19 16:33:15 +00:00
}
// test +
2024-04-16 11:35:25 +00:00
func (r *QuestionRepository) GetQuestionTitleByID(ctx context.Context, questionID uint64) (QueTitleResp, error) {
2024-02-19 16:33:15 +00:00
row, err := r.queries.GetQuestionTitle(ctx, int64(questionID))
if err != nil {
2024-04-16 11:35:25 +00:00
return QueTitleResp{}, err
}
resp := QueTitleResp{
Title: row.Title,
Type: string(row.Questiontype.([]byte)),
Page: int(row.Page.Int16),
2024-02-19 16:33:15 +00:00
}
2024-04-16 11:35:25 +00:00
return resp, nil
2024-02-19 16:33:15 +00:00
}
2024-04-16 14:25:44 +00:00
func (r *QuestionRepository) ForSortingResults(ctx context.Context, allAnswers []model.Answer) ([]model.Answer, error) {
var questions []QueTitleResp
for _, answer := range allAnswers {
questionData, err := r.GetQuestionTitleByID(ctx, answer.QuestionId)
if err != nil {
return nil, err
}
questionData.AnswerID = answer.Id
questions = append(questions, questionData)
}
sort.Slice(questions, func(i, j int) bool {
return questions[i].Page < questions[j].Page
})
// TODO O2 REFACTOR
var sortedAllAnswers []model.Answer
for _, que := range questions {
for _, answer := range allAnswers {
if que.AnswerID == answer.Id {
sortedAllAnswers = append(sortedAllAnswers, answer)
}
}
}
return sortedAllAnswers, nil
}
func (r *QuestionRepository) GetQuestionListByIDs(ctx context.Context, ids []int32) ([]model.Question, error) {
rows, err := r.queries.GetQuestionListByIDs(ctx, ids)
if err != nil {
return nil, err
}
var questions []model.Question
for _, row := range rows {
question := model.Question{
Id: uint64(row.ID),
QuizId: uint64(row.QuizID),
Title: row.Title,
Description: row.Description.String,
Type: string(row.Questiontype.([]byte)),
Required: row.Required.Bool,
Deleted: row.Deleted.Bool,
Page: int(row.Page.Int16),
Content: row.Content.String,
Version: int(row.Version.Int16),
ParentIds: row.ParentIds,
CreatedAt: row.CreatedAt.Time,
UpdatedAt: row.UpdatedAt.Time,
}
questions = append(questions, question)
}
return questions, nil
}