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
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strconv"
"strings"
)
type AirbusAPI struct {
@ -18,14 +19,18 @@ type AirbusAPI struct {
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 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 {
req, err = http.NewRequest(method, a.BaseURL+endpoint, bytes.NewReader(data))
req, err = http.NewRequest(method, a.BaseURL+endpoint, data)
} else {
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("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 {
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) {
log.Printf("ValidateChallenge: %d, %s, %d", a.SessionID, challengeId.String(), team.Members[0].ID)
if dryRun {
log.Printf("ValidateChallenge: %d, %s, %d", a.SessionID, challengeId.String(), team.Members[0].ID)
return
}
@ -174,17 +179,17 @@ func (a *AirbusAPI) AwardUser(team *AirbusTeam, value int64, message string) (er
Value: value,
}
log.Printf("AwardUser: %v", awards)
if dryRun {
log.Printf("AwardUser: %v", awards)
return
}
j, err := json.Marshal(awards)
if err != nil {
return fmt.Errorf("unable to marshall JSON from awards struct: %w", err)
}
data := url.Values{}
data.Set("gaming_user_id", fmt.Sprintf("%d", awards.UserId))
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 {
return err
}

View File

@ -62,6 +62,12 @@ func main() {
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 {
@ -83,6 +89,7 @@ func main() {
if err != nil {
log.Fatal("Unable to open teams bindings file: ", err.Error())
}
log.Println("Team bindings loaded: ", len(teamsbindings))
w := Walker{
LastSync: ts,
@ -103,10 +110,9 @@ func main() {
}
if !skipInitialSync {
// Iterate over teams scores
err = filepath.WalkDir(TeamsDir, w.WalkScoreSync)
err = w.BalanceScores()
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
@ -116,6 +122,8 @@ func main() {
}
}
log.Println("initial sync done")
if daemon != nil && *daemon {
// Watch teams.json and scores.json
log.Println("Registering directory events...")
@ -196,7 +204,7 @@ func main() {
if err := watchsubdir(watcher, ev.Name); err != nil {
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 {
log.Println("Treating event:", ev, "for", ev.Name)
}
@ -213,10 +221,10 @@ func main() {
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.")
watchedNotify = fsnotify.Write
} else if *debugINotify {
} else if err == nil && *debugINotify {
log.Println("Skipped event:", ev, "for", ev.Name)
}
case err := <-watcher.Errors:

View File

@ -10,6 +10,6 @@ type Session struct {
}
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
}

View File

@ -23,7 +23,7 @@ type airbusDataTeam struct {
func (a *AirbusAPI) GetTeams() ([]AirbusTeam, error) {
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 {
return nil, err
} else {

View File

@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"log"
"os"
"time"
)
@ -32,6 +33,15 @@ func loadTS(tspath string) (timestamp map[string]*TSValue, err 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 {
return err
} else {

View File

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