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 }