Implement number flags
This commit is contained in:
parent
30d0afe43f
commit
c3742ade4e
6 changed files with 104 additions and 5 deletions
|
@ -399,6 +399,7 @@ func deleteExerciceHint(hint fic.EHint, _ []byte) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type uploadedFlag struct {
|
type uploadedFlag struct {
|
||||||
|
Type string
|
||||||
Label string
|
Label string
|
||||||
Placeholder string
|
Placeholder string
|
||||||
IgnoreCase bool
|
IgnoreCase bool
|
||||||
|
@ -424,7 +425,7 @@ func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error)
|
||||||
vre = uk.ValidatorRe
|
vre = uk.ValidatorRe
|
||||||
}
|
}
|
||||||
|
|
||||||
return exercice.AddRawFlagKey(uk.Label, uk.Placeholder, uk.IgnoreCase, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost)
|
return exercice.AddRawFlagKey(uk.Label, uk.Type, uk.Placeholder, uk.IgnoreCase, uk.Multiline, vre, []byte(uk.Flag), uk.ChoicesCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
func showExerciceFlag(flag fic.FlagKey, _ fic.Exercice, body []byte) (interface{}, error) {
|
func showExerciceFlag(flag fic.FlagKey, _ fic.Exercice, body []byte) (interface{}, error) {
|
||||||
|
|
|
@ -46,6 +46,9 @@ type ExerciceFlag struct {
|
||||||
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
||||||
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`
|
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`
|
||||||
NoShuffle bool
|
NoShuffle bool
|
||||||
|
NumberMin interface{} `toml:"min,omitempty"`
|
||||||
|
NumberMax interface{} `toml:"max,omitempty"`
|
||||||
|
NumberStep interface{} `toml:"step,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ).
|
// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ).
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
@ -83,6 +84,8 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
|
||||||
prep = fmt.Sprintf("`%s%s%d", separator, ignord, nbLines)
|
prep = fmt.Sprintf("`%s%s%d", separator, ignord, nbLines)
|
||||||
} else if f, ok := input.(int64); ok {
|
} else if f, ok := input.(int64); ok {
|
||||||
raw = fmt.Sprintf("%d", f)
|
raw = fmt.Sprintf("%d", f)
|
||||||
|
} else if f, ok := input.(float64); ok {
|
||||||
|
raw = strconv.FormatFloat(f, 'f', -1, 64)
|
||||||
} else if f, ok := input.(string); !ok {
|
} else if f, ok := input.(string); !ok {
|
||||||
errs = append(errs, fmt.Sprintf("has an invalid type: can only be []string or string, not %T", input))
|
errs = append(errs, fmt.Sprintf("has an invalid type: can only be []string or string, not %T", input))
|
||||||
return
|
return
|
||||||
|
@ -127,6 +130,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fl := fic.Flag(fic.FlagKey{
|
fl := fic.Flag(fic.FlagKey{
|
||||||
|
Type: flag.Type,
|
||||||
IdExercice: exercice.Id,
|
IdExercice: exercice.Id,
|
||||||
Order: int8(flagline),
|
Order: int8(flagline),
|
||||||
Label: flag.Label,
|
Label: flag.Label,
|
||||||
|
@ -192,6 +196,20 @@ type importFlag struct {
|
||||||
FlagsDeps []int64
|
FlagsDeps []int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func iface2Number(input interface{}, output *string) error {
|
||||||
|
if input != nil {
|
||||||
|
if v, ok := input.(int64); ok {
|
||||||
|
*output = fmt.Sprintf("%d", v)
|
||||||
|
} else if v, ok := input.(float64); ok {
|
||||||
|
*output = strconv.FormatFloat(v, 'f', -1, 64)
|
||||||
|
fmt.Printf("%s\n", *output)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("has an invalid type: expected int or float, got %T", input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// buildExerciceFlags read challenge.txt and extract all flags.
|
// buildExerciceFlags read challenge.txt and extract all flags.
|
||||||
func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []string) {
|
func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []string) {
|
||||||
switch strings.ToLower(flag.Type) {
|
switch strings.ToLower(flag.Type) {
|
||||||
|
@ -199,6 +217,23 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli
|
||||||
flag.Type = "key"
|
flag.Type = "key"
|
||||||
case "key":
|
case "key":
|
||||||
flag.Type = "key"
|
flag.Type = "key"
|
||||||
|
case "number":
|
||||||
|
var smin, smax, sstep string
|
||||||
|
err := iface2Number(flag.NumberMin, &smin)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: min %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iface2Number(flag.NumberMax, &smax)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: max %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = iface2Number(flag.NumberStep, &sstep)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: step %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||||
|
}
|
||||||
|
flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep)
|
||||||
case "text":
|
case "text":
|
||||||
flag.Type = "text"
|
flag.Type = "text"
|
||||||
case "vector":
|
case "vector":
|
||||||
|
@ -208,11 +243,21 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli
|
||||||
case "mcq":
|
case "mcq":
|
||||||
flag.Type = "mcq"
|
flag.Type = "mcq"
|
||||||
default:
|
default:
|
||||||
errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'text', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1))
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'number', 'text', 'mcq', 'ucq' or 'vector'.", path.Base(exercice.Path), nline+1))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag.Type == "key" || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "vector" {
|
if !strings.HasPrefix(flag.Type, "number") {
|
||||||
|
if flag.NumberMin != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: property min undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
|
||||||
|
} else if flag.NumberMax != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: property max undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
|
||||||
|
} else if flag.NumberStep != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: flag #%d: property step undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "vector" {
|
||||||
addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag")
|
addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag")
|
||||||
if len(berrs) > 0 {
|
if len(berrs) > 0 {
|
||||||
errs = append(errs, berrs...)
|
errs = append(errs, berrs...)
|
||||||
|
|
|
@ -103,7 +103,20 @@
|
||||||
{#each values as v, index}
|
{#each values as v, index}
|
||||||
<div class="input-group" class:mt-1={index != 0}>
|
<div class="input-group" class:mt-1={index != 0}>
|
||||||
{#if !flag.choices}
|
{#if !flag.choices}
|
||||||
{#if !flag.multiline}
|
{#if flag.type == 'number'}
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="form-control flag"
|
||||||
|
id="sol_{flag.type}{flag.id}_{index}"
|
||||||
|
autocomplete="off"
|
||||||
|
bind:value={values[index]}
|
||||||
|
placeholder={flag.placeholder}
|
||||||
|
title={flag.placeholder}
|
||||||
|
min={flag.min}
|
||||||
|
max={flag.max}
|
||||||
|
step={flag.step}
|
||||||
|
>
|
||||||
|
{:else if !flag.multiline}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control flag"
|
class="form-control flag"
|
||||||
|
|
|
@ -111,13 +111,14 @@ func ExecValidatorRegexp(vre string, val []byte, ignorecase bool) ([]byte, error
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database.
|
// AddRawFlagKey creates and fills a new struct FlagKey, from a non-hashed flag, and registers it into the database.
|
||||||
func (e Exercice) AddRawFlagKey(name string, placeholder string, ignorecase bool, multiline bool, validator_regexp *string, raw_value []byte, choicescost int64) (f FlagKey, err error) {
|
func (e Exercice) AddRawFlagKey(name string, t string, placeholder string, ignorecase bool, multiline bool, validator_regexp *string, raw_value []byte, choicescost int64) (f FlagKey, err error) {
|
||||||
hash, errr := ComputeHashedFlag(raw_value, ignorecase, validator_regexp)
|
hash, errr := ComputeHashedFlag(raw_value, ignorecase, validator_regexp)
|
||||||
if errr != nil {
|
if errr != nil {
|
||||||
return f, err
|
return f, err
|
||||||
}
|
}
|
||||||
|
|
||||||
f = FlagKey{
|
f = FlagKey{
|
||||||
|
Type: t,
|
||||||
Label: name,
|
Label: name,
|
||||||
Placeholder: placeholder,
|
Placeholder: placeholder,
|
||||||
IgnoreCase: ignorecase,
|
IgnoreCase: ignorecase,
|
||||||
|
|
|
@ -46,6 +46,9 @@ type myTeamFlag struct {
|
||||||
Justify bool `json:"justify,omitempty"`
|
Justify bool `json:"justify,omitempty"`
|
||||||
Choices map[string]interface{} `json:"choices,omitempty"`
|
Choices map[string]interface{} `json:"choices,omitempty"`
|
||||||
ChoicesCost int64 `json:"choices_cost,omitempty"`
|
ChoicesCost int64 `json:"choices_cost,omitempty"`
|
||||||
|
Min *float64 `json:"min,omitempty"`
|
||||||
|
Max *float64 `json:"max,omitempty"`
|
||||||
|
Step *float64 `json:"step,omitempty"`
|
||||||
}
|
}
|
||||||
type myTeamMCQJustifiedChoice struct {
|
type myTeamMCQJustifiedChoice struct {
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
|
@ -204,6 +207,39 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
||||||
Help: k.Help,
|
Help: k.Help,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(flag.Type, "number") {
|
||||||
|
fields := strings.Split(flag.Type, ",")
|
||||||
|
if len(fields) == 4 {
|
||||||
|
flag.Type = fields[0]
|
||||||
|
|
||||||
|
var tmp_min, tmp_max, tmp_step float64
|
||||||
|
|
||||||
|
if len(fields[1]) > 0 {
|
||||||
|
tmp_min, err = strconv.ParseFloat(fields[1], 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
flag.Min = &tmp_min
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fields[2]) > 0 {
|
||||||
|
tmp_max, err = strconv.ParseFloat(fields[2], 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
flag.Max = &tmp_max
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fields[3]) > 0 {
|
||||||
|
tmp_step, err = strconv.ParseFloat(fields[3], 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
flag.Step = &tmp_step
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve solved state or solution for public iface
|
// Retrieve solved state or solution for public iface
|
||||||
if t == nil {
|
if t == nil {
|
||||||
flag.IgnoreCase = k.IgnoreCase
|
flag.IgnoreCase = k.IgnoreCase
|
||||||
|
|
Reference in a new issue