From 01ed1de8c39e3797ac75ef0764ce0ba13804bbc4 Mon Sep 17 00:00:00 2001 From: holoti Date: Sun, 13 Apr 2025 11:54:11 +0500 Subject: [PATCH] feat: UploadProblem --- internal/tester/delivery.go | 1 + internal/tester/delivery/rest/handlers.go | 20 ++- .../repository/pg_problems_repository.go | 1 + internal/tester/usecase.go | 2 + internal/tester/usecase/problems_usecase.go | 117 +++++++++++++++++- proto | 2 +- 6 files changed, 140 insertions(+), 3 deletions(-) diff --git a/internal/tester/delivery.go b/internal/tester/delivery.go index d1045a9..a380e0d 100644 --- a/internal/tester/delivery.go +++ b/internal/tester/delivery.go @@ -20,6 +20,7 @@ type Handlers interface { DeleteProblem(c *fiber.Ctx, id int32) error GetProblem(c *fiber.Ctx, id int32) error UpdateProblem(c *fiber.Ctx, id int32) error + UploadProblem(c *fiber.Ctx, id int32) error ListSolutions(c *fiber.Ctx, params testerv1.ListSolutionsParams) error CreateSolution(c *fiber.Ctx, params testerv1.CreateSolutionParams) error GetSolution(c *fiber.Ctx, id int32) error diff --git a/internal/tester/delivery/rest/handlers.go b/internal/tester/delivery/rest/handlers.go index cd5bbb3..3be303f 100644 --- a/internal/tester/delivery/rest/handlers.go +++ b/internal/tester/delivery/rest/handlers.go @@ -1,12 +1,13 @@ package rest import ( + "io" + "git.sch9.ru/new_gate/ms-tester/internal/models" "git.sch9.ru/new_gate/ms-tester/internal/tester" "git.sch9.ru/new_gate/ms-tester/pkg" testerv1 "git.sch9.ru/new_gate/ms-tester/proto/tester/v1" "github.com/gofiber/fiber/v2" - "io" ) type TesterHandlers struct { @@ -240,6 +241,23 @@ func (h *TesterHandlers) UpdateProblem(c *fiber.Ctx, id int32) error { return c.SendStatus(fiber.StatusOK) } +func (h *TesterHandlers) UploadProblem(c *fiber.Ctx, id int32) error { + var req testerv1.UploadProblemRequest + err := c.BodyParser(&req) + if err != nil { + return err + } + + data, err := req.Archive.Bytes() + if err != nil { + return err + } + if err = h.problemsUC.UploadProblem(c.Context(), id, data); err != nil { + return err + } + return nil +} + func (h *TesterHandlers) UpdateContest(c *fiber.Ctx, id int32) error { var req testerv1.UpdateContestRequest err := c.BodyParser(&req) diff --git a/internal/tester/repository/pg_problems_repository.go b/internal/tester/repository/pg_problems_repository.go index fa007da..d4f16ad 100644 --- a/internal/tester/repository/pg_problems_repository.go +++ b/internal/tester/repository/pg_problems_repository.go @@ -2,6 +2,7 @@ package repository import ( "context" + "git.sch9.ru/new_gate/ms-tester/internal/models" "git.sch9.ru/new_gate/ms-tester/internal/tester" "github.com/jmoiron/sqlx" diff --git a/internal/tester/usecase.go b/internal/tester/usecase.go index 9ddf235..169e2b4 100644 --- a/internal/tester/usecase.go +++ b/internal/tester/usecase.go @@ -2,6 +2,7 @@ package tester import ( "context" + "git.sch9.ru/new_gate/ms-tester/internal/models" ) @@ -11,6 +12,7 @@ type ProblemUseCase interface { DeleteProblem(ctx context.Context, id int32) error ListProblems(ctx context.Context, filter models.ProblemsFilter) (*models.ProblemsList, error) UpdateProblem(ctx context.Context, id int32, problem models.ProblemUpdate) error + UploadProblem(ctx context.Context, id int32, archive []byte) error } type ContestUseCase interface { diff --git a/internal/tester/usecase/problems_usecase.go b/internal/tester/usecase/problems_usecase.go index 2311318..27a9c37 100644 --- a/internal/tester/usecase/problems_usecase.go +++ b/internal/tester/usecase/problems_usecase.go @@ -1,14 +1,19 @@ package usecase import ( + "archive/zip" + "bytes" "context" + "encoding/json" "errors" "fmt" + "io" + "strings" + "git.sch9.ru/new_gate/ms-tester/internal/models" "git.sch9.ru/new_gate/ms-tester/internal/tester" "git.sch9.ru/new_gate/ms-tester/pkg" "github.com/microcosm-cc/bluemonday" - "strings" ) type ProblemUseCase struct { @@ -115,6 +120,116 @@ func (u *ProblemUseCase) UpdateProblem(ctx context.Context, id int32, problemUpd return nil } +type ProblemProperties struct { + Title string `json:"name"` + TimeLimit int32 `json:"timeLimit"` + MemoryLimit int32 `json:"memoryLimit"` +} + +func (u *ProblemUseCase) UploadProblem(ctx context.Context, id int32, data []byte) error { + + locale := "russian" + defaultLocale := "english" + var localeProblem, defaultProblem string + var localeProperties, defaultProperties ProblemProperties + + r := bytes.NewReader(data) + rc, err := zip.NewReader(r, int64(r.Len())) + if err != nil { + return err + } + + testsZipBuf := new(bytes.Buffer) + w := zip.NewWriter(testsZipBuf) + + for _, f := range rc.File { + if f.FileInfo().IsDir() { + continue + } + if f.Name == fmt.Sprintf("statements/%s/problem.tex", locale) { + localeProblem, err = readProblem(f) + if err != nil { + return err + } + } + if f.Name == fmt.Sprintf("statements/%s/problem.tex", defaultLocale) { + defaultProblem, err = readProblem(f) + if err != nil { + return err + } + } + if f.Name == fmt.Sprintf("statements/%s/problem-properties.json", locale) { + localeProperties, err = readProperties(f) + if err != nil { + return err + } + } + if f.Name == fmt.Sprintf("statements/%s/problem-properties.json", defaultLocale) { + defaultProperties, err = readProperties(f) + if err != nil { + return err + } + } + if strings.HasPrefix(f.Name, "tests/") { + if err := w.Copy(f); err != nil { + return err + } + } + } + + if err := w.Close(); err != nil { + return err + } + // testsZipBuf contains test files; this is for s3 + + localeProperties.MemoryLimit /= 1024 * 1024 + defaultProperties.MemoryLimit /= 1024 * 1024 + + var problemUpdate models.ProblemUpdate + if localeProblem != "" { + problemUpdate.Legend = &localeProblem + problemUpdate.Title = &localeProperties.Title + problemUpdate.TimeLimit = &localeProperties.TimeLimit + problemUpdate.MemoryLimit = &localeProperties.MemoryLimit + } else { + problemUpdate.Legend = &defaultProblem + problemUpdate.Title = &defaultProperties.Title + problemUpdate.TimeLimit = &defaultProperties.TimeLimit + problemUpdate.MemoryLimit = &defaultProperties.MemoryLimit + } + if err := u.UpdateProblem(ctx, id, problemUpdate); err != nil { + return err + } + + return nil +} + +func readProblem(f *zip.File) (string, error) { + rc, err := f.Open() + if err != nil { + return "", err + } + defer rc.Close() + problemData, err := io.ReadAll(rc) + if err != nil { + return "", err + } + return string(problemData), nil +} + +func readProperties(f *zip.File) (ProblemProperties, error) { + rc, err := f.Open() + if err != nil { + return ProblemProperties{}, err + } + defer rc.Close() + var properties ProblemProperties + if err := json.NewDecoder(rc).Decode(&properties); err != nil { + return ProblemProperties{}, err + } + return properties, nil +} + func isEmpty(p models.ProblemUpdate) bool { return p.Title == nil && p.Legend == nil && diff --git a/proto b/proto index 1fbee7b..f00483d 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 1fbee7ba29c358c76d1c835ac6999ce9e1b59ee9 +Subproject commit f00483d24a53a243734c793fc24e02d52d39fdab