Implement number flags
This commit is contained in:
parent
30d0afe43f
commit
c3742ade4e
|
@ -399,6 +399,7 @@ func deleteExerciceHint(hint fic.EHint, _ []byte) (interface{}, error) {
|
|||
}
|
||||
|
||||
type uploadedFlag struct {
|
||||
Type string
|
||||
Label string
|
||||
Placeholder string
|
||||
IgnoreCase bool
|
||||
|
@ -424,7 +425,7 @@ func createExerciceFlag(exercice fic.Exercice, body []byte) (interface{}, error)
|
|||
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) {
|
||||
|
|
|
@ -46,6 +46,9 @@ type ExerciceFlag struct {
|
|||
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
||||
NeedFlag []ExerciceDependency `toml:"need_flag,omitempty"`
|
||||
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).
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/rand"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
|
@ -83,6 +84,8 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
|
|||
prep = fmt.Sprintf("`%s%s%d", separator, ignord, nbLines)
|
||||
} else if f, ok := input.(int64); ok {
|
||||
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 {
|
||||
errs = append(errs, fmt.Sprintf("has an invalid type: can only be []string or string, not %T", input))
|
||||
return
|
||||
|
@ -127,6 +130,7 @@ func buildKeyFlag(exercice fic.Exercice, flag ExerciceFlag, flagline int, defaul
|
|||
return
|
||||
}
|
||||
fl := fic.Flag(fic.FlagKey{
|
||||
Type: flag.Type,
|
||||
IdExercice: exercice.Id,
|
||||
Order: int8(flagline),
|
||||
Label: flag.Label,
|
||||
|
@ -192,6 +196,20 @@ type importFlag struct {
|
|||
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.
|
||||
func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []string) {
|
||||
switch strings.ToLower(flag.Type) {
|
||||
|
@ -199,6 +217,23 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli
|
|||
flag.Type = "key"
|
||||
case "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":
|
||||
flag.Type = "text"
|
||||
case "vector":
|
||||
|
@ -208,11 +243,21 @@ func buildExerciceFlag(i Importer, exercice fic.Exercice, flag ExerciceFlag, nli
|
|||
case "mcq":
|
||||
flag.Type = "mcq"
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
if len(berrs) > 0 {
|
||||
errs = append(errs, berrs...)
|
||||
|
|
|
@ -103,7 +103,20 @@
|
|||
{#each values as v, index}
|
||||
<div class="input-group" class:mt-1={index != 0}>
|
||||
{#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
|
||||
type="text"
|
||||
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.
|
||||
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)
|
||||
if errr != nil {
|
||||
return f, err
|
||||
}
|
||||
|
||||
f = FlagKey{
|
||||
Type: t,
|
||||
Label: name,
|
||||
Placeholder: placeholder,
|
||||
IgnoreCase: ignorecase,
|
||||
|
|
|
@ -46,6 +46,9 @@ type myTeamFlag struct {
|
|||
Justify bool `json:"justify,omitempty"`
|
||||
Choices map[string]interface{} `json:"choices,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 {
|
||||
Label string `json:"label"`
|
||||
|
@ -204,6 +207,39 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
|||
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
|
||||
if t == nil {
|
||||
flag.IgnoreCase = k.IgnoreCase
|
||||
|
|
Loading…
Reference in New Issue
Block a user