diff --git a/go.mod b/go.mod index 345f970c..d08fe920 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/urfave/negroni v1.0.0 // indirect github.com/wcharczuk/go-chart v2.0.1+incompatible // indirect golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d // indirect + golang.org/x/exp/errors v0.0.0-20201229011636-eab1b5eb1a03 golang.org/x/image v0.0.0-20200119044424-58c23975cae1 // indirect golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect @@ -49,4 +50,5 @@ require ( gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/russross/blackfriday.v2 v2.0.0 gopkg.in/yaml.v2 v2.2.4 + gorm.io/gorm v1.20.11 ) diff --git a/go.sum b/go.sum index b68b3aa2..1e22bc9b 100644 --- a/go.sum +++ b/go.sum @@ -110,6 +110,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -196,7 +198,11 @@ golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522 h1:OeRHuibLsmZkFj773W4LcfAGsSxJgfPONhr8cmO+eLA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20201229011636-eab1b5eb1a03 h1:XlAInxBYX5nBofPaY51uv/x9xmRgZGr/lDOsePd2AcE= +golang.org/x/exp/errors v0.0.0-20201229011636-eab1b5eb1a03 h1:A4FptnpJ/z3jSXVmYXbcrFGO2wsfwGqel7BJcgT99hU= +golang.org/x/exp/errors v0.0.0-20201229011636-eab1b5eb1a03/go.mod h1:YgqsNsAu4fTvlab/7uiYK9LJrCIzKg/NiZUIH1/ayqo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg= golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -304,6 +310,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gorm.io/gorm v1.20.11 h1:jYHQ0LLUViV85V8dM1TP9VBBkfzKTnuTXDjYObkI6yc= +gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/orm/school.go b/orm/school.go index feab8c26..ffb806e0 100644 --- a/orm/school.go +++ b/orm/school.go @@ -240,6 +240,7 @@ func (model *School) ReadAll(db *Database, args map[string]string, w http.Respon if err := db._db. Preload("Participants"). Preload("Participants.Category"). + Preload("Region"). Order("code"). Find(&schools).Error; err != nil { return nil, err diff --git a/scripts/rank/.gitignore b/scripts/rank/.gitignore new file mode 100644 index 00000000..afed0735 --- /dev/null +++ b/scripts/rank/.gitignore @@ -0,0 +1 @@ +*.csv diff --git a/scripts/rank/main.go b/scripts/rank/main.go new file mode 100644 index 00000000..03016e53 --- /dev/null +++ b/scripts/rank/main.go @@ -0,0 +1,144 @@ +package main + +import ( + "flag" + "log" + "os" + "strconv" + "strings" + "time" + + "git.andreafazzi.eu/andrea/oef/client" + "git.andreafazzi.eu/andrea/oef/orm" + "github.com/gocarina/gocsv" + "golang.org/x/exp/errors/fmt" +) + +type Response struct { + Firstname string + Lastname string + School string + Region string + Score int + Duration uint +} + +func findAnswer(answers []*orm.Answer, id uint) (*orm.Answer, error) { + for _, a := range answers { + if a.ID == id { + return a, nil + } + } + return nil, fmt.Errorf("Answer with ID %v not found", id) +} + +func findSchool(schools []*orm.School, id uint) (*orm.School, error) { + for _, s := range schools { + if s.ID == id { + return s, nil + } + } + return nil, fmt.Errorf("School with ID %v not found", id) +} + +func calcScoreAndDuration(answers []*orm.Answer, response *orm.Response) error { + response.Score = 0 + if response.AnswersIDs != "" { + srIDs := strings.Split(response.AnswersIDs, " ") + for _, srID := range srIDs { + id, err := strconv.Atoi(srID) + if err != nil { + return err + } + response.SingleResponses = append(response.SingleResponses, &orm.SingleResponse{uint(id)}) + } + + for _, sr := range response.SingleResponses { + answer, err := findAnswer(answers, sr.AnswerID) + if err != nil { + panic(err) + } + if answer.Correct { + response.Score++ + } + } + + } + + if !response.StartTime.IsZero() { + response.Duration = response.UpdatedAt.Sub(response.StartTime) + } + + return nil +} + +func main() { + username := flag.String("username", "admin", "Username") + password := flag.String("password", "admin", "Password") + output := flag.String("output", "rank.csv", "Output filename") + contestId := flag.Int("id", 0, "Contest ID") + + flag.Parse() + + log.Printf("Contacting %v", flag.Arg(0)) + client, err := client.Dial(flag.Arg(0), *username, *password) + if err != nil { + panic(err) + } + + log.Println("Fetching all the schools...") + schools := make([]*orm.School, 0) + err = client.ReadAll(&schools) + if err != nil { + panic(err) + } + + log.Println("Fetching all the responses...") + responses := make([]*orm.Response, 0) + err = client.ReadAll(&responses) + if err != nil { + panic(err) + } + + log.Println("Fetching all the answers...") + answers := make([]*orm.Answer, 0) + err = client.ReadAll(&answers) + if err != nil { + panic(err) + } + + csvResponses := make([]*Response, 0) + + for _, r := range responses { + if r.ContestID != uint(*contestId) { + continue + } + err := calcScoreAndDuration(answers, r) + if err != nil { + panic(err) + } + + school, err := findSchool(schools, r.Participant.SchoolID) + + csvResponses = append( + csvResponses, + &Response{ + Firstname: r.Participant.Firstname, + Lastname: r.Participant.Lastname, + School: school.String(), + Region: school.Region.String(), + Score: r.Score, + Duration: uint(r.Duration / time.Second), + }, + ) + } + + f, err := os.Create(*output) + if err != nil { + panic(err) + } + defer f.Close() + + gocsv.MarshalFile(csvResponses, f) + +} diff --git a/scripts/rank/rank b/scripts/rank/rank new file mode 100755 index 00000000..136bfc81 Binary files /dev/null and b/scripts/rank/rank differ