package repository import ( "context" "git.sch9.ru/new_gate/ms-tester/internal/models" "git.sch9.ru/new_gate/ms-tester/pkg" sq "github.com/Masterminds/squirrel" ) const ( GetSolutionQuery = "SELECT * FROM solutions WHERE id = $1" ) func (r *Repository) GetSolution(ctx context.Context, id int32) (*models.Solution, error) { const op = "Repository.GetSolution" var solution models.Solution err := r.db.GetContext(ctx, &solution, GetSolutionQuery, id) if err != nil { return nil, pkg.HandlePgErr(err, op) } return &solution, nil } const ( CreateSolutionQuery = `INSERT INTO solutions (task_id, participant_id, language, penalty, solution) VALUES ($1, $2, $3, $4, $5) RETURNING id` ) func (r *Repository) CreateSolution(ctx context.Context, creation *models.SolutionCreation) (int32, error) { const op = "Repository.CreateSolution" rows, err := r.db.QueryxContext(ctx, CreateSolutionQuery, creation.TaskId, creation.ParticipantId, creation.Language, creation.Penalty, creation.Solution, ) if err != nil { return 0, pkg.HandlePgErr(err, op) } defer rows.Close() var id int32 rows.Next() err = rows.Scan(&id) if err != nil { return 0, pkg.HandlePgErr(err, op) } return id, nil } func buildListSolutionsQueries(filter models.SolutionsFilter) (sq.SelectBuilder, sq.SelectBuilder) { columns := []string{ "s.id", "s.participant_id", "p2.name AS participant_name", "s.state", "s.score", "s.penalty", "s.time_stat", "s.memory_stat", "s.language", "s.task_id", "t.position AS task_position", "p.title AS task_title", "t.contest_id", "c.title", "s.updated_at", "s.created_at", } qb := sq.StatementBuilder.PlaceholderFormat(sq.Dollar).Select(columns...). From("solutions s"). LeftJoin("tasks t ON s.task_id = t.id"). LeftJoin("problems p ON t.problem_id = p.id"). LeftJoin("contests c ON t.contest_id = c.id"). LeftJoin("participants p2 ON s.participant_id = p2.id") if filter.ContestId != nil { qb = qb.Where(sq.Eq{"s.contest_id": *filter.ContestId}) } if filter.ParticipantId != nil { qb = qb.Where(sq.Eq{"s.participant_id": *filter.ParticipantId}) } if filter.TaskId != nil { qb = qb.Where(sq.Eq{"s.task_id": *filter.TaskId}) } if filter.Language != nil { qb = qb.Where(sq.Eq{"s.language": *filter.Language}) } if filter.State != nil { qb = qb.Where(sq.Eq{"s.state": *filter.State}) } countQb := sq.Select("COUNT(*)").FromSelect(qb, "sub") if filter.Order != nil && *filter.Order < 0 { qb = qb.OrderBy("s.id DESC") } else { qb = qb.OrderBy("s.id ASC") } qb = qb.Limit(uint64(filter.PageSize)).Offset(uint64(filter.Offset())) return qb, countQb } func (r *Repository) ListSolutions(ctx context.Context, filter models.SolutionsFilter) (*models.SolutionsList, error) { const op = "ContestRepository.ListSolutions" baseQb, countQb := buildListSolutionsQueries(filter) query, args, err := countQb.ToSql() if err != nil { return nil, pkg.HandlePgErr(err, op) } var totalCount int32 err = r.db.GetContext(ctx, &totalCount, query, args...) if err != nil { return nil, pkg.HandlePgErr(err, op) } query, args, err = baseQb.ToSql() if err != nil { return nil, pkg.HandlePgErr(err, op) } rows, err := r.db.QueryxContext(ctx, query, args...) if err != nil { return nil, pkg.HandlePgErr(err, op) } defer rows.Close() solutions := make([]*models.SolutionsListItem, 0) for rows.Next() { var solution models.SolutionsListItem err = rows.StructScan(&solution) if err != nil { return nil, pkg.HandlePgErr(err, op) } solutions = append(solutions, &solution) } if err = rows.Err(); err != nil { return nil, pkg.HandlePgErr(err, op) } return &models.SolutionsList{ Solutions: solutions, Pagination: models.Pagination{ Total: models.Total(totalCount, filter.PageSize), Page: filter.Page, }, }, nil } const ( // state=5 - AC GetBestSolutions = ` WITH contest_tasks AS ( SELECT t.id AS task_id, t.position AS task_position, t.contest_id, t.problem_id, t.created_at, t.updated_at, p.title AS task_title, c.title AS contest_title FROM tasks t LEFT JOIN problems p ON p.id = t.problem_id LEFT JOIN contests c ON c.id = t.contest_id WHERE t.contest_id = ? ), best_solutions AS ( SELECT DISTINCT ON (s.task_id) * FROM solutions s WHERE s.participant_id = ? ORDER BY s.task_id, s.score DESC, s.created_at DESC ) SELECT s.id, s.participant_id, p.name AS participant_name, s.state, s.score, s.penalty, s.time_stat, s.memory_stat, s.language, ct.task_id, ct.task_position, ct.task_title, ct.contest_id, ct.contest_title, s.updated_at, s.created_at FROM contest_tasks ct LEFT JOIN best_solutions s ON s.task_id = ct.task_id LEFT JOIN participants p ON p.id = s.participant_id WHERE s.id IS NOT NULL ORDER BY ct.task_position ` ) func (r *Repository) GetBestSolutions(ctx context.Context, contestId int32, participantId int32) ([]*models.SolutionsListItem, error) { const op = "Repository.GetBestSolutions" var solutions []*models.SolutionsListItem query := r.db.Rebind(GetBestSolutions) err := r.db.SelectContext(ctx, &solutions, query, contestId, participantId) if err != nil { return nil, pkg.HandlePgErr(err, op) } return solutions, nil }