diff --git a/api/api_test.go b/api/api_test.go index b6de023..e11d3c8 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -83,7 +83,7 @@ func TestCreateUser(t *testing.T) { clearUserTable() newUser := store.User{ Username: "testuser", - Passhash: "testpass", + Password: "testpass", Email: "test@example.com", } diff --git a/api/submit.go b/api/submit.go index da0950b..2c10c32 100644 --- a/api/submit.go +++ b/api/submit.go @@ -45,7 +45,7 @@ func (api *ApiContext) submitProgram(w http.ResponseWriter, r *http.Request) { fmt.Println(fileHeader.Size, fileHeader.Filename) m.Lock() - worker := <- *judge.WorkerQueueP + worker := <-*judge.WorkerQueueP m.Unlock() conn := worker.Ctx @@ -53,6 +53,7 @@ func (api *ApiContext) submitProgram(w http.ResponseWriter, r *http.Request) { filenameSep := strings.Split(fileHeader.Filename, ".") filetype := filenameSep[len(filenameSep)-1] + output := int(0) // testcases, err := api.Store.GetTestCases(r.Context(), problemID) testcases := []judge.SubmissionTestCase{ @@ -61,7 +62,7 @@ func (api *ApiContext) submitProgram(w http.ResponseWriter, r *http.Request) { judge.SubmissionTestCase{Input: "13\n", Output: "6227020800\n"}, } timelimit := 1 - createResponse, err := judge.CreateSandbox(conn, filetype, string(buf)[:n], testcases, timelimit) + createResponse, err := judge.CreateSandbox(conn, filetype, string(buf)[:n], testcases, timelimit, output) if err != nil { *judge.WorkerQueueP <- worker s := fmt.Sprintf("Could not create sandbox: %v", err) @@ -101,7 +102,6 @@ func (api *ApiContext) submitProgram(w http.ResponseWriter, r *http.Request) { // Metelo de nuevo *judge.WorkerQueueP <- worker - w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(resMap) diff --git a/judge/judge.go b/judge/judge.go index c80e950..3864c70 100644 --- a/judge/judge.go +++ b/judge/judge.go @@ -19,13 +19,14 @@ import ( "github.com/containers/podman/v5/pkg/domain/entities/types" "github.com/containers/podman/v5/pkg/specgen" dockerContainer "github.com/docker/docker/api/types/container" + _ "modernc.org/sqlite" ) var podmanConns []*Worker var workerQueue = make(chan *Worker, 10) -var WorkerQueueP = &workerQueue; +var WorkerQueueP = &workerQueue var m sync.Mutex @@ -36,8 +37,8 @@ func ConnectToPodman(connURI string) (context.Context, error) { } worker := &Worker{ - Uri: connURI, - Ctx: conn, + Uri: connURI, + Ctx: conn, Available: true, } podmanConns = append(podmanConns, worker) @@ -53,7 +54,6 @@ func CreateFiles(filetype, programStr string, testcases []SubmissionTestCase, ti m.Lock() defer m.Unlock() - err := os.MkdirAll("/vol/podman/inputs", 0755) if err != nil { return err @@ -96,7 +96,6 @@ func CreateFiles(filetype, programStr string, testcases []SubmissionTestCase, ti testInputPath := fmt.Sprintf("./inputs/%02d.txt", k) expectedOutputPath := fmt.Sprintf("./expected-outputs/%02d.txt", k) - err = writeStringToFile(testInputPath, testcase.Input) if err != nil { return err @@ -111,7 +110,7 @@ func CreateFiles(filetype, programStr string, testcases []SubmissionTestCase, ti return nil } -func CreateSandbox(conn context.Context, filetype, programStr string, testcases []SubmissionTestCase, timeLimit int) (types.ContainerCreateResponse, error) { +func CreateSandbox(conn context.Context, filetype, programStr string, testcases []SubmissionTestCase, timeLimit int, output int) (types.ContainerCreateResponse, error) { err := CreateFiles(filetype, programStr, testcases, timeLimit) @@ -176,3 +175,38 @@ func StartSandbox(conn context.Context, createResponse types.ContainerCreateResp return nil } + +/* +///Open function to calculate and update ranking after a submission is judged (suggestion) +func CalculateAndUpdateRanking( + ctx context.Context, + s store.Store, + userID int, + status string, + executionTime float64) ([]store.UserScore, error) { + + log.Printf("Iniciando cálculo de puntaje para el usuario: %d, status: %s", userID, status) + //calculate user score + input := store.ScoringInput{Status: status, ExecutionTime: executionTime} + score := calculateSingleScore(input) + + log.Printf("Puntaje calculado: %d", score) + + //funcion para cambiar el puntaje del usuario + if err := s.UserScore(ctx, userID, score); err != nil { + log.Printf("Error al actualizar puntaje del usuario %d: %v", userID, err) + return nil, fmt.Errorf("error al actualizar puntaje en store: %w", err) + } + + ranking, err := s.GetRanking(ctx) + if err != nil { + log.Printf("Error al obtener ranking: %v", err) + return nil, fmt.Errorf("error al obtener ranking del store: %w", err) + } + + PrintRanking(ranking) + return ranking, nil +} +func calculateSingleScore(sub store.ScoringInput) int { + return 0 +}*/ diff --git a/schema.sql b/schema.sql index dc89191..a471e6b 100644 --- a/schema.sql +++ b/schema.sql @@ -5,7 +5,8 @@ create table if not exists user ( username text not null unique, passhash text not null, passsalt text not null, - email text not null unique + email text not null unique, + total_score integer not null default 0 ); create table if not exists status ( diff --git a/security/hash.go b/security/hash.go index 22dc4e5..00c58b2 100644 --- a/security/hash.go +++ b/security/hash.go @@ -8,11 +8,11 @@ import ( ) type Params struct { - Memory uint32 - Iterations uint32 - Parallelism uint8 - SaltLength uint32 - KeyLength uint32 + Memory uint32 + Iterations uint32 + Parallelism uint8 + SaltLength uint32 + KeyLength uint32 } var Argon2Params *Params = &Params{ @@ -57,7 +57,7 @@ func HashPassword(password string, p *Params) (hash, salt []byte, err error) { return hash, salt, nil } -func generateSalt( n uint32) ([]byte, error) { +func generateSalt(n uint32) ([]byte, error) { b := make([]byte, n) _, err := rand.Read(b) if err != nil { diff --git a/store/sqlite.go b/store/sqlite.go index 27271bb..98730c2 100644 --- a/store/sqlite.go +++ b/store/sqlite.go @@ -61,7 +61,6 @@ func (s *SQLiteStore) CreateUser(ctx context.Context, u *User) error { return err } - if id, ierr := res.LastInsertId(); ierr == nil { u.UserID = id u.JWT = token @@ -296,10 +295,60 @@ func (s *SQLiteStore) Login(ctx context.Context, u *User) error { if !val { log.Println("ERR", err) return security.ErrInvalidCredentials - } + } return nil - + } +func (s *SQLiteStore) GetRanking(ctx context.Context) ([]UserScore, error) { + var ranking []UserScore + + query := ` + SELECT + subm.user_id, + USR.username, + SUM(subm.score) AS total_score + FROM + submission subm + JOIN + contest_has_problem C_h_p ON subm.problem_id = C_h_p.problem_id + JOIN + user USR ON subm.user_id = USR.user_id + WHERE + C_h_p.contest_id = ? + GROUP BY + subm.user_id, USR.username + ORDER BY + total_score DESC, subm.user_id ASC; + ` + + rows, err := s.DB.QueryContext(ctx, query) + if err != nil { + return nil, err + } + defer rows.Close() + + rankCounter := 1 + for rows.Next() { + var userID int + var score int + if err := rows.Scan(&userID, &score); err != nil { + return nil, err + } + + ranking = append(ranking, UserScore{ + Rank: rankCounter, + UserID: userID, + Score: score, + }) + rankCounter++ + } + + if rows.Err() != nil { + return nil, err + } + + return ranking, nil +} diff --git a/store/store.go b/store/store.go index c7ea8bb..54732ec 100644 --- a/store/store.go +++ b/store/store.go @@ -48,6 +48,15 @@ type TestCase struct { ExpectedOut []byte `json:"expected_out"` GivenInput []byte `json:"given_input"` } +type UserScore struct { + Rank int `json:"rank"` + UserID int `json:"user_id"` + Score int `json:"score"` +} +type ScoringInput struct { + Status string + ExecutionTime float64 +} // Store defines persistence operations used by the HTTP layer. type Store interface { @@ -64,4 +73,6 @@ type Store interface { GetContestProblems(ctx context.Context, id int) ([]Problem, error) GetProblemByIDs(ctx context.Context, contestID, problemID int) (*Problem, error) GetTestCases(ctx context.Context, problemID int) ([]TestCase, error) + //UserScore(ctx context.Context, userID int, score int) error + GetRanking(ctx context.Context) ([]UserScore, error) }