WIP: feat: scoreboard (#15) #31
| 
						 | 
					@ -9,6 +9,7 @@ import (
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/scoreboard"
 | 
				
			||||||
	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 | 
						"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,16 @@ func readStageResults(t *testing.T, path string) []stage.StageResult {
 | 
				
			||||||
	return results
 | 
						return results
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func removeStringPrefix(s, prefix string) string {
 | 
				
			||||||
 | 
						if strings.HasPrefix(s, prefix) {
 | 
				
			||||||
 | 
							return s[len(prefix):]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestMain(t *testing.T) {
 | 
					func TestMain(t *testing.T) {
 | 
				
			||||||
 | 
						scoreboard := scoreboard.Scoreboard{}
 | 
				
			||||||
 | 
						scoreboard.Init("TestingStudent", "520370000000")
 | 
				
			||||||
	var tests []string
 | 
						var tests []string
 | 
				
			||||||
	root := "../../tmp/submodules/JOJ3-examples"
 | 
						root := "../../tmp/submodules/JOJ3-examples"
 | 
				
			||||||
	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
 | 
						err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
 | 
				
			||||||
| 
						 | 
					@ -107,6 +117,7 @@ func TestMain(t *testing.T) {
 | 
				
			||||||
			defer os.Remove(outputFile)
 | 
								defer os.Remove(outputFile)
 | 
				
			||||||
			main()
 | 
								main()
 | 
				
			||||||
			stageResults := readStageResults(t, outputFile)
 | 
								stageResults := readStageResults(t, outputFile)
 | 
				
			||||||
 | 
								scoreboard.AddScore(removeStringPrefix(tt, "/examples/"), stageResults)
 | 
				
			||||||
			regex := true
 | 
								regex := true
 | 
				
			||||||
			expectedFile := "expected_regex.json"
 | 
								expectedFile := "expected_regex.json"
 | 
				
			||||||
			if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
 | 
								if _, err := os.Stat(expectedFile); os.IsNotExist(err) {
 | 
				
			||||||
| 
						 | 
					@ -117,4 +128,9 @@ func TestMain(t *testing.T) {
 | 
				
			||||||
			compareStageResults(t, stageResults, expectedStageResults, regex)
 | 
								compareStageResults(t, stageResults, expectedStageResults, regex)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if !t.Failed() {
 | 
				
			||||||
 | 
							scoreboard.SaveFile("../../scoreboard.json")
 | 
				
			||||||
 | 
							defer os.Remove("../../scoreboard.json")
 | 
				
			||||||
 | 
							scoreboard.Submit()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,44 +1,40 @@
 | 
				
			||||||
package scoreboard
 | 
					package scoreboard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log/slog"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Question struct {
 | 
					type StageRecord struct {
 | 
				
			||||||
	Name    string `json:"name"`
 | 
						StageName    string              `json:"stagename"`
 | 
				
			||||||
	Score   int    `json:"score"`
 | 
						StageResults []stage.StageResult `json:"stageresults"`
 | 
				
			||||||
	Comment string `json:"comment"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ScoreboardData struct {
 | 
					type ScoreboardData struct {
 | 
				
			||||||
	StudentName string     `json:"studentname"`
 | 
						StageRecords []StageRecord `json:"stagerecords"`
 | 
				
			||||||
	StudentId   string     `json:"studentid"`
 | 
					 | 
				
			||||||
	Questions   []Question `json:"questions"`
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Scoreboard struct {
 | 
					type Scoreboard struct {
 | 
				
			||||||
	scoreboard ScoreboardData
 | 
						studentName string
 | 
				
			||||||
 | 
						studentId   string
 | 
				
			||||||
 | 
						scoreboard  ScoreboardData
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Scoreboard) Init(studentName string, studentId string) {
 | 
					func (b *Scoreboard) Init(studentName string, studentId string) {
 | 
				
			||||||
	b.scoreboard.StudentName = studentName
 | 
						b.studentName = studentName
 | 
				
			||||||
	b.scoreboard.StudentId = studentId
 | 
						b.studentId = studentId
 | 
				
			||||||
	b.scoreboard.Questions = make([]Question, 0)
 | 
						b.scoreboard.StageRecords = make([]StageRecord, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Scoreboard) AddScore(questionName string, score int) {
 | 
					func (b *Scoreboard) AddScore(stagename string, results []stage.StageResult) {
 | 
				
			||||||
	flag := false
 | 
						b.scoreboard.StageRecords = append(b.scoreboard.StageRecords,
 | 
				
			||||||
	for i, question := range b.scoreboard.Questions {
 | 
							StageRecord{StageName: stagename, StageResults: results})
 | 
				
			||||||
		if question.Name == questionName {
 | 
					 | 
				
			||||||
			b.scoreboard.Questions[i].Score = score
 | 
					 | 
				
			||||||
			flag = true
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if flag {
 | 
					 | 
				
			||||||
		b.scoreboard.Questions = append(b.scoreboard.Questions, Question{Name: questionName, Score: score})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (b *Scoreboard) SaveFile(filePath string) {
 | 
					func (b *Scoreboard) SaveFile(filePath string) {
 | 
				
			||||||
| 
						 | 
					@ -50,3 +46,54 @@ func (b *Scoreboard) SaveFile(filePath string) {
 | 
				
			||||||
	encoder.SetIndent("", "  ")
 | 
						encoder.SetIndent("", "  ")
 | 
				
			||||||
	_ = encoder.Encode(b.scoreboard)
 | 
						_ = encoder.Encode(b.scoreboard)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Scoreboard) Submit() {
 | 
				
			||||||
 | 
						e := os.Mkdir("repos", 0o777)
 | 
				
			||||||
 | 
						if e != nil {
 | 
				
			||||||
 | 
							slog.Error("Encountered problems createing folder: ", "err", e)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var teapotCommand string
 | 
				
			||||||
 | 
						origHome := os.Getenv("ORIG_HOME")
 | 
				
			||||||
 | 
						if origHome != "" {
 | 
				
			||||||
 | 
							// For drone server
 | 
				
			||||||
 | 
							teapotCommand = fmt.Sprintf("cp %s/.config/ci/teapot.env .env && joint-teapot JOJ3-scoreboard \"../../scoreboard.json\" %s %s \"JOJ3-examples\" \"JOJ3_dev.csv\"", origHome, b.studentName, b.studentId)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// For local developers
 | 
				
			||||||
 | 
							jointTeapotRoot := os.Getenv("TEAPOT_ROOT")
 | 
				
			||||||
 | 
							if jointTeapotRoot == "" {
 | 
				
			||||||
 | 
								slog.Error("Unable to find joint-teapot. Have you configured environment variable TEAPOT_ROOT?")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							teapotCommand = fmt.Sprintf("cp %s/.env .env && source %s/env/bin/activate && python3 -m joint_teapot JOJ3-scoreboard \"../../scoreboard.json\" %s %s \"JOJ3-examples\" \"JOJ3_dev.csv\" && deactivate", jointTeapotRoot, jointTeapotRoot, b.studentName, b.studentId)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(".env")
 | 
				
			||||||
 | 
						teapotCmd := exec.Command("/bin/bash", "-c", teapotCommand)
 | 
				
			||||||
 | 
						defer os.Remove("joint-teapot.log")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stderrPipe, err := teapotCmd.StderrPipe()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							slog.Error("Error creating stderr pipe when submitting scoreboard: ", "err", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = teapotCmd.Start()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							slog.Error("Error starting command when submitting scoreboard: ", "err", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var stderr bytes.Buffer
 | 
				
			||||||
 | 
						_, err = stderr.ReadFrom(stderrPipe)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							slog.Error("Error reading stderr when submitting scoreboard: ", "err", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = teapotCmd.Wait()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							slog.Error("Tried to submit scoreboard, but it finished with error: ", "err", err)
 | 
				
			||||||
 | 
							slog.Error("Stderr output:", "stderr", stderr.String())
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						slog.Info("Scoreboard was submitted with following outputs: ", "stderr", stderr.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user