server/remote/challenge-sync-airbus/treat.go

224 lines
4.9 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"math"
"os"
"path/filepath"
"time"
"srs.epita.fr/fic-server/libfic"
)
var (
noValidateChallenge bool
)
type Walker struct {
LastSync map[string]*TSValue
Exercices AirbusExercicesBindings
Teams map[string]fic.ExportedTeam
RevTeams map[string]string
TeamBindings map[string]*AirbusTeam
API AirbusAPI
Coeff float64
}
func (w *Walker) fetchTeams() error {
teams, err := w.API.GetTeams()
if err != nil {
return err
}
w.RevTeams = map[string]string{}
w.TeamBindings = map[string]*AirbusTeam{}
for tid, team := range w.Teams {
for i, t := range teams {
if team.Name == t.Name || team.ExternalId == t.Name {
w.TeamBindings[tid] = &teams[i]
break
}
}
if _, ok := w.TeamBindings[tid]; !ok {
log.Printf("Team binding not found: %s - %s", tid, team.Name)
}
w.RevTeams[team.Name] = tid
}
return nil
}
func (w *Walker) treat(path string) error {
teamid := filepath.Base(filepath.Dir(path))
if _, ok := w.TeamBindings[teamid]; ok {
return w.TreatScoreGrid(path, w.TeamBindings[teamid])
}
return nil
}
func (w *Walker) LoadScoreState(path string) (int64, error) {
mypath := filepath.Join(filepath.Dir(path), "airbus.json")
if _, err := os.Stat(mypath); os.IsNotExist(err) {
fd, err := os.Create(mypath)
if err != nil {
return 0, err
}
defer fd.Close()
fd.Write([]byte("0"))
return 0, nil
}
fd, err := os.Open(mypath)
if err != nil {
return 0, err
}
defer fd.Close()
var ret int64
jdec := json.NewDecoder(fd)
if err := jdec.Decode(&ret); err != nil {
return 0, fmt.Errorf("an error occurs when trying to decode airbus.json: %w", err)
}
return ret, nil
}
func (w *Walker) LoadScoreGrid(path string) ([]fic.ScoreGridRow, error) {
fd, err := os.Open(filepath.Join(filepath.Dir(path), "scores.json"))
if err != nil {
return nil, err
}
defer fd.Close()
var ret []fic.ScoreGridRow
jdec := json.NewDecoder(fd)
if err := jdec.Decode(&ret); err != nil {
return nil, fmt.Errorf("an error occurs when trying to decode airbus.json: %w", err)
}
return ret, nil
}
func (w *Walker) WalkScoreSync(path string, d os.DirEntry, err error) error {
if filepath.Base(path) == "scores.json" {
return w.treat(path)
}
return nil
}
func (w *Walker) loadMyFile(path string) (*fic.MyTeam, error) {
fd, err := os.Open(path)
if err != nil {
return nil, err
}
defer fd.Close()
var ret fic.MyTeam
jdec := json.NewDecoder(fd)
if err := jdec.Decode(&ret); err != nil {
return nil, fmt.Errorf("an error occurs when trying to decode airbus.json: %w", err)
}
return &ret, nil
}
func (w *Walker) WalkScore(path string, d os.DirEntry, err error) error {
if filepath.Base(path) == "scores.json" {
go w.treat(path)
}
return nil
}
func (w *Walker) TreatScoreGrid(path string, airbusTeam *AirbusTeam) error {
// Read score grid
fdscores, err := os.Open(path)
if err != nil {
return err
}
defer fdscores.Close()
teamscores, err := fic.ReadScoreGrid(fdscores)
if err != nil {
return err
}
// Found all new entries
maxts := TSValue{
Time: time.Time{},
}
ts, ok := w.LastSync[airbusTeam.Name]
if ok {
maxts = *ts
} else {
ts = &TSValue{Time: time.Time{}}
}
for _, row := range teamscores {
if row.Time.After(ts.Time) {
if !noValidateChallenge && row.Reason == "Validation" {
err = w.API.ValidateChallengeFromUser(airbusTeam, w.Exercices[row.IdExercice])
} else if row.Reason == "Tries" {
// Just add 1 try at a time as the field is updated
row.Points = fic.TermTriesSeq(fic.ReverseTriesPoints(int64(row.Points)))
err = w.API.AwardUser(airbusTeam, int64(math.Trunc(row.Points*row.Coeff*w.Coeff)), row.Reason)
} else {
err = w.API.AwardUser(airbusTeam, int64(row.Points*row.Coeff*w.Coeff), row.Reason)
}
if err != nil {
return err
}
maxts.Score += int64(row.Points * row.Coeff * w.Coeff)
}
if row.Time.After(maxts.Time) {
maxts.Time = row.Time
}
}
w.LastSync[airbusTeam.Name] = &maxts
return nil
}
func (w *Walker) BalanceScores() error {
for team_id, team := range w.Teams {
myteam, err := w.loadMyFile(filepath.Join(TeamsDir, team_id, "my.json"))
if err != nil {
return fmt.Errorf("Unable to open %s/my.json: %w", team_id, err)
}
airbusTeam := w.TeamBindings[fmt.Sprintf("%d", myteam.Id)]
expected_score := int64(math.Floor(float64(myteam.Points) * w.Coeff))
if airbusTeam == nil {
log.Printf("Skip team %q (tid=%d): no binding found", myteam.Name, myteam.Id)
} else if airbusTeam.Score != expected_score {
err := w.API.AwardUser(airbusTeam, expected_score-airbusTeam.Score, "Équilibrage")
if err != nil {
return fmt.Errorf("Unable to open %s/my.json: %w", team, err)
}
if _, ok := w.LastSync[airbusTeam.Name]; !ok {
w.LastSync[airbusTeam.Name] = &TSValue{}
}
w.LastSync[airbusTeam.Name].Score = expected_score
w.LastSync[airbusTeam.Name].Time = time.Now()
}
}
return nil
}