server/libfic/team_my.go

326 lines
9.2 KiB
Go

package fic
import (
"encoding/hex"
"fmt"
"log"
"path"
"strconv"
"strings"
"time"
)
// DisplayAllFlags doesn't respect the predefined constraint existing between flags.
var DisplayAllFlags bool
type myTeamFile struct {
Path string `json:"path"`
Name string `json:"name"`
Checksum string `json:"checksum"`
Size int64 `json:"size"`
}
type myTeamHint struct {
HintId int64 `json:"id"`
Title string `json:"title"`
Content string `json:"content,omitempty"`
File string `json:"file,omitempty"`
Cost int64 `json:"cost"`
}
type myTeamFlag struct {
Label string `json:"label"`
Placeholder string `json:"placeholder,omitempty"`
Separator string `json:"separator,omitempty"`
NbLines uint64 `json:"nb_lines,omitempty"`
IgnoreOrder bool `json:"ignore_order,omitempty"`
IgnoreCase bool `json:"ignore_case,omitempty"`
Multiline bool `json:"multiline,omitempty"`
ValidatorRe *string `json:"validator_regexp,omitempty"`
Solved *time.Time `json:"found,omitempty"`
Soluce string `json:"soluce,omitempty"`
Choices map[string]string `json:"choices,omitempty"`
ChoicesCost int64 `json:"choices_cost,omitempty"`
}
type myTeamMCQJustifiedChoice struct {
Label string `json:"label"`
Value bool `json:"value,omitempty"`
Justification myTeamFlag `json:"justification,omitempty"`
}
type myTeamMCQ struct {
Title string `json:"title"`
Justify bool `json:"justify,omitempty"`
Choices map[int64]interface{} `json:"choices,omitempty"`
Solved *time.Time `json:"solved,omitempty"`
PSolved *time.Time `json:"part_solved,omitempty"`
Soluce string `json:"soluce,omitempty"`
}
type myTeamExercice struct {
ThemeId int64 `json:"theme_id"`
Statement string `json:"statement"`
Overview string `json:"overview,omitempty"`
Finished string `json:"finished,omitempty"`
Hints []myTeamHint `json:"hints,omitempty"`
Gain int `json:"gain"`
Files []myTeamFile `json:"files,omitempty"`
Flags map[int64]myTeamFlag `json:"flags,omitempty"`
MCQs map[int64]myTeamMCQ `json:"mcqs,omitempty"`
SolveDist int64 `json:"solve_dist,omitempty"`
SolvedTime *time.Time `json:"solved_time,omitempty"`
SolvedRank int64 `json:"solved_rank,omitempty"`
Tries int64 `json:"tries,omitempty"`
TotalTries int64 `json:"total_tries,omitempty"`
VideoURI string `json:"video_uri,omitempty"`
Issue string `json:"issue,omitempty"`
IssueKind string `json:"issuekind,omitempty"`
}
type myTeam struct {
Id int64 `json:"team_id"`
Name string `json:"name"`
Points int64 `json:"score"`
Members []Member `json:"members"`
Exercices map[string]myTeamExercice `json:"exercices"`
}
func MyJSONTeam(t *Team, started bool) (interface{}, error) {
ret := myTeam{}
// Fill information about the team
if t == nil {
ret.Id = 0
} else {
ret.Name = t.Name
ret.Id = t.Id
points, _ := t.GetPoints()
ret.Points = int64(points)
if members, err := t.GetMembers(); err == nil {
ret.Members = members
}
}
// Fill exercices, only if the challenge is started
ret.Exercices = map[string]myTeamExercice{}
if exos, err := GetExercices(); err != nil {
return ret, err
} else if started {
for _, e := range exos {
if t == nil || t.HasAccess(e) {
exercice := myTeamExercice{}
exercice.ThemeId = e.IdTheme
exercice.Statement = strings.Replace(e.Statement, "$FILES$", FilesDir, -1)
if len(e.Issue) > 0 {
exercice.Issue = e.Issue
exercice.IssueKind = e.IssueKind
}
if t == nil {
exercice.Overview = strings.Replace(e.Overview, "$FILES$", FilesDir, -1)
exercice.Finished = strings.Replace(e.Finished, "$FILES$", FilesDir, -1)
exercice.VideoURI = e.VideoURI
exercice.TotalTries = e.TriedCount()
exercice.Gain = int(float64(e.Gain) * e.Coefficient)
} else {
solved, stime := t.HasSolved(e)
exercice.SolvedTime = &stime
if solved {
exercice.SolvedRank, _ = t.GetSolvedRank(e)
exercice.Finished = e.Finished
exercice.Tries, _ = t.CountTries(e)
} else {
exercice.Tries, stime = t.CountTries(e)
exercice.SolvedTime = &stime
if exercice.Tries > 0 {
exercice.SolveDist = t.LastTryDist(e)
}
}
if exercice.SolvedTime != nil && exercice.SolvedTime.Equal(time.Unix(0, 0)) {
exercice.SolvedTime = nil
}
if gain, err := e.EstimateGain(*t, solved); err == nil {
exercice.Gain = int(gain)
} else {
log.Println("ERROR during gain estimation:", err)
}
}
// Expose exercice files
exercice.Files = []myTeamFile{}
if files, err := e.GetFiles(); err != nil {
return nil, err
} else {
for _, f := range files {
if t == nil || t.CanDownload(f) {
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(f.Checksum), f.Size})
}
}
}
// Expose exercice hints
exercice.Hints = []myTeamHint{}
if hints, err := e.GetHints(); err != nil {
return nil, err
} else {
for _, h := range hints {
if t == nil || HintCoefficient < 0 || t.HasHint(h) {
exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, h.Content, h.File, h.Cost})
} else if t.CanSeeHint(h) {
exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, "", "", h.Cost})
}
}
}
// Expose exercice flags
justifiedMCQ := map[int64]myTeamFlag{}
exercice.Flags = map[int64]myTeamFlag{}
if flags, err := e.GetFlagKeys(); err != nil {
return nil, err
} else {
for _, k := range flags {
var flag myTeamFlag
if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) {
// Dependancy missing, skip the flag for now
continue
}
// Retrieve solved state or solution for public iface
if t == nil {
flag.IgnoreCase = k.IgnoreCase
flag.ValidatorRe = k.ValidatorRegexp
flag.Soluce = hex.EncodeToString(k.Checksum)
} else if PartialValidation {
flag.Solved = t.HasPartiallySolved(k)
}
flag.Multiline = k.Multiline
var fl FlagLabel
if fl, err = k.GetMCQJustification(); err == nil {
k.Label = fl.Label
}
// Treat array flags
if k.Label[0] == '`' && len(k.Label) > 4 {
flag.Label = k.Label[4:]
flag.Separator = string(k.Label[1])
flag.IgnoreOrder = k.Label[2] == 't'
flag.NbLines, _ = strconv.ParseUint(string(k.Label[3]), 10, 8)
} else {
flag.Label = k.Label
}
// Fill more information if the flag is displaied
if flag.Solved == nil {
flag.Placeholder = k.Placeholder
if choices, err := k.GetChoices(); err != nil {
return nil, err
} else if t == nil || WChoiceCoefficient < 0 || k.ChoicesCost == 0 || t.SeeChoices(k) {
flag.Choices = map[string]string{}
for _, c := range choices {
flag.Choices[c.Value] = c.Label
}
} else {
flag.ChoicesCost = k.ChoicesCost
}
}
// Append to corresponding flags' map
if fl.IdChoice != 0 {
justifiedMCQ[fl.IdChoice] = flag
} else {
exercice.Flags[k.Id] = flag
}
}
}
// Expose exercice MCQs
exercice.MCQs = map[int64]myTeamMCQ{}
if mcqs, err := e.GetMCQ(); err != nil {
return nil, err
} else {
for _, mcq := range mcqs {
if !DisplayAllFlags && t != nil && !t.CanSeeFlag(mcq) {
// Dependancy missing, skip the flag for now
continue
}
m := myTeamMCQ{
Title: mcq.Title,
Choices: map[int64]interface{}{},
}
soluce := ""
fullySolved := true
if t != nil {
m.PSolved = t.HasPartiallySolved(mcq)
}
for _, e := range mcq.Entries {
if e.Response {
soluce += "t"
} else {
soluce += "f"
}
if v, ok := justifiedMCQ[e.Id]; ok {
m.Justify = true
if m.PSolved != nil || v.Solved != nil {
jc := myTeamMCQJustifiedChoice{
Label: e.Label,
Value: v.Solved != nil,
Justification: v,
}
if v.Solved == nil {
fullySolved = false
}
if PartialValidation && m.PSolved != nil {
jc.Value = e.Response
}
m.Choices[e.Id] = jc
} else {
m.Choices[e.Id] = e.Label
}
} else {
m.Choices[e.Id] = e.Label
}
}
if t == nil {
h, _ := ComputeHashedFlag([]byte(soluce), false, nil)
m.Soluce = hex.EncodeToString(h[:])
}
if fullySolved {
m.Solved = m.PSolved
m.PSolved = nil
}
exercice.MCQs[mcq.Id] = m
}
}
// Hash table ordered by exercice Id
ret.Exercices[fmt.Sprintf("%d", e.Id)] = exercice
}
}
}
return ret, nil
}