package usecase import ( "context" "errors" "fmt" "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" "strings" ) type ProblemUseCase struct { problemRepo tester.ProblemPostgresRepository pandocClient pkg.PandocClient } func NewProblemUseCase( problemRepo tester.ProblemPostgresRepository, pandocClient pkg.PandocClient, ) *ProblemUseCase { return &ProblemUseCase{ problemRepo: problemRepo, pandocClient: pandocClient, } } func (u *ProblemUseCase) CreateProblem(ctx context.Context, title string) (int32, error) { return u.problemRepo.CreateProblem(ctx, u.problemRepo.DB(), title) } func (u *ProblemUseCase) ReadProblemById(ctx context.Context, id int32) (*models.Problem, error) { return u.problemRepo.ReadProblemById(ctx, u.problemRepo.DB(), id) } func (u *ProblemUseCase) DeleteProblem(ctx context.Context, id int32) error { return u.problemRepo.DeleteProblem(ctx, u.problemRepo.DB(), id) } func (u *ProblemUseCase) ListProblems(ctx context.Context, page int32, pageSize int32) ([]*models.ProblemListItem, int32, error) { return u.problemRepo.ListProblems(ctx, u.problemRepo.DB(), page, pageSize) } func isEmpty(p models.ProblemUpdate) bool { return p.Title == nil && p.Legend == nil && p.InputFormat == nil && p.OutputFormat == nil && p.Notes == nil && p.Scoring == nil && p.MemoryLimit == nil && p.TimeLimit == nil } const heading = ` \newcommand{\InputFile}{\subsection*{Входные данные}} \newcommand{\OutputFile}{\subsection*{Выходные данные}} \newcommand{\Scoring}{\subsection*{Система оценки}} \newcommand{\Note}{\subsection*{Примечание}} \newcommand{\Examples}{\subsection*{Примеры}} ` func wrap(s string) string { return fmt.Sprintf("%s\n\\begin{document}\n%s\n\\end{document}\n", heading, s) } func trimSpaces(statement models.ProblemStatement) models.ProblemStatement { return models.ProblemStatement{ Legend: strings.TrimSpace(statement.Legend), InputFormat: strings.TrimSpace(statement.InputFormat), OutputFormat: strings.TrimSpace(statement.OutputFormat), Notes: strings.TrimSpace(statement.Notes), Scoring: strings.TrimSpace(statement.Scoring), } } func build(ctx context.Context, pandocClient pkg.PandocClient, p models.ProblemStatement) (models.Html5ProblemStatement, error) { p = trimSpaces(p) latex := models.ProblemStatement{} if p.Legend != "" { latex.Legend = wrap(fmt.Sprintf("\\InputFile\n%s\n", p.Legend)) } if p.InputFormat != "" { latex.InputFormat = wrap(fmt.Sprintf("\\InputFile\n%s\n", p.InputFormat)) } if p.OutputFormat != "" { latex.OutputFormat = wrap(fmt.Sprintf("\\OutputFile\n%s\n", p.OutputFormat)) } if p.Notes != "" { latex.Notes = wrap(fmt.Sprintf("\\Note\n%s\n", p.Notes)) } if p.Scoring != "" { latex.Scoring = wrap(fmt.Sprintf("\\Scoring\n%s\n", p.Scoring)) } req := []string{ latex.Legend, latex.InputFormat, latex.OutputFormat, latex.Notes, latex.Scoring, } res, err := pandocClient.BatchConvertLatexToHtml5(ctx, req) if err != nil { return models.Html5ProblemStatement{}, err } if len(res) != len(req) { return models.Html5ProblemStatement{}, fmt.Errorf("wrong number of fieilds returned: %d", len(res)) } return models.Html5ProblemStatement{ LegendHtml: res[0], InputFormatHtml: res[1], OutputFormatHtml: res[2], NotesHtml: res[3], ScoringHtml: res[4], }, nil } func (u *ProblemUseCase) UpdateProblem(ctx context.Context, id int32, problemUpdate models.ProblemUpdate) error { if isEmpty(problemUpdate) { return pkg.Wrap(pkg.ErrBadInput, nil, "UpdateProblem", "empty problem update") } tx, err := u.problemRepo.BeginTx(ctx) if err != nil { return err } problem, err := u.problemRepo.ReadProblemById(ctx, tx, id) if err != nil { return errors.Join(err, tx.Rollback()) } statement := models.ProblemStatement{ Legend: problem.Legend, InputFormat: problem.InputFormat, OutputFormat: problem.OutputFormat, Notes: problem.Notes, Scoring: problem.Scoring, } if problemUpdate.Legend != nil { statement.Legend = *problemUpdate.Legend } if problemUpdate.InputFormat != nil { statement.InputFormat = *problemUpdate.InputFormat } if problemUpdate.OutputFormat != nil { statement.OutputFormat = *problemUpdate.OutputFormat } if problemUpdate.Notes != nil { statement.Notes = *problemUpdate.Notes } if problemUpdate.Scoring != nil { statement.Scoring = *problemUpdate.Scoring } builtStatement, err := build(ctx, u.pandocClient, trimSpaces(statement)) if err != nil { return errors.Join(err, tx.Rollback()) } if builtStatement.LegendHtml != problem.LegendHtml { problemUpdate.LegendHtml = &builtStatement.LegendHtml } if builtStatement.InputFormatHtml != problem.InputFormatHtml { problemUpdate.InputFormatHtml = &builtStatement.InputFormatHtml } if builtStatement.OutputFormatHtml != problem.OutputFormatHtml { problemUpdate.OutputFormatHtml = &builtStatement.OutputFormatHtml } if builtStatement.NotesHtml != problem.NotesHtml { problemUpdate.NotesHtml = &builtStatement.NotesHtml } if builtStatement.ScoringHtml != problem.ScoringHtml { problemUpdate.ScoringHtml = &builtStatement.ScoringHtml } err = u.problemRepo.UpdateProblem(ctx, tx, id, problemUpdate) if err != nil { return errors.Join(err, tx.Rollback()) } err = tx.Commit() if err != nil { return err } return nil }