Skip to content

Commit

Permalink
Refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy committed Jun 15, 2024
1 parent 43fa647 commit e7abb8d
Show file tree
Hide file tree
Showing 33 changed files with 438 additions and 415 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ check: gen-proto install-cilint
golangci-lint run

.PHONY: test
test: gen-swagger setup-dependencies
test: build gen-swagger setup-dependencies
go test -cover -v -count=1 ./...

# Dependent targets
Expand Down
4 changes: 2 additions & 2 deletions cmd/init_db/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func main() {
err := db.AutoMigrate(
&user_model.User{},
&problem_model.Problem{},
&judge_model.JudgeTaskSubmission{},
&judge_model.Judger{})
&judge_model.Judge{},
)
if err != nil {
panic("failed to migrate database")
}
Expand Down
31 changes: 16 additions & 15 deletions cmd/problem_loader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io/fs"
"log"
"os"
"path"
"path/filepath"
Expand All @@ -15,6 +14,7 @@ import (
gormAgent "github.com/oj-lab/oj-lab-platform/modules/agent/gorm"
minioAgent "github.com/oj-lab/oj-lab-platform/modules/agent/minio"
"github.com/oj-lab/oj-lab-platform/modules/config"
"github.com/oj-lab/oj-lab-platform/modules/log"
yaml "gopkg.in/yaml.v2"
)

