580 lines
15 KiB
Go
580 lines
15 KiB
Go
package quiz
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/google/uuid"
|
|
"github.com/lib/pq"
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal/sqlcgen"
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type Deps struct {
|
|
Queries *sqlcgen.Queries
|
|
Pool *sql.DB
|
|
}
|
|
|
|
type QuizRepository struct {
|
|
queries *sqlcgen.Queries
|
|
pool *sql.DB
|
|
}
|
|
|
|
func NewQuizRepository(deps Deps) *QuizRepository {
|
|
return &QuizRepository{
|
|
queries: deps.Queries,
|
|
pool: deps.Pool,
|
|
}
|
|
}
|
|
|
|
// test +
|
|
func (r *QuizRepository) CreateQuiz(ctx context.Context, record *model.Quiz) error {
|
|
if record.Qid == "" {
|
|
record.Qid = uuid.NewString()
|
|
}
|
|
|
|
params := sqlcgen.InsertQuizParams{
|
|
Accountid: record.AccountId,
|
|
Fingerprinting: sql.NullBool{Bool: record.Fingerprinting, Valid: true},
|
|
Repeatable: sql.NullBool{Bool: record.Repeatable, Valid: true},
|
|
NotePrevented: sql.NullBool{Bool: record.NotePrevented, Valid: true},
|
|
MailNotifications: sql.NullBool{Bool: record.MailNotifications, Valid: true},
|
|
UniqueAnswers: sql.NullBool{Bool: record.UniqueAnswers, Valid: true},
|
|
Super: sql.NullBool{Bool: record.Super, Valid: true},
|
|
GroupID: sql.NullInt64{Int64: int64(record.GroupId), Valid: true},
|
|
Name: sql.NullString{String: record.Name, Valid: true},
|
|
Description: sql.NullString{String: record.Description, Valid: true},
|
|
Config: sql.NullString{String: record.Config, Valid: true},
|
|
Status: record.Status,
|
|
LimitAnswers: sql.NullInt32{Int32: int32(record.Limit), Valid: true},
|
|
DueTo: sql.NullInt32{Int32: int32(record.DueTo), Valid: true},
|
|
TimeOfPassing: sql.NullInt32{Int32: int32(record.TimeOfPassing), Valid: true},
|
|
Pausable: sql.NullBool{Bool: record.Pausable, Valid: true},
|
|
ParentIds: record.ParentIds,
|
|
QuestionsCount: sql.NullInt32{Int32: int32(record.QuestionsCount), Valid: true},
|
|
Qid: uuid.NullUUID{UUID: uuid.MustParse(record.Qid), Valid: true},
|
|
}
|
|
|
|
data, err := r.queries.InsertQuiz(ctx, params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
record.Id = uint64(data.ID)
|
|
record.CreatedAt = data.CreatedAt.Time
|
|
record.UpdatedAt = data.UpdatedAt.Time
|
|
record.Qid = data.Qid.UUID.String()
|
|
|
|
return nil
|
|
}
|
|
|
|
type GetQuizListDeps struct {
|
|
Limit, Offset, From, To, Group uint64
|
|
Deleted, Archived, Super bool
|
|
Search, Status, AccountId string
|
|
}
|
|
|
|
// test +
|
|
// GetQuizList function for get data page from db
|
|
func (r *QuizRepository) GetQuizList(
|
|
ctx context.Context,
|
|
deps GetQuizListDeps) ([]model.Quiz, uint64, error) {
|
|
query := `
|
|
SELECT * FROM quiz
|
|
%s
|
|
ORDER BY created_at DESC
|
|
LIMIT $1 OFFSET $2;
|
|
`
|
|
|
|
queryCnt := `SELECT count(1) FROM quiz %s;`
|
|
|
|
var (
|
|
whereClause []string
|
|
data []interface{}
|
|
)
|
|
|
|
whereClause = append(whereClause, fmt.Sprintf(`accountid = '%s'`, deps.AccountId))
|
|
if deps.From != 0 {
|
|
data = append(data, deps.From)
|
|
whereClause = append(whereClause, fmt.Sprintf("created_at >= to_timestamp($%d)", len(data)))
|
|
}
|
|
if deps.To != 0 {
|
|
data = append(data, deps.To)
|
|
whereClause = append(whereClause, fmt.Sprintf("created_at <= to_timestamp($%d)", len(data)))
|
|
}
|
|
if deps.Deleted {
|
|
whereClause = append(whereClause, fmt.Sprintf("deleted = true"))
|
|
} else {
|
|
whereClause = append(whereClause, fmt.Sprintf("deleted = false"))
|
|
}
|
|
if deps.Archived {
|
|
whereClause = append(whereClause, fmt.Sprintf("archived = true"))
|
|
} else {
|
|
whereClause = append(whereClause, fmt.Sprintf("archived = false"))
|
|
}
|
|
|
|
if deps.Super {
|
|
whereClause = append(whereClause, fmt.Sprintf("super = true"))
|
|
}
|
|
|
|
if deps.Group > 0 {
|
|
whereClause = append(whereClause, fmt.Sprintf("group_id = %d", deps.Group))
|
|
}
|
|
|
|
if deps.Status != "" {
|
|
data = append(data, deps.Status)
|
|
whereClause = append(whereClause, fmt.Sprintf("status = $%d", len(data)))
|
|
}
|
|
if deps.Search != "" {
|
|
data = append(data, deps.Search)
|
|
whereClause = append(whereClause, fmt.Sprintf("to_tsvector('russian', name) @@ to_tsquery('russian', $%d)", len(data)))
|
|
}
|
|
|
|
if len(whereClause) != 0 {
|
|
query = fmt.Sprintf(query, fmt.Sprintf(" WHERE %s ", strings.Join(whereClause, " AND ")))
|
|
queryCnt = fmt.Sprintf(queryCnt, fmt.Sprintf(" WHERE %s ", strings.Join(whereClause, " AND ")))
|
|
} else {
|
|
query = fmt.Sprintf(query, "")
|
|
queryCnt = fmt.Sprintf(queryCnt, "")
|
|
}
|
|
|
|
var (
|
|
qerr, cerr error
|
|
count uint64
|
|
result []model.Quiz
|
|
)
|
|
|
|
data = append(data, deps.Limit, deps.Offset)
|
|
wg := sync.WaitGroup{}
|
|
wg.Add(2)
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
fmt.Println("Q1", query, deps.Limit, deps.Offset)
|
|
rows, err := r.pool.QueryContext(ctx, query, deps.Limit, deps.Offset)
|
|
if err != nil {
|
|
qerr = err
|
|
return
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
var piece model.Quiz
|
|
pIds := pq.Int32Array{}
|
|
for rows.Next() {
|
|
if err := rows.Scan(
|
|
&piece.Id,
|
|
&piece.Qid,
|
|
&piece.AccountId,
|
|
&piece.Deleted,
|
|
&piece.Archived,
|
|
&piece.Fingerprinting,
|
|
&piece.Repeatable,
|
|
&piece.NotePrevented,
|
|
&piece.MailNotifications,
|
|
&piece.UniqueAnswers,
|
|
&piece.Super,
|
|
&piece.GroupId,
|
|
&piece.Name,
|
|
&piece.Description,
|
|
&piece.Config,
|
|
&piece.Status,
|
|
&piece.Limit,
|
|
&piece.DueTo,
|
|
&piece.TimeOfPassing,
|
|
&piece.Pausable,
|
|
&piece.Version,
|
|
&piece.VersionComment,
|
|
&pIds,
|
|
&piece.CreatedAt,
|
|
&piece.UpdatedAt,
|
|
&piece.QuestionsCount,
|
|
&piece.PassedCount,
|
|
&piece.AverageTime,
|
|
&piece.SessionCount,
|
|
); err != nil {
|
|
qerr = err
|
|
return
|
|
}
|
|
piece.ParentIds = pIds
|
|
result = append(result, piece)
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
defer wg.Done()
|
|
|
|
fmt.Println("Q2", queryCnt)
|
|
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 !rows.Next() {
|
|
cerr = errors.New("can not next count")
|
|
}
|
|
|
|
if err := rows.Scan(&count); err != nil {
|
|
cerr = err
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
fmt.Println("res", result, count, "!", cerr, "?", qerr)
|
|
if cerr != nil {
|
|
return nil, 0, cerr
|
|
}
|
|
if qerr != nil {
|
|
return nil, 0, qerr
|
|
}
|
|
return result, count, nil
|
|
}
|
|
|
|
// test +
|
|
// GetQuizByQid method for obtain quiz model by secured id
|
|
func (r *QuizRepository) GetQuizByQid(ctx context.Context, qid string) (model.Quiz, error) {
|
|
fmt.Println("QUID", `
|
|
SELECT * FROM quiz
|
|
WHERE
|
|
deleted = false AND
|
|
archived = false AND
|
|
status = 'start' AND
|
|
qid = $1;
|
|
`)
|
|
rows, err := r.pool.QueryContext(ctx, `
|
|
SELECT * FROM quiz
|
|
WHERE
|
|
deleted = false AND
|
|
archived = false AND
|
|
status = 'start' AND
|
|
qid = $1;
|
|
`, qid)
|
|
if err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
if !rows.Next() {
|
|
return model.Quiz{}, rows.Err()
|
|
}
|
|
|
|
var piece model.Quiz
|
|
pIds := pq.Int32Array{}
|
|
if err := rows.Scan(
|
|
&piece.Id,
|
|
&piece.Qid,
|
|
&piece.AccountId,
|
|
&piece.Deleted,
|
|
&piece.Archived,
|
|
&piece.Fingerprinting,
|
|
&piece.Repeatable,
|
|
&piece.NotePrevented,
|
|
&piece.MailNotifications,
|
|
&piece.UniqueAnswers,
|
|
&piece.Super,
|
|
&piece.GroupId,
|
|
&piece.Name,
|
|
&piece.Description,
|
|
&piece.Config,
|
|
&piece.Status,
|
|
&piece.Limit,
|
|
&piece.DueTo,
|
|
&piece.TimeOfPassing,
|
|
&piece.Pausable,
|
|
&piece.Version,
|
|
&piece.VersionComment,
|
|
&pIds,
|
|
&piece.CreatedAt,
|
|
&piece.UpdatedAt,
|
|
&piece.QuestionsCount,
|
|
&piece.PassedCount,
|
|
&piece.AverageTime,
|
|
&piece.SessionCount,
|
|
); err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
|
|
piece.ParentIds = pIds
|
|
|
|
return piece, nil
|
|
}
|
|
|
|
// test +
|
|
func (r *QuizRepository) DeleteQuiz(ctx context.Context, accountId string, id uint64) (model.Quiz, error) {
|
|
row, err := r.queries.DeleteQuizByID(ctx, sqlcgen.DeleteQuizByIDParams{
|
|
ID: int64(id),
|
|
Accountid: accountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
|
|
piece := model.Quiz{
|
|
Id: uint64(row.ID),
|
|
Qid: row.Qid.UUID.String(),
|
|
AccountId: row.Accountid,
|
|
Deleted: row.Deleted.Bool,
|
|
Archived: row.Archived.Bool,
|
|
Fingerprinting: row.Fingerprinting.Bool,
|
|
Repeatable: row.Repeatable.Bool,
|
|
NotePrevented: row.NotePrevented.Bool,
|
|
MailNotifications: row.MailNotifications.Bool,
|
|
UniqueAnswers: row.UniqueAnswers.Bool,
|
|
Super: row.Super.Bool,
|
|
GroupId: uint64(row.GroupID.Int64),
|
|
Name: row.Name.String,
|
|
Description: row.Description.String,
|
|
Config: row.Config.String,
|
|
Status: string(row.Status.([]byte)),
|
|
Limit: uint64(row.LimitAnswers.Int32),
|
|
DueTo: uint64(row.DueTo.Int32),
|
|
TimeOfPassing: uint64(row.TimeOfPassing.Int32),
|
|
Pausable: row.Pausable.Bool,
|
|
Version: int(row.Version.Int16),
|
|
VersionComment: row.VersionComment.String,
|
|
ParentIds: row.ParentIds,
|
|
CreatedAt: row.CreatedAt.Time,
|
|
UpdatedAt: row.UpdatedAt.Time,
|
|
QuestionsCount: uint64(row.QuestionsCount.Int32),
|
|
PassedCount: uint64(row.AnswersCount.Int32),
|
|
AverageTime: uint64(row.AverageTimePassing.Int32),
|
|
SessionCount: uint64(row.SessionsCount.Int32),
|
|
}
|
|
|
|
return piece, nil
|
|
}
|
|
|
|
// test +
|
|
// MoveToHistoryQuiz insert deleted duplicate of quiz
|
|
func (r *QuizRepository) MoveToHistoryQuiz(ctx context.Context, id uint64, accountId string) (model.Quiz, error) {
|
|
row, err := r.queries.MoveToHistoryQuiz(ctx, sqlcgen.MoveToHistoryQuizParams{
|
|
ID: int64(id),
|
|
Accountid: accountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
|
|
result := model.Quiz{
|
|
Id: uint64(row.ID),
|
|
Qid: row.Qid.UUID.String(),
|
|
ParentIds: row.ParentIds,
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// test +
|
|
// UpdateQuiz set new data for quiz
|
|
func (r *QuizRepository) UpdateQuiz(ctx context.Context, accountId string, record model.Quiz) error {
|
|
query := `UPDATE quiz
|
|
SET %s
|
|
WHERE id=$1 AND accountid=$2;`
|
|
|
|
var values []string
|
|
|
|
if record.Name != "" {
|
|
values = append(values, fmt.Sprintf(` name='%s' `, record.Name))
|
|
}
|
|
|
|
if record.Description != "" {
|
|
values = append(values, fmt.Sprintf(` description='%s' `, record.Description))
|
|
}
|
|
|
|
if record.Status != "" {
|
|
values = append(values, fmt.Sprintf(` status='%s' `, record.Status))
|
|
}
|
|
|
|
values = append(values, fmt.Sprintf(`group_id=%d `, record.GroupId), fmt.Sprintf(` version=%d `, record.Version))
|
|
|
|
if record.Config != "" {
|
|
values = append(values, fmt.Sprintf(` config='%s' `, record.Config))
|
|
}
|
|
|
|
fmt.Println("UPQUI", fmt.Sprintf(query, strings.Join(values, ",")))
|
|
_, err := r.pool.ExecContext(ctx, fmt.Sprintf(query, strings.Join(values, ",")), record.Id, accountId)
|
|
|
|
return err
|
|
}
|
|
|
|
// test +
|
|
// CopyQuiz method for copy quiz with all of his questions
|
|
func (r *QuizRepository) CopyQuiz(ctx context.Context, accountId string, id uint64) (model.Quiz, error) {
|
|
row, err := r.queries.CopyQuiz(ctx, sqlcgen.CopyQuizParams{
|
|
ID: int64(id),
|
|
Accountid: accountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
|
|
result := model.Quiz{
|
|
Id: uint64(row.ID),
|
|
Qid: row.Qid.UUID.String(),
|
|
CreatedAt: row.CreatedAt.Time,
|
|
UpdatedAt: row.UpdatedAt.Time,
|
|
}
|
|
|
|
err = r.queries.CopyQuizQuestions(ctx, sqlcgen.CopyQuizQuestionsParams{
|
|
QuizID: int64(id),
|
|
QuizID_2: row.ID,
|
|
})
|
|
if err != nil {
|
|
return model.Quiz{}, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
type QuizHistoryDeps struct {
|
|
Id, Limit, Offset uint64
|
|
AccountId string
|
|
}
|
|
|
|
// test +
|
|
// QuizHistory method for obtain quiz history from db
|
|
func (r *QuizRepository) QuizHistory(ctx context.Context, deps QuizHistoryDeps) ([]model.Quiz, error) {
|
|
rows, err := r.queries.GetQuizHistory(ctx, sqlcgen.GetQuizHistoryParams{
|
|
ID: int64(deps.Id),
|
|
Limit: int32(deps.Limit),
|
|
Offset: int32(deps.Offset),
|
|
Accountid: deps.AccountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []model.Quiz
|
|
for _, row := range rows {
|
|
piece := model.Quiz{
|
|
Id: uint64(row.ID),
|
|
Qid: row.Qid.UUID.String(),
|
|
AccountId: row.Accountid,
|
|
Deleted: row.Deleted.Bool,
|
|
Archived: row.Archived.Bool,
|
|
Fingerprinting: row.Fingerprinting.Bool,
|
|
Repeatable: row.Repeatable.Bool,
|
|
NotePrevented: row.NotePrevented.Bool,
|
|
MailNotifications: row.MailNotifications.Bool,
|
|
UniqueAnswers: row.UniqueAnswers.Bool,
|
|
Super: row.Super.Bool,
|
|
GroupId: uint64(row.GroupID.Int64),
|
|
Name: row.Name.String,
|
|
Description: row.Description.String,
|
|
Config: row.Config.String,
|
|
Status: string(row.Status.([]byte)),
|
|
Limit: uint64(row.LimitAnswers.Int32),
|
|
DueTo: uint64(row.DueTo.Int32),
|
|
TimeOfPassing: uint64(row.TimeOfPassing.Int32),
|
|
Pausable: row.Pausable.Bool,
|
|
Version: int(row.Version.Int16),
|
|
VersionComment: row.VersionComment.String,
|
|
ParentIds: row.ParentIds,
|
|
CreatedAt: row.CreatedAt.Time,
|
|
UpdatedAt: row.UpdatedAt.Time,
|
|
QuestionsCount: uint64(row.QuestionsCount.Int32),
|
|
PassedCount: uint64(row.AnswersCount.Int32),
|
|
AverageTime: uint64(row.AverageTimePassing.Int32),
|
|
SessionCount: uint64(row.SessionsCount.Int32),
|
|
}
|
|
|
|
result = append(result, piece)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *QuizRepository) ArchiveQuiz(ctx context.Context, accountId string, id uint64) error {
|
|
err := r.queries.ArchiveQuiz(ctx, sqlcgen.ArchiveQuizParams{
|
|
ID: int64(id),
|
|
Accountid: accountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// test +
|
|
func (r *QuizRepository) GetQuizById(ctx context.Context, accountId string, id uint64) (*model.Quiz, error) {
|
|
row, err := r.queries.GetQuizById(ctx, sqlcgen.GetQuizByIdParams{
|
|
ID: int64(id),
|
|
Accountid: accountId,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
piece := model.Quiz{
|
|
Id: uint64(row.ID),
|
|
Qid: row.Qid.UUID.String(),
|
|
AccountId: row.Accountid,
|
|
Deleted: row.Deleted.Bool,
|
|
Archived: row.Archived.Bool,
|
|
Fingerprinting: row.Fingerprinting.Bool,
|
|
Repeatable: row.Repeatable.Bool,
|
|
NotePrevented: row.NotePrevented.Bool,
|
|
MailNotifications: row.MailNotifications.Bool,
|
|
UniqueAnswers: row.UniqueAnswers.Bool,
|
|
Super: row.Super.Bool,
|
|
GroupId: uint64(row.GroupID.Int64),
|
|
Name: row.Name.String,
|
|
Description: row.Description.String,
|
|
Config: row.Config.String,
|
|
Status: string(row.Status.([]byte)),
|
|
Limit: uint64(row.LimitAnswers.Int32),
|
|
DueTo: uint64(row.DueTo.Int32),
|
|
TimeOfPassing: uint64(row.TimeOfPassing.Int32),
|
|
Pausable: row.Pausable.Bool,
|
|
Version: int(row.Version.Int16),
|
|
VersionComment: row.VersionComment.String,
|
|
ParentIds: row.ParentIds,
|
|
CreatedAt: row.CreatedAt.Time,
|
|
UpdatedAt: row.UpdatedAt.Time,
|
|
QuestionsCount: uint64(row.QuestionsCount.Int32),
|
|
PassedCount: uint64(row.AnswersCount.Int32),
|
|
AverageTime: uint64(row.AverageTimePassing.Int32),
|
|
SessionCount: uint64(row.SessionsCount.Int32),
|
|
}
|
|
|
|
return &piece, nil
|
|
}
|
|
|
|
// test +
|
|
func (r *QuizRepository) GetQuizConfig(ctx context.Context, quizID uint64) (model.QuizConfig, string, error) {
|
|
row, err := r.queries.GetQuizConfig(ctx, int64(quizID))
|
|
|
|
if err != nil {
|
|
return model.QuizConfig{}, "", err
|
|
}
|
|
|
|
var config model.QuizConfig
|
|
if err := json.Unmarshal([]byte(row.Config.String), &config); err != nil {
|
|
return model.QuizConfig{}, "", err
|
|
}
|
|
|
|
return config, row.Accountid, nil
|
|
}
|