diff --git a/gradation.go b/gradation.go
index 8e762e9..4acf203 100644
--- a/gradation.go
+++ b/gradation.go
@@ -1,8 +1,11 @@
package main
import (
+ "fmt"
"log"
"net/http"
+ "strconv"
+ "strings"
"github.com/drone/drone-go/drone"
"github.com/gin-gonic/gin"
@@ -30,3 +33,74 @@ func declareAPIAdminGradationRoutes(router *gin.RouterGroup) {
c.JSON(http.StatusOK, result)
})
}
+
+type TestsWebhook struct {
+ Login string `json:"login"`
+ RepositoryId int `json:"repository_id"`
+ BuildNumber int `json:"build_number"`
+ Steps map[string]float64 `json:"steps,omitempty"`
+}
+
+func (r *Repository) fetchRepoTests(build_number int, steps map[string]float64) error {
+ tmp := strings.Split(r.TestsRef, "/")
+ if len(tmp) < 3 {
+ return fmt.Errorf("This repository tests reference is not filled properly.")
+ }
+
+ work, err := getWork(r.IdWork)
+ if err != nil {
+ return fmt.Errorf("Unable to retrieve the related work: %w", err)
+ }
+
+ client := drone.NewClient(droneEndpoint, droneConfig)
+ result, err := client.Build(tmp[0], tmp[1], build_number)
+ if err != nil {
+ return fmt.Errorf("Unable to find the referenced build (%d): %w", build_number, err)
+ }
+
+ if result.Finished > 0 {
+ return fmt.Errorf("The test phase is not finished")
+ }
+
+ var grade float64
+ for _, stage := range result.Stages {
+ for _, step := range stage.Steps {
+ if g, ok := steps[fmt.Sprintf("%d", step.Number)]; ok {
+ log.Printf("Step %q (%d) in status %q", step.Name, step.Number, step.Status)
+ // Give the point if it succeed
+ if step.Status == "success" {
+ grade += g
+ }
+ continue
+ }
+
+ logs, err := client.Logs(tmp[0], tmp[1], build_number, stage.Number, step.Number)
+ if err != nil {
+ log.Printf("Unable to retrieve build logs %s/%s/%d/%d/%d: %s", tmp[0], tmp[1], build_number, stage.Number, step.Number, err.Error())
+ continue
+ }
+
+ if len(logs) < 1 {
+ continue
+ }
+
+ line := logs[len(logs)-1]
+ if strings.HasPrefix(line.Message, "grade:") {
+ g, err := strconv.ParseFloat(strings.TrimSpace(strings.TrimPrefix(line.Message, "grade:")), 64)
+ if err == nil {
+ grade += g
+ } else {
+ log.Println("Unable to parse grade:", err.Error())
+ }
+ }
+ }
+ }
+
+ work.AddGrade(WorkGrade{
+ IdUser: r.IdUser,
+ IdWork: work.Id,
+ Grade: grade,
+ })
+
+ return nil
+}
diff --git a/repositories.go b/repositories.go
index 855397e..d06d8fa 100644
--- a/repositories.go
+++ b/repositories.go
@@ -334,12 +334,6 @@ type GitLabWebhook struct {
Repository GitLabRepository
}
-type TestsWebhook struct {
- Login string `json:"login"`
- RepositoryId int `json:"repository_id"`
- BuildNumber int `json:"build_number"`
-}
-
func declareCallbacksRoutes(router *gin.RouterGroup) {
router.POST("/callbacks/trigger.json", func(c *gin.Context) {
// Check event type
@@ -446,7 +440,7 @@ func declareCallbacksRoutes(router *gin.RouterGroup) {
return
}
- err = repo.fetchRepoTests(hook.BuildNumber)
+ err = repo.fetchRepoTests(hook.BuildNumber, hook.Steps)
if err != nil {
log.Printf("Unable to fetchRepoTests(%d): %s", hook.RepositoryId, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to fetch tests results."})
@@ -576,7 +570,7 @@ func TriggerTests(c *gin.Context, work *Work, repo *Repository, u *User) {
Key: aws.String(filepath.Join(fmt.Sprintf("%d", work.Id), fmt.Sprintf("rendu-%s.tar.xz", login))),
})
- url, err := req.Presign(SharingTime)
+ url, err := req.Presign(SharingTime * 20)
if err != nil {
log.Println("Unable to create presign URL:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something goes wrong when creating the presigned URL."})
@@ -609,6 +603,42 @@ func TriggerTests(c *gin.Context, work *Work, repo *Repository, u *User) {
c.JSON(http.StatusOK, repo)
}
+func (w *Work) stopTests() error {
+ repos, err := w.GetRepositories()
+ if err != nil {
+ return err
+ }
+
+ client := drone.NewClient(droneEndpoint, droneConfig)
+ for _, repo := range repos {
+ slug := strings.Split(repo.TestsRef, "/")
+ if len(slug) < 3 {
+ continue
+ }
+
+ buildn, err := strconv.ParseInt(slug[2], 10, 32)
+ if err != nil {
+ continue
+ }
+
+ build, err := client.Build(slug[0], slug[1], int(buildn))
+ if err != nil {
+ log.Println("Unable to communicate with Drone:", err.Error())
+ continue
+ }
+
+ if build.Status == "pending" {
+ err := client.BuildCancel(slug[0], slug[1], int(buildn))
+ if err != nil {
+ log.Println("Unable to cancel the build:", err.Error())
+ continue
+ }
+ }
+ }
+
+ return nil
+}
+
type Repository struct {
Id int64 `json:"id"`
IdUser int64 `json:"id_user"`
@@ -643,6 +673,27 @@ func (u *User) GetRepositories() (repositories []*Repository, err error) {
}
}
+func (w *Work) GetRepositories() (repositories []*Repository, err error) {
+ if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE id_work=?", w.Id); errr != nil {
+ return nil, errr
+ } else {
+ defer rows.Close()
+
+ for rows.Next() {
+ var repo Repository
+ if err = rows.Scan(&repo.Id, &repo.IdUser, &repo.IdWork, &repo.URI, &repo.Secret, &repo.LastCheck, &repo.DroneRef, &repo.LastTests, &repo.TestsRef); err != nil {
+ return
+ }
+ repositories = append(repositories, &repo)
+ }
+ if err = rows.Err(); err != nil {
+ return
+ }
+
+ return
+ }
+}
+
func getRepositoriesByURI(uri string) (repositories []*Repository, err error) {
if rows, errr := DBQuery("SELECT id_repository, id_user, id_work, uri, secret, last_check, droneref, last_tests, testsref FROM user_work_repositories WHERE uri=?", uri); errr != nil {
return nil, errr
@@ -689,7 +740,7 @@ func (u *User) NewRepository(w *Work, uri string) (*Repository, error) {
return nil, err
}
- if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref) VALUES (?, ?, ?, ?, ?)", u.Id, w.Id, uri, secret, ""); err != nil {
+ if res, err := DBExec("INSERT INTO user_work_repositories (id_user, id_work, uri, secret, droneref, testsref) VALUES (?, ?, ?, ?, ?, '')", u.Id, w.Id, uri, secret, ""); err != nil {
return nil, err
} else if rid, err := res.LastInsertId(); err != nil {
return nil, err
@@ -716,58 +767,6 @@ func (r Repository) Delete() (int64, error) {
}
}
-func (r *Repository) fetchRepoTests(build_number int) error {
- tmp := strings.Split(r.TestsRef, "/")
- if len(tmp) < 3 {
- return fmt.Errorf("This repository tests reference is not filled properly.")
- }
-
- work, err := getWork(r.IdWork)
- if err != nil {
- return fmt.Errorf("Unable to retrieve the related work: %w", err)
- }
-
- client := drone.NewClient(droneEndpoint, droneConfig)
- result, err := client.Build(tmp[0], tmp[1], build_number)
- if err != nil {
- return fmt.Errorf("Unable to find the referenced build (%d): %w", build_number, err)
- }
-
- if result.Finished > 0 {
- return fmt.Errorf("The test phase is not finished")
- }
-
- var grade float64
- for _, stage := range result.Stages {
- for _, step := range stage.Steps {
- logs, err := client.Logs(tmp[0], tmp[1], build_number, stage.Number, step.Number)
- if err != nil {
- log.Printf("Unable to retrieve build logs %s/%s/%d/%d/%d: %s", tmp[0], tmp[1], build_number, stage.Number, step.Number, err.Error())
- continue
- }
-
- for _, line := range logs {
- if strings.HasPrefix(line.Message, "grade:") {
- g, err := strconv.ParseFloat(strings.TrimSpace(strings.TrimPrefix(line.Message, "grade:")), 64)
- if err == nil {
- grade += g
- } else {
- log.Println("Unable to parse grade:", err.Error())
- }
- }
- }
- }
- }
-
- work.AddGrade(WorkGrade{
- IdUser: r.IdUser,
- IdWork: work.Id,
- Grade: grade,
- })
-
- return nil
-}
-
func ClearRepositories() (int64, error) {
if res, err := DBExec("DELETE FROM user_work_repositories"); err != nil {
return 0, err
diff --git a/submissions.go b/submissions.go
index b5f0da1..8f7aa59 100644
--- a/submissions.go
+++ b/submissions.go
@@ -17,7 +17,7 @@ import (
"github.com/gin-gonic/gin"
)
-const SharingTime = 15 * time.Minute
+const SharingTime = 10 * time.Minute
var (
s3_endpoint string
diff --git a/ui/src/lib/components/ScoreBadge.svelte b/ui/src/lib/components/ScoreBadge.svelte
new file mode 100644
index 0000000..f170afe
--- /dev/null
+++ b/ui/src/lib/components/ScoreBadge.svelte
@@ -0,0 +1,14 @@
+
+
+= 18}
+ class:bg-info={score < 18 && score >= 15}
+ class:bg-warning={score < 15 && score >= 9}
+ class:bg-danger={score < 9}
+ class:bg-dark={score == "N/A"}
+>
+ {score}
+
diff --git a/ui/src/lib/components/SurveyList.svelte b/ui/src/lib/components/SurveyList.svelte
index 6b97e43..5ca0c68 100644
--- a/ui/src/lib/components/SurveyList.svelte
+++ b/ui/src/lib/components/SurveyList.svelte
@@ -4,6 +4,7 @@
import { user } from '$lib/stores/user';
import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
+ import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import { getCategories } from '$lib/categories';
import { getSurveys } from '$lib/surveys';
@@ -144,16 +145,7 @@
{/if}
{:else}
- = 18}
- class:bg-info={score.score < 18 && score.score >= 15}
- class:bg-warning={score.score < 15 && score.score >= 9}
- class:bg-danger={score.score < 9}
- class:bg-dark={score.score == "N/A"}
- >
- {score.score}
-
+