challenge-sync-airbus: Done

This commit is contained in:
nemunaire 2023-04-06 15:37:05 +02:00
parent 268925db0d
commit 0d5b87b3f7
6 changed files with 73 additions and 68 deletions

View File

@ -1,14 +1,15 @@
package main package main
import ( import (
"bytes"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings"
) )
type AirbusAPI struct { type AirbusAPI struct {
@ -18,14 +19,18 @@ type AirbusAPI struct {
SessionUUID string SessionUUID string
} }
func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{}) error { func (a *AirbusAPI) request(method, endpoint string, data io.Reader, out interface{}) error {
var req *http.Request var req *http.Request
var err error var err error
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
if data != nil { if data != nil {
req, err = http.NewRequest(method, a.BaseURL+endpoint, bytes.NewReader(data)) req, err = http.NewRequest(method, a.BaseURL+endpoint, data)
} else { } else {
req, err = http.NewRequest(method, a.BaseURL+endpoint, nil) req, err = http.NewRequest(method, a.BaseURL+endpoint, nil)
} }
@ -34,9 +39,9 @@ func (a *AirbusAPI) request(method, endpoint string, data []byte, out interface{
} }
req.Header.Add("Authorization", "Bearer "+a.Token) req.Header.Add("Authorization", "Bearer "+a.Token)
req.Header.Add("Content-Type", "application/json") req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := http.DefaultClient.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("error during request execution to %q: %w", endpoint, err) return fmt.Errorf("error during request execution to %q: %w", endpoint, err)
} }
@ -152,8 +157,8 @@ func (a *AirbusAPI) GetChallengeFromName(name string) (*AirbusChallenge, error)
} }
func (a *AirbusAPI) ValidateChallengeFromUser(team *AirbusTeam, challengeId AirbusChallengeId) (err error) { func (a *AirbusAPI) ValidateChallengeFromUser(team *AirbusTeam, challengeId AirbusChallengeId) (err error) {
log.Printf("ValidateChallenge: %d, %s, %d", a.SessionID, challengeId.String(), team.Members[0].ID)
if dryRun { if dryRun {
log.Printf("ValidateChallenge: %d, %s, %d", a.SessionID, challengeId.String(), team.Members[0].ID)
return return
} }
@ -174,17 +179,17 @@ func (a *AirbusAPI) AwardUser(team *AirbusTeam, value int64, message string) (er
Value: value, Value: value,
} }
log.Printf("AwardUser: %v", awards)
if dryRun { if dryRun {
log.Printf("AwardUser: %v", awards)
return return
} }
j, err := json.Marshal(awards) data := url.Values{}
if err != nil { data.Set("gaming_user_id", fmt.Sprintf("%d", awards.UserId))
return fmt.Errorf("unable to marshall JSON from awards struct: %w", err) data.Set("name", awards.Message)
} data.Set("value", fmt.Sprintf("%d", awards.Value))
err = a.request("POST", fmt.Sprintf("/v1/sessions/%d/awards", a.SessionID), j, nil) err = a.request("POST", fmt.Sprintf("/v1/sessions/%d/awards", a.SessionID), strings.NewReader(data.Encode()), nil)
if err != nil { if err != nil {
return err return err
} }

View File

@ -62,6 +62,12 @@ func main() {
break break
} }
} }
if api.SessionID == 0 {
log.Fatal("Session ID not found")
} else {
log.Println("Session ID discovered: ", api.SessionID)
}
} }
if v, exists := os.LookupEnv("AIRBUS_SESSIONUUID"); exists { if v, exists := os.LookupEnv("AIRBUS_SESSIONUUID"); exists {
@ -83,6 +89,7 @@ func main() {
if err != nil { if err != nil {
log.Fatal("Unable to open teams bindings file: ", err.Error()) log.Fatal("Unable to open teams bindings file: ", err.Error())
} }
log.Println("Team bindings loaded: ", len(teamsbindings))
w := Walker{ w := Walker{
LastSync: ts, LastSync: ts,
@ -103,10 +110,9 @@ func main() {
} }
if !skipInitialSync { if !skipInitialSync {
// Iterate over teams scores err = w.BalanceScores()
err = filepath.WalkDir(TeamsDir, w.WalkScoreSync)
if err != nil { if err != nil {
log.Println("Something goes wrong during walking: ", err.Error()) log.Println("Something goes wrong during score balance: ", err.Error())
} }
// save current timestamp for teams // save current timestamp for teams
@ -116,6 +122,8 @@ func main() {
} }
} }
log.Println("initial sync done")
if daemon != nil && *daemon { if daemon != nil && *daemon {
// Watch teams.json and scores.json // Watch teams.json and scores.json
log.Println("Registering directory events...") log.Println("Registering directory events...")
@ -196,7 +204,7 @@ func main() {
if err := watchsubdir(watcher, ev.Name); err != nil { if err := watchsubdir(watcher, ev.Name); err != nil {
log.Println(err) log.Println(err)
} }
} else if ev.Op&watchedNotify == watchedNotify && d.Mode().IsRegular() { } else if err == nil && ev.Op&watchedNotify == watchedNotify && d.Mode().IsRegular() {
if *debugINotify { if *debugINotify {
log.Println("Treating event:", ev, "for", ev.Name) log.Println("Treating event:", ev, "for", ev.Name)
} }
@ -213,10 +221,10 @@ func main() {
log.Fatal("Unable to fetch teams: ", err.Error()) log.Fatal("Unable to fetch teams: ", err.Error())
} }
} }
} else if ev.Op&fsnotify.Write == fsnotify.Write { } else if err == nil && ev.Op&fsnotify.Write == fsnotify.Write {
log.Println("FSNOTIFY WRITE SEEN. Prefer looking at them, as it appears files are not atomically moved.") log.Println("FSNOTIFY WRITE SEEN. Prefer looking at them, as it appears files are not atomically moved.")
watchedNotify = fsnotify.Write watchedNotify = fsnotify.Write
} else if *debugINotify { } else if err == nil && *debugINotify {
log.Println("Skipped event:", ev, "for", ev.Name) log.Println("Skipped event:", ev, "for", ev.Name)
} }
case err := <-watcher.Errors: case err := <-watcher.Errors:

View File

@ -10,6 +10,6 @@ type Session struct {
} }
func (a *AirbusAPI) GetSessions() (ret []Session, err error) { func (a *AirbusAPI) GetSessions() (ret []Session, err error) {
err = a.request("GET", "/api/v1/sessions", nil, &ret) err = a.request("GET", "/v1/sessions", nil, &ret)
return return
} }

