393 lines
10 KiB
Go
393 lines
10 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"git.sch9.ru/new_gate/ms-tester/internal/models"
|
|
"github.com/jmoiron/sqlx"
|
|
"strings"
|
|
)
|
|
|
|
type ContestRepository struct {
|
|
db *sqlx.DB
|
|
}
|
|
|
|
func NewContestRepository(db *sqlx.DB) *ContestRepository {
|
|
return &ContestRepository{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
const createContestQuery = "INSERT INTO contests (title) VALUES (?) RETURNING id"
|
|
|
|
func (r *ContestRepository) CreateContest(ctx context.Context, title string) (int32, error) {
|
|
const op = "ContestRepository.CreateContest"
|
|
|
|
query := r.db.Rebind(createContestQuery)
|
|
|
|
rows, err := r.db.QueryxContext(ctx, query, title)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
|
|
defer rows.Close()
|
|
var id int32
|
|
rows.Next()
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
const readContestByIdQuery = "SELECT * from contests WHERE id=? LIMIT 1"
|
|
|
|
func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) {
|
|
const op = "ContestRepository.ReadContestById"
|
|
|
|
var contest models.Contest
|
|
query := r.db.Rebind(readContestByIdQuery)
|
|
err := r.db.GetContext(ctx, &contest, query, id)
|
|
if err != nil {
|
|
return nil, handlePgErr(err, op)
|
|
}
|
|
return &contest, nil
|
|
}
|
|
|
|
const deleteContestQuery = "DELETE FROM contests WHERE id=?"
|
|
|
|
func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error {
|
|
const op = "ContestRepository.DeleteContest"
|
|
|
|
query := r.db.Rebind(deleteContestQuery)
|
|
_, err := r.db.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
return handlePgErr(err, op)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const addTaskQuery = `INSERT INTO tasks (problem_id, contest_id, position)
|
|
VALUES (?, ?, COALESCE((SELECT MAX(position) FROM tasks WHERE contest_id = ?), 0) + 1)
|
|
RETURNING id
|
|
`
|
|
|
|
func (r *ContestRepository) AddTask(ctx context.Context, contestId int32, problemId int32) (int32, error) {
|
|
const op = "ContestRepository.AddTask"
|
|
|
|
query := r.db.Rebind(addTaskQuery)
|
|
rows, err := r.db.QueryxContext(ctx, query, problemId, contestId, contestId)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
defer rows.Close()
|
|
var id int32
|
|
rows.Next()
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
const deleteTaskQuery = "DELETE FROM tasks WHERE id=?"
|
|
|
|
func (r *ContestRepository) DeleteTask(ctx context.Context, taskId int32) error {
|
|
const op = "ContestRepository.DeleteTask"
|
|
|
|
query := r.db.Rebind(deleteTaskQuery)
|
|
_, err := r.db.ExecContext(ctx, query, taskId)
|
|
if err != nil {
|
|
return handlePgErr(err, op)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const addParticipantQuery = "INSERT INTO participants (user_id ,contest_id, name) VALUES (?, ?, ?) RETURNING id"
|
|
|
|
func (r *ContestRepository) AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error) {
|
|
const op = "ContestRepository.AddParticipant"
|
|
|
|
query := r.db.Rebind(addParticipantQuery)
|
|
name := ""
|
|
rows, err := r.db.QueryxContext(ctx, query, contestId, userId, name)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
defer rows.Close()
|
|
var id int32
|
|
rows.Next()
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
const deleteParticipantQuery = "DELETE FROM participants WHERE id=?"
|
|
|
|
func (r *ContestRepository) DeleteParticipant(ctx context.Context, participantId int32) error {
|
|
const op = "ContestRepository.DeleteParticipant"
|
|
|
|
query := r.db.Rebind(deleteParticipantQuery)
|
|
_, err := r.db.ExecContext(ctx, query, participantId)
|
|
if err != nil {
|
|
return handlePgErr(err, op)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const readTasksQuery = `SELECT tasks.id,
|
|
problem_id,
|
|
contest_id,
|
|
position,
|
|
title,
|
|
memory_limit,
|
|
time_limit,
|
|
tasks.created_at,
|
|
tasks.updated_at
|
|
FROM tasks
|
|
INNER JOIN problems ON tasks.problem_id = problems.id
|
|
WHERE contest_id = ? ORDER BY position`
|
|
|
|
func (r *ContestRepository) ReadRichTasks(ctx context.Context, contestId int32) ([]*models.RichTask, error) {
|
|
const op = "ContestRepository.ReadTasks"
|
|
|
|
var tasks []*models.RichTask
|
|
query := r.db.Rebind(readTasksQuery)
|
|
err := r.db.SelectContext(ctx, &tasks, query, contestId)
|
|
if err != nil {
|
|
return nil, handlePgErr(err, op)
|
|
}
|
|
return tasks, nil
|
|
}
|
|
|
|
const (
|
|
readContestsListQuery = `SELECT id, title, created_at, updated_at FROM contests LIMIT ? OFFSET ?`
|
|
countContestsQuery = "SELECT COUNT(*) FROM contests"
|
|
)
|
|
|
|
func (r *ContestRepository) ListContests(ctx context.Context, page int32, pageSize int32) ([]*models.ContestsListItem, int32, error) {
|
|
const op = "ContestRepository.ReadTasks"
|
|
|
|
var tasks []*models.ContestsListItem
|
|
query := r.db.Rebind(readContestsListQuery)
|
|
err := r.db.SelectContext(ctx, &tasks, query, pageSize, (page-1)*pageSize)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
query = r.db.Rebind(countContestsQuery)
|
|
var count int32
|
|
err = r.db.GetContext(ctx, &count, query)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
return tasks, count, nil
|
|
}
|
|
|
|
const (
|
|
readParticipantsListQuery = `SELECT id, user_id, name, created_at, updated_at FROM participants WHERE contest_id = ? LIMIT ? OFFSET ?`
|
|
countParticipantsQuery = "SELECT COUNT(*) FROM participants WHERE contest_id = ?"
|
|
)
|
|
|
|
func (r *ContestRepository) ListParticipants(ctx context.Context, contestId int32, page int32, pageSize int32) ([]*models.ParticipantsListItem, int32, error) {
|
|
const op = "ContestRepository.ReadParticipants"
|
|
|
|
if pageSize > 20 {
|
|
pageSize = 1
|
|
}
|
|
|
|
var participants []*models.ParticipantsListItem
|
|
query := r.db.Rebind(readParticipantsListQuery)
|
|
err := r.db.SelectContext(ctx, &participants, query, contestId, pageSize, (page-1)*pageSize)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
query = r.db.Rebind(countParticipantsQuery)
|
|
var count int32
|
|
err = r.db.GetContext(ctx, &count, query, contestId)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
return participants, count, nil
|
|
}
|
|
|
|
const (
|
|
updateContestQuery = "UPDATE contests SET title = COALESCE(?, title) WHERE id = ?"
|
|
)
|
|
|
|
func (r *ContestRepository) UpdateContest(ctx context.Context, id int32, contestUpdate models.ContestUpdate) error {
|
|
const op = "ContestRepository.UpdateContest"
|
|
|
|
query := r.db.Rebind(updateContestQuery)
|
|
_, err := r.db.ExecContext(ctx, query, contestUpdate.Title, id)
|
|
if err != nil {
|
|
return handlePgErr(err, op)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
updateParticipantQuery = "UPDATE participants SET name = COALESCE(?, name) WHERE id = ?"
|
|
)
|
|
|
|
func (r *ContestRepository) UpdateParticipant(ctx context.Context, id int32, participantUpdate models.ParticipantUpdate) error {
|
|
const op = "ContestRepository.UpdateParticipant"
|
|
|
|
query := r.db.Rebind(updateParticipantQuery)
|
|
_, err := r.db.ExecContext(ctx, query, participantUpdate.Name, id)
|
|
if err != nil {
|
|
return handlePgErr(err, op)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
readSolutionQuery = "SELECT * FROM solutions WHERE id = ?"
|
|
)
|
|
|
|
func (r *ContestRepository) ReadSolution(ctx context.Context, id int32) (*models.Solution, error) {
|
|
const op = "ContestRepository.ReadSolution"
|
|
|
|
query := r.db.Rebind(readSolutionQuery)
|
|
var solution models.Solution
|
|
err := r.db.GetContext(ctx, &solution, query, id)
|
|
if err != nil {
|
|
return nil, handlePgErr(err, op)
|
|
}
|
|
|
|
return &solution, nil
|
|
}
|
|
|
|
const (
|
|
createSolutionQuery = `INSERT INTO solutions (task_id, participant_id, language, penalty, solution)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
RETURNING id`
|
|
)
|
|
|
|
func (r *ContestRepository) CreateSolution(ctx context.Context, creation *models.SolutionCreation) (int32, error) {
|
|
const op = "ContestRepository.CreateSolution"
|
|
|
|
query := r.db.Rebind(createSolutionQuery)
|
|
|
|
rows, err := r.db.QueryxContext(ctx,
|
|
query,
|
|
creation.TaskId,
|
|
creation.ParticipantId,
|
|
creation.Language,
|
|
creation.Penalty,
|
|
creation.Solution,
|
|
)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
|
|
defer rows.Close()
|
|
var id int32
|
|
rows.Next()
|
|
err = rows.Scan(&id)
|
|
if err != nil {
|
|
return 0, handlePgErr(err, op)
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func (r *ContestRepository) ListSolutions(ctx context.Context, filters models.SolutionsFilter) ([]*models.SolutionsListItem, int32, error) {
|
|
const op = "ContestRepository.ListSolutions"
|
|
|
|
baseQuery := `
|
|
SELECT
|
|
s.id,
|
|
s.task_id,
|
|
t.contest_id,
|
|
s.participant_id,
|
|
s.state,
|
|
s.score,
|
|
s.penalty,
|
|
s.total_score,
|
|
s.language,
|
|
s.updated_at,
|
|
s.created_at
|
|
FROM solutions s
|
|
LEFT JOIN tasks t ON s.task_id = t.id
|
|
WHERE 1=1
|
|
`
|
|
|
|
var conditions []string
|
|
var args []interface{}
|
|
|
|
if filters.ContestId != nil {
|
|
conditions = append(conditions, "contest_id = ?")
|
|
args = append(args, *filters.ContestId)
|
|
}
|
|
if filters.ParticipantId != nil {
|
|
conditions = append(conditions, "participant_id = ?")
|
|
args = append(args, *filters.ParticipantId)
|
|
}
|
|
if filters.TaskId != nil {
|
|
conditions = append(conditions, "task_id = ?")
|
|
args = append(args, *filters.TaskId)
|
|
}
|
|
if filters.Language != nil {
|
|
conditions = append(conditions, "language = ?")
|
|
args = append(args, *filters.Language)
|
|
}
|
|
if filters.State != nil {
|
|
conditions = append(conditions, "state = ?")
|
|
args = append(args, *filters.State)
|
|
}
|
|
|
|
if len(conditions) > 0 {
|
|
baseQuery += " AND " + strings.Join(conditions, " AND ")
|
|
}
|
|
|
|
if filters.Order != nil {
|
|
orderDirection := "ASC"
|
|
if *filters.Order < 0 {
|
|
orderDirection = "DESC"
|
|
}
|
|
baseQuery += fmt.Sprintf(" ORDER BY s.id %s", orderDirection)
|
|
}
|
|
|
|
countQuery := "SELECT COUNT(*) FROM (" + baseQuery + ") as count_table"
|
|
var totalCount int32
|
|
err := r.db.QueryRowxContext(ctx, r.db.Rebind(countQuery), args...).Scan(&totalCount)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
offset := (filters.Page - 1) * filters.PageSize
|
|
baseQuery += " LIMIT ? OFFSET ?"
|
|
args = append(args, filters.PageSize, offset)
|
|
|
|
rows, err := r.db.QueryxContext(ctx, r.db.Rebind(baseQuery), args...)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var solutions []*models.SolutionsListItem
|
|
for rows.Next() {
|
|
var solution models.SolutionsListItem
|
|
err = rows.StructScan(&solution)
|
|
if err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
solutions = append(solutions, &solution)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, 0, handlePgErr(err, op)
|
|
}
|
|
|
|
return solutions, totalCount, nil
|
|
}
|