2018-01-10 02:45:56 +00:00
|
|
|
package sync
|
|
|
|
|
|
|
|
import (
|
2019-07-05 20:28:56 +00:00
|
|
|
"fmt"
|
2018-01-10 02:45:56 +00:00
|
|
|
"path"
|
2024-01-13 15:40:25 +00:00
|
|
|
"strconv"
|
2018-01-10 02:45:56 +00:00
|
|
|
|
|
|
|
"github.com/BurntSushi/toml"
|
2023-11-22 11:16:53 +00:00
|
|
|
"go.uber.org/multierr"
|
|
|
|
|
2019-07-05 20:28:56 +00:00
|
|
|
"srs.epita.fr/fic-server/libfic"
|
2018-01-10 02:45:56 +00:00
|
|
|
)
|
|
|
|
|
2018-03-09 18:07:08 +00:00
|
|
|
// ExerciceHintParams holds EHint definition infomation.
|
2018-01-10 03:42:50 +00:00
|
|
|
type ExerciceHintParams struct {
|
|
|
|
Filename string
|
2018-05-12 00:01:49 +00:00
|
|
|
Content string
|
2021-11-14 15:15:19 +00:00
|
|
|
Cost *int64
|
2018-01-10 03:42:50 +00:00
|
|
|
Title string
|
2019-10-26 09:33:30 +00:00
|
|
|
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`
|
2018-01-10 03:42:50 +00:00
|
|
|
}
|
|
|
|
|
2018-05-12 00:01:49 +00:00
|
|
|
// ExerciceDependency holds dependency definitions information.
|
|
|
|
type ExerciceDependency struct {
|
|
|
|
Id int64
|
|
|
|
Theme string `toml:",omitempty"`
|
|
|
|
}
|
|
|
|
|
2018-09-07 18:53:08 +00:00
|
|
|
// ExerciceUnlockFile holds parameters related to a locked file.
|
|
|
|
type ExerciceUnlockFile struct {
|
|
|
|
Filename string `toml:",omitempty"`
|
|
|
|
}
|
|
|
|
|
2022-10-31 17:52:29 +00:00
|
|
|
// ExerciceFile defines attributes on files.
|
|
|
|
type ExerciceFile struct {
|
2022-11-23 15:55:49 +00:00
|
|
|
Filename string `toml:",omitempty"`
|
2023-10-13 14:05:15 +00:00
|
|
|
URL string `toml:",omitempty"`
|
2022-11-23 15:55:49 +00:00
|
|
|
Hidden bool `toml:",omitempty"`
|
|
|
|
Disclaimer string `toml:",omitempty"`
|
2022-10-31 17:52:29 +00:00
|
|
|
}
|
|
|
|
|
2019-01-16 04:25:20 +00:00
|
|
|
// ExerciceFlag holds informations about one flag.
|
2018-05-12 00:01:49 +00:00
|
|
|
type ExerciceFlag struct {
|
2019-09-06 21:03:08 +00:00
|
|
|
Id int64
|
|
|
|
Label string `toml:",omitempty"`
|
|
|
|
Type string `toml:",omitempty"`
|
|
|
|
Raw interface{}
|
|
|
|
Separator string `toml:",omitempty"`
|
2019-11-25 16:45:41 +00:00
|
|
|
ShowLines bool `toml:",omitempty"`
|
2019-09-06 21:03:08 +00:00
|
|
|
Ordered bool `toml:",omitempty"`
|
|
|
|
CaseSensitive bool `toml:",omitempty"`
|
2021-12-07 15:33:30 +00:00
|
|
|
NoTrim bool `toml:",omitempty"`
|
2023-05-16 07:59:59 +00:00
|
|
|
CaptureRe string `toml:"capture_regexp,omitempty"`
|
|
|
|
SortReGroups bool `toml:"sort_capture_regexp_groups,omitempty"`
|
2020-09-07 17:33:09 +00:00
|
|
|
Placeholder string `toml:",omitempty"`
|
2021-08-30 16:33:14 +00:00
|
|
|
Help string `toml:",omitempty"`
|
2022-05-31 20:03:51 +00:00
|
|
|
BonusGain int32 `toml:"bonus_gain,omitempty"`
|
|
|
|
ChoicesCost int32 `toml:"choices_cost,omitempty"`
|
2019-09-06 21:03:08 +00:00
|
|
|
Choice []ExerciceFlagChoice
|
|
|
|
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
|
|
|
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`
|
2022-11-02 21:24:15 +00:00
|
|
|
NeedFlags []int64 `toml:"need_flags,omitempty"`
|
2019-09-06 21:03:08 +00:00
|
|
|
NoShuffle bool
|
2021-11-12 22:52:22 +00:00
|
|
|
Unit string `toml:"unit,omitempty"`
|
2022-01-21 12:06:37 +00:00
|
|
|
Variant string `toml:"variant,omitempty"`
|
2021-11-12 20:36:27 +00:00
|
|
|
NumberMin interface{} `toml:"min,omitempty"`
|
|
|
|
NumberMax interface{} `toml:"max,omitempty"`
|
|
|
|
NumberStep interface{} `toml:"step,omitempty"`
|
2018-05-12 00:01:49 +00:00
|
|
|
}
|
|
|
|
|
2022-11-24 12:11:21 +00:00
|
|
|
func (f ExerciceFlag) RawString() []string {
|
|
|
|
switch f.Raw.(type) {
|
|
|
|
case string:
|
|
|
|
return []string{f.Raw.(string)}
|
|
|
|
case []string:
|
|
|
|
return f.Raw.([]string)
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-13 15:40:25 +00:00
|
|
|
func (f ExerciceFlag) RawNumber() ([]float64, error) {
|
|
|
|
switch f.Raw.(type) {
|
|
|
|
case float64:
|
|
|
|
return []float64{f.Raw.(float64)}, nil
|
|
|
|
case []float64:
|
|
|
|
return f.Raw.([]float64), nil
|
|
|
|
case int64:
|
|
|
|
return []float64{float64(f.Raw.(int64))}, nil
|
|
|
|
case []int64:
|
|
|
|
var res []float64
|
|
|
|
for _, raw := range f.Raw.([]int64) {
|
|
|
|
res = append(res, float64(raw))
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
case string:
|
|
|
|
if v, err := strconv.ParseFloat(f.Raw.(string), 64); err == nil {
|
|
|
|
return []float64{v}, nil
|
|
|
|
} else {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case []string:
|
|
|
|
var res []float64
|
|
|
|
for _, raw := range f.Raw.([]string) {
|
|
|
|
if v, err := strconv.ParseFloat(raw, 64); err == nil {
|
|
|
|
res = append(res, v)
|
|
|
|
} else {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("invalid raw type: %T", f.Raw)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-06 20:58:30 +00:00
|
|
|
// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ).
|
|
|
|
type ExerciceFlagChoice struct {
|
|
|
|
ExerciceFlag
|
|
|
|
Value interface{} `toml:",omitempty"`
|
|
|
|
}
|
|
|
|
|
2018-03-09 18:07:08 +00:00
|
|
|
// ExerciceParams contains values parsed from defines.txt.
|
2018-01-10 02:45:56 +00:00
|
|
|
type ExerciceParams struct {
|
2022-11-05 14:26:57 +00:00
|
|
|
WIP bool `toml:"wip"`
|
2018-05-12 00:01:49 +00:00
|
|
|
Gain int64
|
2018-11-18 21:44:23 +00:00
|
|
|
Tags []string
|
2022-10-31 17:52:29 +00:00
|
|
|
Files []ExerciceFile `toml:"file"`
|
2018-05-12 00:01:49 +00:00
|
|
|
Hints []ExerciceHintParams `toml:"hint"`
|
|
|
|
Dependencies []ExerciceDependency `toml:"depend"`
|
|
|
|
Flags []ExerciceFlag `toml:"flag"`
|
2019-01-16 04:25:20 +00:00
|
|
|
FlagsMCQ []ExerciceFlag `toml:"flag_mcq"`
|
|
|
|
FlagsUCQ []ExerciceFlag `toml:"flag_ucq"`
|
2018-01-10 02:45:56 +00:00
|
|
|
}
|
|
|
|
|
2022-11-24 12:11:21 +00:00
|
|
|
func (p ExerciceParams) GetRawFlags() (ret []string) {
|
|
|
|
for _, f := range append(p.Flags, p.FlagsUCQ...) {
|
|
|
|
raw := f.RawString()
|
|
|
|
if len(raw) > 0 {
|
|
|
|
ret = append(ret, raw...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, f := range p.FlagsMCQ {
|
|
|
|
for _, c := range f.Choice {
|
|
|
|
ret = append(ret, c.Label)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-03-09 18:07:08 +00:00
|
|
|
// parseExerciceParams reads challenge definitions from defines.txt and extract usefull data to set up the challenge.
|
2019-07-21 20:31:43 +00:00
|
|
|
func parseExerciceParams(i Importer, exPath string) (p ExerciceParams, md toml.MetaData, err error) {
|
2018-01-10 02:45:56 +00:00
|
|
|
var defs string
|
2023-05-05 13:25:20 +00:00
|
|
|
|
|
|
|
if i.Exists(path.Join(exPath, "challenge.toml")) {
|
|
|
|
defs, err = GetFileContent(i, path.Join(exPath, "challenge.toml"))
|
|
|
|
} else {
|
|
|
|
defs, err = GetFileContent(i, path.Join(exPath, "challenge.txt"))
|
|
|
|
}
|
|
|
|
|
2019-07-21 20:31:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2018-01-10 02:45:56 +00:00
|
|
|
|
2019-07-21 20:31:43 +00:00
|
|
|
md, err = toml.Decode(defs, &p)
|
2018-01-10 02:45:56 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2019-07-05 20:28:56 +00:00
|
|
|
|
|
|
|
// getExerciceParams returns normalized
|
2023-11-22 11:16:53 +00:00
|
|
|
func getExerciceParams(i Importer, exercice *fic.Exercice) (params ExerciceParams, errs error) {
|
2019-07-05 20:28:56 +00:00
|
|
|
var err error
|
2019-07-21 20:31:43 +00:00
|
|
|
if params, _, err = parseExerciceParams(i, exercice.Path); err != nil {
|
2023-11-22 11:16:53 +00:00
|
|
|
errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, err))
|
2019-07-05 20:28:56 +00:00
|
|
|
} else if len(params.Flags) == 0 && len(params.FlagsUCQ) == 0 && len(params.FlagsMCQ) == 0 {
|
2023-11-22 11:16:53 +00:00
|
|
|
errs = multierr.Append(errs, NewChallengeTxtError(exercice, 0, fmt.Errorf("has no flag")))
|
2019-07-05 20:28:56 +00:00
|
|
|
} else {
|
|
|
|
// Treat legacy UCQ flags as ExerciceFlag
|
|
|
|
for _, flag := range params.FlagsUCQ {
|
|
|
|
params.Flags = append(params.Flags, ExerciceFlag{
|
|
|
|
Id: flag.Id,
|
|
|
|
Label: flag.Label,
|
|
|
|
Type: "ucq",
|
|
|
|
Raw: flag.Raw,
|
2023-05-16 07:59:59 +00:00
|
|
|
CaptureRe: flag.CaptureRe,
|
2020-09-07 17:33:09 +00:00
|
|
|
Placeholder: flag.Placeholder,
|
2019-07-05 20:28:56 +00:00
|
|
|
ChoicesCost: flag.ChoicesCost,
|
|
|
|
Choice: flag.Choice,
|
|
|
|
LockedFile: flag.LockedFile,
|
|
|
|
NeedFlag: flag.NeedFlag,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
params.FlagsUCQ = []ExerciceFlag{}
|
|
|
|
|
|
|
|
// Treat legacy MCQ flags as ExerciceFlag
|
|
|
|
for _, flag := range params.FlagsMCQ {
|
|
|
|
params.Flags = append(params.Flags, ExerciceFlag{
|
|
|
|
Id: flag.Id,
|
|
|
|
Label: flag.Label,
|
|
|
|
Type: "mcq",
|
|
|
|
ChoicesCost: flag.ChoicesCost,
|
|
|
|
Choice: flag.Choice,
|
|
|
|
LockedFile: flag.LockedFile,
|
|
|
|
NeedFlag: flag.NeedFlag,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
params.FlagsMCQ = []ExerciceFlag{}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2022-10-31 17:52:29 +00:00
|
|
|
|
|
|
|
func GetExerciceFilesParams(i Importer, exercice *fic.Exercice) (map[string]ExerciceFile, error) {
|
|
|
|
params, _, err := parseExerciceParams(i, exercice.Path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
paramsFiles := map[string]ExerciceFile{}
|
|
|
|
for _, f := range params.Files {
|
|
|
|
paramsFiles[f.Filename] = f
|
|
|
|
}
|
|
|
|
|
|
|
|
return paramsFiles, nil
|
|
|
|
}
|