View File

@ -23,7 +23,7 @@ type airbusDataTeam struct {
func (a *AirbusAPI) GetTeams() ([]AirbusTeam, error) { func (a *AirbusAPI) GetTeams() ([]AirbusTeam, error) {
var data airbusDataTeam var data airbusDataTeam
err := a.request("GET", fmt.Sprintf("/api/v1/sessions/%d/teams", a.SessionID), nil, &data) err := a.request("GET", fmt.Sprintf("/v1/sessions/%d/teams", a.SessionID), nil, &data)
if err != nil { if err != nil {
return nil, err return nil, err
} else { } else {

View File

@ -2,6 +2,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"log"
"os" "os"
"time" "time"
) )
@ -32,6 +33,15 @@ func loadTS(tspath string) (timestamp map[string]*TSValue, err error) {
} }
func saveTS(tspath string, ts map[string]*TSValue) error { func saveTS(tspath string, ts map[string]*TSValue) error {
if dryRun {
tmp := map[string]TSValue{}
for k, v := range ts {
tmp[k] = *v
}
log.Println("saving TS: ", tmp)
return nil
}
if fd, err := os.Create(tspath); err != nil { if fd, err := os.Create(tspath); err != nil {
return err return err
} else { } else {

View File

@ -6,6 +6,7 @@ import (
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"time"
"srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/libfic"
) )
@ -51,12 +52,14 @@ func (w *Walker) fetchTeams() error {
return nil return nil
} }
func (w *Walker) treat(path string) { func (w *Walker) treat(path string) error {
teamid := filepath.Base(filepath.Dir(path)) teamid := filepath.Base(filepath.Dir(path))
if _, ok := w.TeamBindings[teamid]; ok { if _, ok := w.TeamBindings[teamid]; ok {
w.TreatScoreGrid(path, w.TeamBindings[teamid]) return w.TreatScoreGrid(path, w.TeamBindings[teamid])
} }
return nil
} }
func (w *Walker) LoadScoreState(path string) (int64, error) { func (w *Walker) LoadScoreState(path string) (int64, error) {
@ -107,30 +110,7 @@ func (w *Walker) LoadScoreGrid(path string) ([]fic.ScoreGridRow, error) {
func (w *Walker) WalkScoreSync(path string, d os.DirEntry, err error) error { func (w *Walker) WalkScoreSync(path string, d os.DirEntry, err error) error {
if filepath.Base(path) == "scores.json" { if filepath.Base(path) == "scores.json" {
w.treat(path) return w.treat(path)
}
for team, ts := range w.LastSync {
team_id, ok := w.RevTeams[team]
if !ok {
continue
}
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)]
if ts.Score != myteam.Points*int64(w.Coeff) {
err := w.API.AwardUser(airbusTeam, myteam.Points-ts.Score, "Équilibrage")
if err != nil {
return fmt.Errorf("Unable to open %s/my.json: %w", team, err)
}
w.LastSync[airbusTeam.Name].Score = myteam.Points * int64(w.Coeff)
}
} }
return nil return nil
@ -174,7 +154,9 @@ func (w *Walker) TreatScoreGrid(path string, airbusTeam *AirbusTeam) error {
} }
// Found all new entries // Found all new entries
maxts := &TSValue{} maxts := &TSValue{
Time: time.Time{},
}
if ts, ok := w.LastSync[airbusTeam.Name]; ok { if ts, ok := w.LastSync[airbusTeam.Name]; ok {
maxts = ts maxts = ts
} }
@ -182,7 +164,7 @@ func (w *Walker) TreatScoreGrid(path string, airbusTeam *AirbusTeam) error {
if row.Time.After(maxts.Time) { if row.Time.After(maxts.Time) {
maxts.Time = row.Time maxts.Time = row.Time
} }
if row.Time.After(w.LastSync[airbusTeam.Name].Time) { if ts, ok := w.LastSync[airbusTeam.Name]; !ok || row.Time.After(ts.Time) {
if !noValidateChallenge && row.Reason == "Validation" { if !noValidateChallenge && row.Reason == "Validation" {
err = w.API.ValidateChallengeFromUser(airbusTeam, w.Exercices[row.IdExercice]) err = w.API.ValidateChallengeFromUser(airbusTeam, w.Exercices[row.IdExercice])
} else { } else {
@ -202,29 +184,29 @@ func (w *Walker) TreatScoreGrid(path string, airbusTeam *AirbusTeam) error {
return nil return nil
} }
func (w *Walker) BalanceScore(score int64, airbusTeam *AirbusTeam) error { func (w *Walker) BalanceScores() error {
// Read current score on other platform for team, ts := range w.LastSync {
stats, err := w.API.GetCurrentStats() team_id, ok := w.RevTeams[team]
if err != nil { if !ok {
fmt.Errorf("unable to retrieve current stats: %w", err) continue
} }
my_session := stats.Data.GetSession(AirbusUUID(w.API.SessionUUID)) myteam, err := w.loadMyFile(filepath.Join(TeamsDir, team_id, "my.json"))
if my_session == nil { if err != nil {
return fmt.Errorf("session not found") return fmt.Errorf("Unable to open %s/my.json: %w", team_id, err)
} }
other_team := my_session.GetTeam(AirbusUUID(airbusTeam.Name)) airbusTeam := w.TeamBindings[fmt.Sprintf("%d", myteam.Id)]
if other_team == nil {
return fmt.Errorf("team %q not found", airbusTeam.Name)
}
other_score := other_team.Score if ts.Score != myteam.Points*int64(w.Coeff) {
err := w.API.AwardUser(airbusTeam, myteam.Points*int64(w.Coeff)-ts.Score, "Équilibrage")
if err != nil {
return fmt.Errorf("Unable to open %s/my.json: %w", team, err)
}
// Send diff to the platform w.LastSync[airbusTeam.Name].Score = myteam.Points * int64(w.Coeff)
if other_score != score { w.LastSync[airbusTeam.Name].Time = time.Now()
diff := score - other_score }
return w.API.AwardUser(airbusTeam, diff, "Équilibrage")
} }
return nil return nil