diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-20 21:38:58 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-20 21:38:58 +0900 |
| commit | 85b7a14913c05b88b720fc546eaca5575ffe53fd (patch) | |
| tree | b19d66d3500bb4b697cf656a769a111411793cf3 /backend/admin/handler.go | |
| parent | fa788237eb5649e08b2a38ec21689b481b10c073 (diff) | |
| download | phperkaigi-2026-albatross-85b7a14913c05b88b720fc546eaca5575ffe53fd.tar.gz phperkaigi-2026-albatross-85b7a14913c05b88b720fc546eaca5575ffe53fd.tar.zst phperkaigi-2026-albatross-85b7a14913c05b88b720fc546eaca5575ffe53fd.zip | |
feat(admin): add bulk rejudge for game submissions
Extract common rejudge logic into a helper method and add two new
endpoints: rejudge-latest (per-user latest only) and rejudge-all.
This allows re-running submissions in bulk after testcase changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/admin/handler.go')
| -rw-r--r-- | backend/admin/handler.go | 84 |
1 files changed, 74 insertions, 10 deletions
diff --git a/backend/admin/handler.go b/backend/admin/handler.go index c8ddd25..9bdeb69 100644 --- a/backend/admin/handler.go +++ b/backend/admin/handler.go @@ -72,6 +72,8 @@ func (h *Handler) RegisterHandlers(g *echo.Group) { g.POST("/games/:gameID", h.postGameEdit) g.POST("/games/:gameID/start", h.postGameStart) g.GET("/games/:gameID/submissions", h.getSubmissions) + g.POST("/games/:gameID/submissions/rejudge-latest", h.postSubmissionsRejudgeLatest) + g.POST("/games/:gameID/submissions/rejudge-all", h.postSubmissionsRejudgeAll) g.GET("/games/:gameID/submissions/:submissionID", h.getSubmissionDetail) g.POST("/games/:gameID/submissions/:submissionID/rejudge", h.postSubmissionRejudge) @@ -671,6 +673,22 @@ func (h *Handler) getSubmissionDetail(c echo.Context) error { }) } +func (h *Handler) rejudgeSubmission(ctx context.Context, submission db.Submission, language string) error { + err := h.txm.RunInTx(ctx, func(qtx db.Querier) error { + if err := qtx.DeleteTestcaseResultsBySubmissionID(ctx, submission.SubmissionID); err != nil { + return err + } + return qtx.UpdateSubmissionStatus(ctx, db.UpdateSubmissionStatusParams{ + SubmissionID: submission.SubmissionID, + Status: "running", + }) + }) + if err != nil { + return err + } + return h.hub.EnqueueTestTasks(ctx, int(submission.SubmissionID), int(submission.GameID), int(submission.UserID), language, submission.Code) +} + func (h *Handler) postSubmissionRejudge(c echo.Context) error { gameID, err := strconv.Atoi(c.Param("gameID")) if err != nil { @@ -700,25 +718,71 @@ func (h *Handler) postSubmissionRejudge(c echo.Context) error { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } - err = h.txm.RunInTx(ctx, func(qtx db.Querier) error { - if err := qtx.DeleteTestcaseResultsBySubmissionID(ctx, int32(submissionID)); err != nil { - return err + if err := h.rejudgeSubmission(ctx, submission, game.Language); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%sadmin/games/%d/submissions/%d", h.conf.BasePath, gameID, submissionID)) +} + +func (h *Handler) postSubmissionsRejudgeLatest(c echo.Context) error { + gameID, err := strconv.Atoi(c.Param("gameID")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_id") + } + + ctx := c.Request().Context() + + game, err := h.q.GetGameByID(ctx, int32(gameID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusNotFound) } - return qtx.UpdateSubmissionStatus(ctx, db.UpdateSubmissionStatusParams{ - SubmissionID: int32(submissionID), - Status: "running", - }) - }) + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + submissions, err := h.q.GetLatestSubmissionsByGameID(ctx, int32(gameID)) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } - err = h.hub.EnqueueTestTasks(ctx, submissionID, gameID, int(submission.UserID), game.Language, submission.Code) + for _, s := range submissions { + if err := h.rejudgeSubmission(ctx, s, game.Language); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + } + + return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%sadmin/games/%d/submissions", h.conf.BasePath, gameID)) +} + +func (h *Handler) postSubmissionsRejudgeAll(c echo.Context) error { + gameID, err := strconv.Atoi(c.Param("gameID")) if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game_id") + } + + ctx := c.Request().Context() + + game, err := h.q.GetGameByID(ctx, int32(gameID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusNotFound) + } return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } - return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%sadmin/games/%d/submissions/%d", h.conf.BasePath, gameID, submissionID)) + submissions, err := h.q.GetSubmissionsByGameID(ctx, int32(gameID)) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + for _, s := range submissions { + if err := h.rejudgeSubmission(ctx, s, game.Language); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + } + + return c.Redirect(http.StatusSeeOther, fmt.Sprintf("%sadmin/games/%d/submissions", h.conf.BasePath, gameID)) } func (h *Handler) getProblems(c echo.Context) error { |
