feat(tester): add endpoints

add GetMonitor&GetTask endpoints
This commit is contained in:
Vyacheslav1557 2025-03-28 01:17:53 +05:00
parent ef696d2836
commit b960a923d2
9 changed files with 400 additions and 16 deletions

View file

@ -391,3 +391,184 @@ func (r *ContestRepository) ListSolutions(ctx context.Context, filters models.So
return solutions, totalCount, nil
}
const (
readTaskQuery = `
SELECT
t.id,
t.position,
p.title,
p.time_limit,
p.memory_limit,
t.problem_id,
t.contest_id,
p.legend_html,
p.input_format_html,
p.output_format_html,
p.notes_html,
p.scoring_html,
t.created_at,
t.updated_at
FROM tasks t
LEFT JOIN problems p ON t.problem_id = p.id
WHERE t.id = ?
`
)
func (r *ContestRepository) ReadTask(ctx context.Context, id int32) (*models.Task, error) {
const op = "ContestRepository.ReadTask"
query := r.db.Rebind(readTaskQuery)
var task models.Task
err := r.db.GetContext(ctx, &task, query, id)
if err != nil {
return nil, handlePgErr(err, op)
}
return &task, nil
}
const (
// state=5 - AC
readStatisticsQuery = `
SELECT t.id as task_id,
t.position,
COUNT(*) as total,
COUNT(CASE WHEN s.state = 5 THEN 1 END) as success
FROM tasks t
LEFT JOIN solutions s ON t.id = s.task_id
WHERE t.contest_id = ?
GROUP BY t.id, t.position
ORDER BY t.position;
`
solutionsQuery = `
WITH RankedSolutions AS (
SELECT
s.id,
s.task_id,
s.participant_id,
s.state,
s.score,
s.penalty,
s.total_score,
s.language,
s.created_at,
s.updated_at,
t.contest_id,
ROW_NUMBER() OVER (
PARTITION BY s.participant_id, s.task_id
ORDER BY
CASE WHEN s.state = 5 THEN 0 ELSE 1 END,
s.created_at
) as rn
FROM solutions s
JOIN tasks t ON s.task_id = t.id
WHERE t.contest_id = ?
)
SELECT
rs.id,
rs.task_id,
rs.contest_id,
rs.participant_id,
rs.state,
rs.score,
rs.penalty,
rs.total_score,
rs.language,
rs.created_at,
rs.updated_at
FROM RankedSolutions rs
WHERE rs.rn = 1
`
participantsQuery = `
WITH Attempts AS (
SELECT
s.participant_id,
s.task_id,
COUNT(*) FILTER (WHERE s.state != 5 AND s.created_at < (
SELECT MIN(s2.created_at)
FROM solutions s2
WHERE s2.participant_id = s.participant_id
AND s2.task_id = s.task_id
AND s2.state = 5
)) as failed_attempts,
MIN(CASE WHEN s.state = 5 THEN s.penalty END) as success_penalty
FROM solutions s
JOIN tasks t ON t.id = s.task_id
WHERE t.contest_id = :contest_id
GROUP BY s.participant_id, s.task_id
)
SELECT
p.id,
p.name,
COUNT(DISTINCT CASE WHEN a.success_penalty IS NOT NULL THEN a.task_id END) as solved_in_total,
COALESCE(SUM(CASE WHEN a.success_penalty IS NOT NULL
THEN a.failed_attempts * :penalty + a.success_penalty
ELSE 0 END), 0) as penalty_in_total
FROM participants p
LEFT JOIN Attempts a ON a.participant_id = p.id
WHERE p.contest_id = :contest_id
GROUP BY p.id, p.name
`
)
func (r *ContestRepository) ReadMonitor(ctx context.Context, contestId int32) (*models.Monitor, error) {
const op = "ContestRepository.ReadMonitor"
query := r.db.Rebind(readStatisticsQuery)
rows, err := r.db.QueryxContext(ctx, query, contestId)
if err != nil {
return nil, handlePgErr(err, op)
}
defer rows.Close()
var monitor models.Monitor
for rows.Next() {
var stat models.ProblemStatSummary
err = rows.StructScan(&stat)
if err != nil {
return nil, handlePgErr(err, op)
}
monitor.Summary = append(monitor.Summary, &stat)
}
var solutions []*models.SolutionsListItem
err = r.db.SelectContext(ctx, &solutions, r.db.Rebind(solutionsQuery), contestId)
if err != nil {
return nil, handlePgErr(err, op)
}
penalty := int32(20) // FIXME
namedQuery := r.db.Rebind(participantsQuery)
rows3, err := r.db.NamedQueryContext(ctx, namedQuery, map[string]interface{}{
"contest_id": contestId,
"penalty": penalty,
})
if err != nil {
return nil, handlePgErr(err, op)
}
defer rows3.Close()
solutionsMap := make(map[int32][]*models.SolutionsListItem)
for _, solution := range solutions {
solutionsMap[solution.ParticipantId] = append(solutionsMap[solution.ParticipantId], solution)
}
for rows3.Next() {
var stat models.ParticipantsStat
err = rows3.StructScan(&stat)
if err != nil {
return nil, handlePgErr(err, op)
}
if sols, ok := solutionsMap[stat.Id]; ok {
stat.Solutions = sols
}
monitor.Participants = append(monitor.Participants, &stat)
}
return &monitor, nil
}