package fic import ( "encoding/hex" "encoding/json" "fmt" "log" "os" "path" "sort" "strconv" "strings" "time" ) // DisplayAllFlags doesn't respect the predefined constraint existing between flags. var DisplayAllFlags bool // DisplayMCQBadCount activates the report of MCQ bad responses counter. var DisplayMCQBadCount bool // HideCaseSensitivity never tells the user if the flag is case sensitive or not. var HideCaseSensitivity bool // UnlockedStandaloneExercices unlock this number of standalone exercice. var UnlockedStandaloneExercices int // UnlockedStandaloneExercicesByThemeStepValidation unlock this number of standalone exercice for each theme step validated. var UnlockedStandaloneExercicesByThemeStepValidation float64 // UnlockedStandaloneExercicesByStandaloneExerciceValidation unlock this number of standalone exercice for each standalone exercice validated. var UnlockedStandaloneExercicesByStandaloneExerciceValidation float64 type myTeamFile struct { Path string `json:"path"` Name string `json:"name"` Checksum string `json:"checksum"` Compressed bool `json:"compressed,omitempty"` 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 { Id int `json:"id,omitempty"` Label string `json:"label"` Type string `json:"type,omitempty"` Placeholder string `json:"placeholder,omitempty"` Help string `json:"help,omitempty"` Unit string `json:"unit,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"` CaptureRe *string `json:"capture_regexp,omitempty"` Solved *time.Time `json:"found,omitempty"` PSolved *time.Time `json:"part_solved,omitempty"` Soluce string `json:"soluce,omitempty"` Justify bool `json:"justify,omitempty"` BonusGain int64 `json:"bonus_gain,omitempty"` Choices map[string]interface{} `json:"choices,omitempty"` ChoicesCost int64 `json:"choices_cost,omitempty"` Variant string `json:"variant,omitempty"` Min *float64 `json:"min,omitempty"` Max *float64 `json:"max,omitempty"` Step *float64 `json:"step,omitempty"` order int8 } type myTeamMCQJustifiedChoice struct { Label string `json:"label"` Value bool `json:"value,omitempty"` Justification myTeamFlag `json:"justification,omitempty"` } type myTeamExercice struct { ThemeId int64 `json:"theme_id,omitempty"` Disabled bool `json:"disabled,omitempty"` WIP bool `json:"wip,omitempty"` 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 []myTeamFlag `json:"flags,omitempty"` NbFlags int `json:"nb_flags,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"` Resolution string `json:"resolution,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"` Points100 int64 `json:"score100"` Members []*Member `json:"members,omitempty"` Exercices map[string]myTeamExercice `json:"exercices"` } type ByOrder []myTeamFlag func (a ByOrder) Len() int { return len(a) } func (a ByOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByOrder) Less(i, j int) bool { return a[i].order < a[j].order } 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(float64(points) * GlobalScoreCoefficient) ret.Points100 = int64(float64(points) * GlobalScoreCoefficient * 100) if members, err := t.GetMembers(); err == nil { ret.Members = members } } // Retrieve themes themes, err := GetThemes() if err != nil { return ret, err } mapthemes := map[int64]*Theme{} for _, theme := range themes { mapthemes[theme.Id] = theme } // Fill exercices, only if the challenge is started ret.Exercices = map[string]myTeamExercice{} if exos, err := GetDiscountedExercices(); err != nil { return ret, err } else if started { for _, e := range exos { if t == nil || ((!e.Disabled || (e.IdTheme != nil && !mapthemes[*e.IdTheme].Locked)) && t.HasAccess(e)) { exercice := myTeamExercice{} exercice.Disabled = e.Disabled exercice.WIP = e.WIP if e.IdTheme != nil { 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 = strings.Replace(e.VideoURI, "$FILES$", FilesDir, -1) exercice.Resolution = strings.Replace(e.Resolution, "$FILES$", FilesDir, -1) exercice.TotalTries = e.TriedCount() exercice.Gain = int(float64(e.Gain) * e.Coefficient * GlobalScoreCoefficient) } else { stime := t.HasSolved(e) exercice.SolvedTime = stime if stime != nil { exercice.SolvedRank, _ = t.GetSolvedRank(e) exercice.Finished = e.Finished exercice.Tries, _ = t.CountTries(e) } else { var ttime *time.Time exercice.Tries, ttime = t.CountTries(e) exercice.SolvedTime = ttime if DisplayMCQBadCount && 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, stime != nil); err == nil { exercice.Gain = int(gain * GlobalScoreCoefficient) } else { log.Printf("ERROR during gain estimation (tid=%d ; eid=%d): %s", t.Id, e.Id, err.Error()) exercice.Gain = int(float64(e.Gain) * GlobalScoreCoefficient) } } // 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) { cksum := f.Checksum compressed := false if len(f.ChecksumShown) > 0 { cksum = f.ChecksumShown compressed = true } exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(cksum), compressed, 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, int64(float64(h.Cost) * GlobalScoreCoefficient)}) } else if t.CanSeeHint(h) { exercice.Hints = append(exercice.Hints, myTeamHint{h.Id, h.Title, "", "", int64(float64(h.Cost) * GlobalScoreCoefficient)}) } } } // Expose exercice flags if !e.Disabled { justifiedMCQ := map[int]myTeamFlag{} if labels, err := e.GetFlagLabels(); err != nil { return nil, err } else { for _, l := range labels { if !DisplayAllFlags && t != nil && !t.CanSeeFlag(l) { // Dependancy missing, skip the flag for now continue } exercice.Flags = append(exercice.Flags, myTeamFlag{ order: l.Order, Label: l.Label, Variant: l.Variant, }) } } if flags, err := e.GetFlagKeys(); err != nil { return nil, err } else { for _, k := range flags { if !strings.HasPrefix(k.Type, "label") { exercice.NbFlags += 1 } if !DisplayAllFlags && t != nil && !t.CanSeeFlag(k) { // Dependancy missing, skip the flag for now continue } flag := myTeamFlag{ Id: k.Id, Type: k.Type, order: k.Order, Help: k.Help, Unit: k.Unit, } if strings.HasPrefix(flag.Type, "number") { flag.Min, flag.Max, flag.Step, err = AnalyzeNumberFlag(flag.Type) flag.Type = "number" if err != nil { return nil, err } } // Retrieve solved state or solution for public iface if t == nil { flag.IgnoreCase = k.IgnoreCase flag.CaptureRe = k.CaptureRegexp flag.Soluce = hex.EncodeToString(k.Checksum) } else { if PartialValidation { flag.Solved = t.HasPartiallySolved(k) } if !HideCaseSensitivity { flag.IgnoreCase = k.IgnoreCase } } flag.Multiline = k.Multiline flag.BonusGain = int64(float64(k.BonusGain) * GlobalScoreCoefficient) var fl FlagMCQLabel if fl, err = k.GetMCQJustification(); err == nil { k.Label = fl.Label } // Treat array flags flag.Label, flag.Separator, flag.IgnoreOrder, flag.NbLines = k.AnalyzeFlagLabel() // 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]interface{}{} for _, c := range choices { flag.Choices[c.Value] = c.Label } } else { flag.ChoicesCost = int64(float64(k.ChoicesCost) * GlobalScoreCoefficient) } } // Append to corresponding flags' map if fl.IdChoice != 0 { justifiedMCQ[fl.IdChoice] = flag } else { exercice.Flags = append(exercice.Flags, flag) } } } if mcqs, err := e.GetMCQ(); err != nil { return nil, err } else { for _, mcq := range mcqs { exercice.NbFlags += 1 if !DisplayAllFlags && t != nil && !t.CanSeeFlag(mcq) { // Dependancy missing, skip the flag for now continue } m := myTeamFlag{ Id: mcq.Id, Type: "mcq", order: mcq.Order, Label: mcq.Title, Choices: map[string]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[strconv.Itoa(e.Id)] = jc } else { m.Choices[strconv.Itoa(e.Id)] = e.Label } } else { m.Choices[strconv.Itoa(e.Id)] = e.Label } } if t == nil { h, _ := ComputeHashedFlag([]byte(soluce), false, false, nil, false) m.Soluce = hex.EncodeToString(h[:]) } if fullySolved { m.Solved = m.PSolved m.PSolved = nil } exercice.Flags = append(exercice.Flags, m) } } // Sort flags by order sort.Sort(ByOrder(exercice.Flags)) } // Hash table ordered by exercice Id ret.Exercices[fmt.Sprintf("%d", e.Id)] = exercice } } } return ret, nil } func ReadMyJSON(fd *os.File) (my MyTeam, err error) { jdec := json.NewDecoder(fd) err = jdec.Decode(&my) return }