Expand All @@ -24,8 +24,6 @@ func main() {
db := gormAgent.GetDefaultDB()
minioClient := minioAgent.GetMinioClient()

log.Printf("%#v\n", minioClient) // minioClient is now set up

// Read package files
// Search Problem under packagePath
// 1. parse problem path as `slug`,
Expand All @@ -38,39 +36,42 @@ func main() {
slug string
)
err := filepath.Walk(packagePath, func(path string, info fs.FileInfo, err error) error {
if err != nil {
log.AppLogger().WithError(err).Error("Walk package path failed")
return err
}
if info == nil {
return fmt.Errorf("file info is nil")
}
if info.IsDir() {
return nil
}
relativePath := strings.Replace(path, packagePath, "", 1)
println("relativePath: ", relativePath)
log.AppLogger().WithField("relativePath", relativePath).Debug("Read file from package")
if filepath.Base(relativePath) == "problem.yaml" {
resultMap := make(map[string]interface{})
yamlFile, err := os.ReadFile(path)
if err != nil {
log.Println(err)
log.AppLogger().WithError(err).Error("Read problem.yaml failed")
}
err = yaml.Unmarshal(yamlFile, &resultMap)
if err != nil {
log.Printf("Unmarshal: %v\n", err)
log.AppLogger().WithError(err).Error("Unmarshal problem.yaml failed")
}
title = resultMap["name"].(string)
if title == "" {
log.Fatal("name key not exist in problem.yaml")
log.AppLogger().Error("Problem title is empty")
}
slug = strings.Split(relativePath, "/")[1]
log.Println("title: ", title)
log.Println("slug: ", slug)
log.AppLogger().WithField("title", title).WithField("slug", slug).Debug("Read problem.yaml")
}
if filepath.Base(relativePath) == "problem.md" {
content, err := os.ReadFile(path)
if err != nil {
log.Println(err)
log.AppLogger().WithError(err).Error("Read problem.md failed")
}
description := string(content)
println("description: ", description)
log.AppLogger().WithField("description", description).Debug("Read problem.md")
err = problem_model.CreateProblem(db, problem_model.Problem{
Slug: slug,
Title: title,
Expand All @@ -84,18 +85,18 @@ func main() {
}
}

_, minioErr := minioClient.FPutObject(ctx, minioAgent.GetBucketName(),
_, err = minioClient.FPutObject(ctx, minioAgent.GetBucketName(),
relativePath,
path,
minio.PutObjectOptions{})
if minioErr != nil {
log.Fatalln(minioErr)
if err != nil {
log.AppLogger().WithError(err).Error("Put object to minio failed")
}
return err
})
if err != nil {
panic(err)
}

log.Println("Read Problem Success!")
log.AppLogger().Info("Problem loaded")
}
2 changes: 1 addition & 1 deletion cmd/web_server/handler/judge.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func postReportJudgeTaskResult(ginCtx *gin.Context) {
return
}

if err := judge_service.ReportJudgeTaskResult(ginCtx, body.Consumer, body.StreamID, body.VerdictJson); err != nil {
if err := judge_service.ReportJudgeTask(ginCtx, body.Consumer, body.StreamID, body.VerdictJson); err != nil {
_ = ginCtx.Error(err)
return
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/web_server/handler/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,10 @@ func checkProblemSlug(ginCtx *gin.Context) {
//
// @Description The body of a submission request, containing the code and the language used for the submission.
// @Property code (string) required "The source code of the submission" minlength(1)
// @Property language (SubmissionLanguage) required "The programming language used for the submission"
// @Property language (ProgrammingLanguage) required "The programming language used for the submission"
type PostSubmissionBody struct {
Code string `json:"code" binding:"required"`
Language judge_model.SubmissionLanguage `json:"language" binding:"required"`
Code string `json:"code" binding:"required"`
Language judge_model.ProgrammingLanguage `json:"language" binding:"required"`
}

// postSubmission
Expand All @@ -191,8 +191,8 @@ func postSubmission(ginCtx *gin.Context) {
return
}

submission := judge_model.NewSubmission("", slug, body.Code, body.Language)
result, err := judge_service.CreateJudgeTaskSubmission(ginCtx, submission)
submission := judge_model.NewJudge("", slug, body.Code, body.Language)
result, err := judge_service.CreateJudge(ginCtx, submission)
if err != nil {
modules.NewInternalError(err.Error()).AppendToGin(ginCtx)
return
Expand Down
11 changes: 5 additions & 6 deletions cmd/web_server/handler/submission.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func SetupSubmissionRouter(baseRoute *gin.RouterGroup) {
func getSubmission(ginCtx *gin.Context) {
uid := ginCtx.Param("uid")

submission, err := judge_service.GetJudgeTaskSubmission(ginCtx, uid)
submission, err := judge_service.GetJudge(ginCtx, uid)
if err != nil {
modules.NewInternalError(fmt.Sprintf("failed to get submission: %v", err)).AppendToGin(ginCtx)
return
Expand All @@ -38,14 +38,13 @@ func getSubmission(ginCtx *gin.Context) {
"code": submission.Code,
"language": submission.Language,
"status": submission.Status,
"verdictJson": submission.VerdictJson,
"mainResult": submission.MainResult,
})
}

type getSubmissionListResponse struct {
Total int64 `json:"total"`
List []*judge_model.JudgeTaskSubmission `json:"list"`
Total int64 `json:"total"`
List []*judge_model.Judge `json:"list"`
}

// Get Submission List
Expand Down Expand Up @@ -78,13 +77,13 @@ func getSubmissionList(ginCtx *gin.Context) {
return
}

options := judge_model.GetSubmissionOptions{
options := judge_model.GetJudgeOptions{
Limit: &limit,
Offset: &offset,
OrderByColumns: []models.OrderByColumnOption{{Column: "create_at", Desc: true}},
}

submissions, total, err := judge_service.GetJudgeTaskSubmissionList(ginCtx, options)
submissions, total, err := judge_service.GetJudgeList(ginCtx, options)
if err != nil {
modules.NewInternalError(fmt.Sprintf("failed to get submission list: %v", err)).AppendToGin(ginCtx)
return
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
gorm.io/gorm v1.25.10
)

require github.com/swaggo/swag v1.16.3 // indirect
require github.com/swaggo/swag v1.16.3

require (
github.com/bytedance/sonic/loader v0.1.1 // indirect
Expand Down
138 changes: 47 additions & 91 deletions models/judge/judge.go
Original file line number Diff line number Diff line change
@@ -1,114 +1,70 @@
package judge
package judge_model

import (
"strings"

"github.com/google/uuid"
"github.com/oj-lab/oj-lab-platform/models"
problem_model "github.com/oj-lab/oj-lab-platform/models/problem"
user_model "github.com/oj-lab/oj-lab-platform/models/user"
)

// Should contains a priority definition
// Ex. CompileError > RuntimeError > TimeLimitExceeded > MemoryLimitExceeded > SystemError > WrongAnswer > Accepted
type JudgeVerdict string
type JudgeTaskStatus string

const (
JudgeVerdictCompileError JudgeVerdict = "CompileError" // Only for main verdict
JudgeVerdictRuntimeError JudgeVerdict = "RuntimeError"
JudgeVerdictTimeLimitExceeded JudgeVerdict = "TimeLimitExceeded"
JudgeVerdictMemoryLimitExceeded JudgeVerdict = "MemoryLimitExceeded"
JudgeVerdictSystemError JudgeVerdict = "SystemError" // Some runtime unknown error ?
JudgeVerdictWrongAnswer JudgeVerdict = "WrongAnswer"
JudgeVerdictAccepted JudgeVerdict = "Accepted"
JudgeVerdictCancelled JudgeVerdict = "cancelled" // Judge will be cancelled if some point results in Runtime error, Time limit exceeded, Memory limit exceeded
JudgeTaskStatusPending JudgeTaskStatus = "pending"
JudgeTaskStatusWaiting JudgeTaskStatus = "waiting"
JudgeTaskStatusRunning JudgeTaskStatus = "running"
JudgeTaskStatusFinished JudgeTaskStatus = "finished"
)

type JudgeResult struct {
MainVerdict JudgeVerdict `json:"verdict"` // A merge of all TestPoints' verdict, according to the pirority
Detail string `json:"detail"` // A brief description of the result
TestPointCount uint64 `json:"testPointCount"` // Won't be stored in database
TestPointMap map[string]TestPoint `json:"testPoints"` // Won't be stored in database
TestPointsJson string `json:"-"` // Used to store TestPoints in database
AverageTimeMs uint64 `json:"averageTimeMs"` // Won't be stored in database
MaxTimeMs uint64 `json:"maxTimeMs"` // Won't be stored in database
AverageMemory uint64 `json:"averageMemory"` // Won't be stored in database
MaxMemory uint64 `json:"maxMemory"` // Won't be stored in database
}

type TestPoint struct {
Index string `json:"index"` // The name of *.in/ans file
Verdict JudgeVerdict `json:"verdict"`
Diff *ResultDiff `json:"diff"` // Required if verdict is wrong_answer
TimeUsageMs uint64 `json:"timeUsageMs"`
MemoryUsageByte uint64 `json:"memoryUsageByte"`
}
type ProgrammingLanguage string

type ResultDiff struct {
Expected string `json:"expected"`
Received string `json:"received"`
func (sl ProgrammingLanguage) String() string {
return string(sl)
}

type JudgerState string

const (
JudgerStateIdle JudgerState = "idle"
JudgerStateBusy JudgerState = "busy"
JudgerStateOffline JudgerState = "offline"
ProgrammingLanguageCpp ProgrammingLanguage = "Cpp"
ProgrammingLanguageRust ProgrammingLanguage = "Rust"
ProgrammingLanguagePython ProgrammingLanguage = "Python"
)

type Judger struct {
// Using relationship according to https://gorm.io/docs/belongs_to.html
type Judge struct {
models.MetaFields
Host string `gorm:"primaryKey" json:"host"`
State JudgerState `gorm:"default:offline" json:"status"`
}

type JudgeTask struct {
SubmissionUID string `json:"submissionUID"`
ProblemSlug string `json:"problemSlug"`
Code string `json:"code"`
Language string `json:"language"`
RedisStreamID *string `json:"redisStreamID"`
}

func (jt *JudgeTask) ToStringMap() map[string]interface{} {
return map[string]interface{}{
"submission_uid": jt.SubmissionUID,
"problem_slug": jt.ProblemSlug,
"code": jt.Code,
"language": jt.Language,
}
}

func JudgeTaskFromMap(m map[string]interface{}) *JudgeTask {
return &JudgeTask{
SubmissionUID: m["submission_uid"].(string),
ProblemSlug: m["problem_slug"].(string),
Code: m["code"].(string),
Language: m["language"].(string),
}
UID uuid.UUID `json:"UID" gorm:"primaryKey"`
RedisStreamID string `json:"redisStreamID"`
UserAccount string `json:"userAccount" gorm:"not null"`
User user_model.User `json:"user"`
ProblemSlug string `json:"problemSlug" gorm:"not null"`
Problem problem_model.Problem `json:"problem"`
Code string `json:"code" gorm:"not null"`
Language ProgrammingLanguage `json:"language" gorm:"not null"`
Status JudgeTaskStatus `json:"status" gorm:"default:pending"`
JudgeResultCount uint `json:"judgeResultCount"`
JudgeResults []JudgeResult `json:"judgeResults" gorm:"foreignKey:JudgeUID"`
MainResult JudgeVerdict `json:"mainResult"`
}

func (js JudgerState) CanUpdate(nextStatus JudgerState) bool {
switch js {
case JudgerStateOffline:
return nextStatus == JudgerStateIdle
case JudgerStateIdle:
return nextStatus == JudgerStateBusy || nextStatus == JudgerStateOffline
case JudgerStateBusy:
return nextStatus == JudgerStateIdle || nextStatus == JudgerStateOffline
default:
return false
func NewJudge(
userAccount string,
problemSlug string,
code string,
language ProgrammingLanguage,
) Judge {
return Judge{
UserAccount: userAccount,
ProblemSlug: problemSlug,
Code: code,
Language: language,
Status: JudgeTaskStatusPending,
}
}

func StringToJudgerState(state string) JudgerState {
state = strings.ToLower(state)
switch state {
case "idle":
return JudgerStateIdle
case "busy":
return JudgerStateBusy
case "offline":
return JudgerStateOffline
default:
return JudgerStateOffline
func (s *Judge) ToJudgeTask() JudgeTask {
return JudgeTask{
JudgeUID: s.UID.String(),
ProblemSlug: s.ProblemSlug,
Code: s.Code,
Language: s.Language.String(),
}
}
Loading

0 comments on commit e7abb8d

Please sign in to comment.