sync: Introduce repochecker-ack.txt to support check exceptions
This commit is contained in:
parent
edde9f885d
commit
fb368d79d1
|
@ -110,6 +110,14 @@ func ExerciceHandler(c *gin.Context) {
|
|||
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Exercice not found"})
|
||||
return
|
||||
}
|
||||
|
||||
theme, err = exercice.GetTheme()
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Unable to find the attached theme."})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("theme", theme)
|
||||
}
|
||||
|
||||
c.Set("exercice", exercice)
|
||||
|
|
|
@ -52,8 +52,10 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
|||
apiSyncDeepRoutes.POST("", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
var st []string
|
||||
for _, se := range sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) {
|
||||
for _, se := range sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250, exceptions) {
|
||||
st = append(st, se.Error())
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theme.Name: st}}, false)
|
||||
|
@ -68,7 +70,8 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
|||
apiSyncRoutes.POST("/fixurlids", fixAllURLIds)
|
||||
|
||||
apiSyncRoutes.POST("/themes", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, sync.SyncThemes(sync.GlobalImporter))
|
||||
_, errs := sync.SyncThemes(sync.GlobalImporter)
|
||||
c.JSON(http.StatusOK, errs)
|
||||
})
|
||||
|
||||
apiSyncThemesRoutes := apiSyncRoutes.Group("/themes/:thid")
|
||||
|
@ -130,25 +133,29 @@ func declareSyncRoutes(router *gin.RouterGroup) {
|
|||
func declareSyncExercicesRoutes(router *gin.RouterGroup) {
|
||||
router.POST("/exercices", func(c *gin.Context) {
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
c.JSON(http.StatusOK, sync.SyncExercices(sync.GlobalImporter, theme))
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
c.JSON(http.StatusOK, sync.SyncExercices(sync.GlobalImporter, theme, exceptions))
|
||||
})
|
||||
apiSyncExercicesRoutes := router.Group("/exercices/:eid")
|
||||
apiSyncExercicesRoutes.Use(ExerciceHandler)
|
||||
apiSyncExercicesRoutes.POST("", func(c *gin.Context) {
|
||||
theme, exists := c.Get("theme")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusNotImplemented, gin.H{"errmsg": "You should sync exercice only through a theme."})
|
||||
return
|
||||
}
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
|
||||
_, _, errs := sync.SyncExercice(sync.GlobalImporter, theme.(*fic.Theme), exercice.Path, nil)
|
||||
_, _, errs := sync.SyncExercice(sync.GlobalImporter, theme, exercice.Path, nil, exceptions)
|
||||
c.JSON(http.StatusOK, errs)
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/files", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
c.JSON(http.StatusOK, sync.SyncExerciceFiles(sync.GlobalImporter, exercice))
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
c.JSON(http.StatusOK, sync.SyncExerciceFiles(sync.GlobalImporter, exercice, exceptions))
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/fixurlid", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
|
@ -167,13 +174,20 @@ func declareSyncExercicesRoutes(router *gin.RouterGroup) {
|
|||
})
|
||||
apiSyncExercicesRoutes.POST("/hints", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
_, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice))
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
_, errs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions)
|
||||
c.JSON(http.StatusOK, errs)
|
||||
})
|
||||
apiSyncExercicesRoutes.POST("/flags", func(c *gin.Context) {
|
||||
exercice := c.MustGet("exercice").(*fic.Exercice)
|
||||
_, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice)
|
||||
_, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice))
|
||||
theme := c.MustGet("theme").(*fic.Theme)
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
_, errs := sync.SyncExerciceFlags(sync.GlobalImporter, exercice, exceptions)
|
||||
_, herrs := sync.SyncExerciceHints(sync.GlobalImporter, exercice, sync.ExerciceFlagsMap(sync.GlobalImporter, exercice), exceptions)
|
||||
c.JSON(http.StatusOK, append(errs, herrs...))
|
||||
})
|
||||
}
|
||||
|
@ -214,8 +228,10 @@ func autoSync(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
exceptions := sync.LoadException(sync.GlobalImporter, theme)
|
||||
|
||||
var st []string
|
||||
for _, se := range sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250) {
|
||||
for _, se := range sync.SyncThemeDeep(sync.GlobalImporter, theme, 0, 250, exceptions) {
|
||||
st = append(st, se.Error())
|
||||
}
|
||||
sync.EditDeepReport(&sync.SyncReport{Themes: map[string][]string{theme.Name: st}}, false)
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
type CheckExceptions map[string]string
|
||||
|
||||
func (c *CheckExceptions) GetExerciceExceptions(e *fic.Exercice) *CheckExceptions {
|
||||
return c.GetFileExceptions(filepath.Base(e.Path))
|
||||
}
|
||||
|
||||
func (c *CheckExceptions) GetFileExceptions(path string) *CheckExceptions {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := CheckExceptions{}
|
||||
|
||||
for k, v := range *c {
|
||||
if strings.HasPrefix(k, "*:") || strings.HasPrefix(k, path) {
|
||||
k = strings.TrimPrefix(k, path)
|
||||
|
||||
if strings.HasPrefix(k, "/") {
|
||||
k = strings.TrimPrefix(k, "/")
|
||||
}
|
||||
|
||||
ret[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (c *CheckExceptions) HasException(ref string) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for k, _ := range *c {
|
||||
if strings.HasSuffix(k, ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func LoadException(i Importer, th *fic.Theme) (exceptions *CheckExceptions) {
|
||||
if fexcept, err := GetFileContent(i, filepath.Join(th.Path, "repochecker-ack.txt")); err == nil {
|
||||
exceptions = &CheckExceptions{}
|
||||
for n, line := range strings.Split(fexcept, "\n") {
|
||||
(*exceptions)[line] = fmt.Sprintf("repochecker-ack.txt:%d", n+1)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -97,7 +97,7 @@ func CheckExerciceFilesPresence(i Importer, exercice *fic.Exercice) (files []str
|
|||
}
|
||||
|
||||
// CheckExerciceFiles checks that remote files have the right digest.
|
||||
func CheckExerciceFiles(i Importer, exercice *fic.Exercice) (files []string, errs []error) {
|
||||
func CheckExerciceFiles(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (files []string, errs []error) {
|
||||
flist, digests, berrs := BuildFilesListInto(i, exercice, "files")
|
||||
errs = append(errs, berrs...)
|
||||
|
||||
|
@ -116,7 +116,7 @@ func CheckExerciceFiles(i Importer, exercice *fic.Exercice) (files []string, err
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.fileHooks {
|
||||
for _, e := range h(file) {
|
||||
for _, e := range h(file, exceptions) {
|
||||
errs = append(errs, NewFileError(exercice, fname, e))
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ func CheckExerciceFiles(i Importer, exercice *fic.Exercice) (files []string, err
|
|||
|
||||
// SyncExerciceFiles reads the content of files/ directory and import it as EFile for the given challenge.
|
||||
// It takes care of DIGESTS.txt and ensure imported files match.
|
||||
func SyncExerciceFiles(i Importer, exercice *fic.Exercice) (errs []error) {
|
||||
func SyncExerciceFiles(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (errs []error) {
|
||||
if _, err := exercice.WipeFiles(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ func SyncExerciceFiles(i Importer, exercice *fic.Exercice) (errs []error) {
|
|||
} else {
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.fileHooks {
|
||||
for _, e := range h(f.(*fic.EFile)) {
|
||||
for _, e := range h(f.(*fic.EFile), exceptions) {
|
||||
errs = append(errs, NewFileError(exercice, fname, e))
|
||||
}
|
||||
}
|
||||
|
@ -161,9 +161,9 @@ func SyncExerciceFiles(i Importer, exercice *fic.Exercice) (errs []error) {
|
|||
|
||||
// ApiGetRemoteExerciceFiles is an accessor to remote exercice files list.
|
||||
func ApiGetRemoteExerciceFiles(c *gin.Context) {
|
||||
theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
theme, exceptions, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if theme != nil {
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil)
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil, exceptions)
|
||||
if exercice != nil {
|
||||
files, digests, errs := BuildFilesListInto(GlobalImporter, exercice, "files")
|
||||
if files != nil {
|
||||
|
|
|
@ -23,7 +23,7 @@ type importHint struct {
|
|||
FlagsDeps []int64
|
||||
}
|
||||
|
||||
func buildExerciceHints(i Importer, exercice *fic.Exercice) (hints []importHint, errs []error) {
|
||||
func buildExerciceHints(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (hints []importHint, errs []error) {
|
||||
params, _, err := parseExerciceParams(i, exercice.Path)
|
||||
if err != nil {
|
||||
errs = append(errs, NewChallengeTxtError(exercice, 0, err))
|
||||
|
@ -82,13 +82,22 @@ func buildExerciceHints(i Importer, exercice *fic.Exercice) (hints []importHint,
|
|||
} else if hint.Content == "" {
|
||||
errs = append(errs, NewHintError(exercice, h, n, fmt.Errorf("content and filename can't be empty at the same time")))
|
||||
continue
|
||||
} else if h.Content, err = ProcessMarkdown(i, fixnbsp(hint.Content), exercice.Path); err != nil {
|
||||
errs = append(errs, NewHintError(exercice, h, n, fmt.Errorf("error during markdown formating: %w", err)))
|
||||
} else {
|
||||
// Call checks hooks
|
||||
for _, hk := range hooks.mdTextHooks {
|
||||
for _, err := range hk(h.Content, exceptions.GetFileExceptions("challenge.txt")) {
|
||||
errs = append(errs, NewHintError(exercice, h, n, err))
|
||||
}
|
||||
}
|
||||
|
||||
if h.Content, err = ProcessMarkdown(i, fixnbsp(hint.Content), exercice.Path); err != nil {
|
||||
errs = append(errs, NewHintError(exercice, h, n, fmt.Errorf("error during markdown formating: %w", err)))
|
||||
}
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, hook := range hooks.hintHooks {
|
||||
for _, e := range hook(h) {
|
||||
for _, e := range hook(h, exceptions.GetFileExceptions("challenge.txt")) {
|
||||
errs = append(errs, NewHintError(exercice, h, n, e))
|
||||
}
|
||||
}
|
||||
|
@ -110,16 +119,16 @@ func buildExerciceHints(i Importer, exercice *fic.Exercice) (hints []importHint,
|
|||
}
|
||||
|
||||
// CheckExerciceHints checks if all hints are corrects..
|
||||
func CheckExerciceHints(i Importer, exercice *fic.Exercice) ([]importHint, []error) {
|
||||
return buildExerciceHints(i, exercice)
|
||||
func CheckExerciceHints(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) ([]importHint, []error) {
|
||||
return buildExerciceHints(i, exercice, exceptions)
|
||||
}
|
||||
|
||||
// SyncExerciceHints reads the content of hints/ directories and import it as EHint for the given challenge.
|
||||
func SyncExerciceHints(i Importer, exercice *fic.Exercice, flagsBindings map[int64]fic.Flag) (hintsBindings map[int]*fic.EHint, errs []error) {
|
||||
func SyncExerciceHints(i Importer, exercice *fic.Exercice, flagsBindings map[int64]fic.Flag, exceptions *CheckExceptions) (hintsBindings map[int]*fic.EHint, errs []error) {
|
||||
if _, err := exercice.WipeHints(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
hints, berrs := buildExerciceHints(i, exercice)
|
||||
hints, berrs := buildExerciceHints(i, exercice, exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
|
||||
hintsBindings = map[int]*fic.EHint{}
|
||||
|
@ -149,11 +158,11 @@ func SyncExerciceHints(i Importer, exercice *fic.Exercice, flagsBindings map[int
|
|||
|
||||
// ApiListRemoteExerciceHints is an accessor letting foreign packages to access remote exercice hints.
|
||||
func ApiGetRemoteExerciceHints(c *gin.Context) {
|
||||
theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
theme, exceptions, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if theme != nil {
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil)
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil, exceptions)
|
||||
if exercice != nil {
|
||||
hints, errs := CheckExerciceHints(GlobalImporter, exercice)
|
||||
hints, errs := CheckExerciceHints(GlobalImporter, exercice, exceptions)
|
||||
if hints != nil {
|
||||
c.JSON(http.StatusOK, hints)
|
||||
return
|
||||
|
|
|
@ -96,7 +96,7 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
|
|||
return
|
||||
}
|
||||
|
||||
func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []error) {
|
||||
func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, exceptions *CheckExceptions) (f *fic.FlagLabel, errs []error) {
|
||||
if len(flag.Label) == 0 {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("Label cannot be empty.")))
|
||||
return
|
||||
|
@ -118,14 +118,14 @@ func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagLabelHooks {
|
||||
for _, e := range h(f) {
|
||||
for _, e := range h(f, exceptions) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string) (f *fic.Flag, choices []*fic.FlagChoice, errs []error) {
|
||||
func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string, exceptions *CheckExceptions) (f *fic.Flag, choices []*fic.FlagChoice, errs []error) {
|
||||
if len(flag.Label) == 0 {
|
||||
flag.Label = defaultLabel
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagKeyHooks {
|
||||
for _, e := range h(fk, raw) {
|
||||
for _, e := range h(fk, raw, exceptions) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +221,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagChoiceHooks {
|
||||
for _, e := range h(fc) {
|
||||
for _, e := range h(fc, exceptions) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagKeyWithChoicesHooks {
|
||||
for _, e := range h(fk, raw, choices) {
|
||||
for _, e := range h(fk, raw, choices, exceptions) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ func iface2Number(input interface{}, output *string) error {
|
|||
}
|
||||
|
||||
// buildExerciceFlags read challenge.txt and extract all flags.
|
||||
func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []error) {
|
||||
func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nline int, exceptions *CheckExceptions) (ret []importFlag, errs []error) {
|
||||
switch strings.ToLower(flag.Type) {
|
||||
case "":
|
||||
flag.Type = "key"
|
||||
|
@ -332,7 +332,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
}
|
||||
|
||||
if flag.Type == "label" {
|
||||
addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1)
|
||||
addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1, exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
|
@ -341,7 +341,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
})
|
||||
}
|
||||
} else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" {
|
||||
addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag")
|
||||
addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag", exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
|
@ -401,7 +401,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
})
|
||||
|
||||
if isJustified && choice.Raw != nil {
|
||||
addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant")
|
||||
addedFlag, choices, berrs := buildKeyFlag(exercice, choice.ExerciceFlag, nline+1, "Flag correspondant", exceptions)
|
||||
if len(berrs) > 0 {
|
||||
errs = append(errs, berrs...)
|
||||
}
|
||||
|
@ -417,7 +417,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagMCQHooks {
|
||||
for _, e := range h(&addedFlag, addedFlag.Entries) {
|
||||
for _, e := range h(&addedFlag, addedFlag.Entries, exceptions) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, e))
|
||||
}
|
||||
}
|
||||
|
@ -431,7 +431,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
}
|
||||
|
||||
// buildExerciceFlags read challenge.txt and extract all flags.
|
||||
func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]importFlag, flagids []int64, errs []error) {
|
||||
func buildExerciceFlags(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (flags map[int64]importFlag, flagids []int64, errs []error) {
|
||||
params, gerrs := getExerciceParams(i, exercice)
|
||||
if len(gerrs) > 0 {
|
||||
return flags, flagids, gerrs
|
||||
|
@ -451,7 +451,7 @@ func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]imp
|
|||
flag.Id = rand.Int63()
|
||||
}
|
||||
|
||||
newFlags, ferrs := buildExerciceFlag(i, exercice, flag, nline)
|
||||
newFlags, ferrs := buildExerciceFlag(i, exercice, flag, nline, exceptions)
|
||||
if len(ferrs) > 0 {
|
||||
errs = append(errs, ferrs...)
|
||||
}
|
||||
|
@ -482,8 +482,8 @@ func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]imp
|
|||
}
|
||||
|
||||
// CheckExerciceFlags checks if all flags for the given challenge are correct.
|
||||
func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf []fic.Flag, errs []error) {
|
||||
flags, flagsids, berrs := buildExerciceFlags(i, exercice)
|
||||
func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string, exceptions *CheckExceptions) (rf []fic.Flag, errs []error) {
|
||||
flags, flagsids, berrs := buildExerciceFlags(i, exercice, exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
|
||||
for _, flagid := range flagsids {
|
||||
|
@ -518,7 +518,7 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf
|
|||
|
||||
// ExerciceFlagsMap builds the flags bindings between challenge.txt and DB.
|
||||
func ExerciceFlagsMap(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Flag) {
|
||||
flags, flagids, _ := buildExerciceFlags(i, exercice)
|
||||
flags, flagids, _ := buildExerciceFlags(i, exercice, nil)
|
||||
|
||||
kmap = map[int64]fic.Flag{}
|
||||
|
||||
|
@ -534,13 +534,13 @@ func ExerciceFlagsMap(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Fl
|
|||
}
|
||||
|
||||
// SyncExerciceFlags imports all kind of flags for the given challenge.
|
||||
func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Flag, errs []error) {
|
||||
func SyncExerciceFlags(i Importer, exercice *fic.Exercice, exceptions *CheckExceptions) (kmap map[int64]fic.Flag, errs []error) {
|
||||
if _, err := exercice.WipeFlags(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else if _, err := exercice.WipeMCQs(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
flags, flagids, berrs := buildExerciceFlags(i, exercice)
|
||||
flags, flagids, berrs := buildExerciceFlags(i, exercice, exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
|
||||
kmap = map[int64]fic.Flag{}
|
||||
|
@ -588,11 +588,11 @@ func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.F
|
|||
|
||||
// ApiListRemoteExerciceFlags is an accessor letting foreign packages to access remote exercice flags.
|
||||
func ApiGetRemoteExerciceFlags(c *gin.Context) {
|
||||
theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
theme, exceptions, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if theme != nil {
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil)
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil, exceptions)
|
||||
if exercice != nil {
|
||||
flags, errs := CheckExerciceFlags(GlobalImporter, exercice, []string{})
|
||||
flags, errs := CheckExerciceFlags(GlobalImporter, exercice, []string{}, exceptions)
|
||||
if flags != nil {
|
||||
c.JSON(http.StatusOK, flags)
|
||||
return
|
||||
|
|
|
@ -89,7 +89,7 @@ func parseExerciceDirname(edir string) (eid int, ename string, err error) {
|
|||
}
|
||||
|
||||
// BuildExercice creates an Exercice from a given importer.
|
||||
func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice) (e *fic.Exercice, p ExerciceParams, eid int, edir string, errs []error) {
|
||||
func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice, exceptions *CheckExceptions) (e *fic.Exercice, p ExerciceParams, eid int, edir string, errs []error) {
|
||||
e = &fic.Exercice{}
|
||||
|
||||
e.Path = epath
|
||||
|
@ -102,6 +102,10 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
|
|||
return nil, p, eid, edir, errs
|
||||
}
|
||||
|
||||
// Limit exceptions to this exercice
|
||||
exceptions = exceptions.GetExerciceExceptions(e)
|
||||
//log.Printf("Kept repochecker exceptions for this exercice: %v", exceptions)
|
||||
|
||||
// Overwrite title if title.txt exists
|
||||
if myTitle, err := GetFileContent(i, path.Join(epath, "title.txt")); err == nil {
|
||||
myTitle = strings.TrimSpace(myTitle)
|
||||
|
@ -128,6 +132,13 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
|
|||
} else {
|
||||
e.Overview = fixnbsp(e.Overview)
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.mdTextHooks {
|
||||
for _, err := range h(e.Overview, exceptions.GetFileExceptions("overview.md")) {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("overview.md: %w", err)))
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := goldmark.Convert([]byte(strings.Split(e.Overview, "\n")[0]), &buf)
|
||||
if err != nil {
|
||||
|
@ -151,6 +162,13 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
|
|||
if err != nil {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("statement.md: %w", err), theme))
|
||||
} else {
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.mdTextHooks {
|
||||
for _, err := range h(e.Statement, exceptions.GetFileExceptions("statement.md")) {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("statement.md: %w", err)))
|
||||
}
|
||||
}
|
||||
|
||||
if e.Statement, err = ProcessMarkdown(i, fixnbsp(e.Statement), epath); err != nil {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("statement.md: an error occurs during markdown formating: %w", err), theme))
|
||||
}
|
||||
|
@ -161,6 +179,13 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
|
|||
if err != nil {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("finished.txt: %w", err), theme))
|
||||
} else {
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.mdTextHooks {
|
||||
for _, err := range h(e.Finished, exceptions.GetFileExceptions("finished.txt")) {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("finished.md: %w", err)))
|
||||
}
|
||||
}
|
||||
|
||||
if e.Finished, err = ProcessMarkdown(i, e.Finished, epath); err != nil {
|
||||
errs = append(errs, NewExerciceError(e, fmt.Errorf("finished.txt: an error occurs during markdown formating: %w", err), theme))
|
||||
}
|
||||
|
@ -259,16 +284,22 @@ func BuildExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*
|
|||
errs = append(errs, NewExerciceError(e, ErrResolutionNotFound, theme))
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.exerciceHooks {
|
||||
for _, err := range h(e, exceptions.GetFileExceptions("challenge.txt")) {
|
||||
errs = append(errs, NewExerciceError(e, err))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SyncExercice imports new or updates existing given exercice.
|
||||
func SyncExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice) (e *fic.Exercice, eid int, errs []error) {
|
||||
func SyncExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*fic.Exercice, exceptions *CheckExceptions) (e *fic.Exercice, eid int, errs []error) {
|
||||
var err error
|
||||
var p ExerciceParams
|
||||
var berrors []error
|
||||
|
||||
e, p, eid, _, berrors = BuildExercice(i, theme, epath, dmap)
|
||||
e, p, eid, _, berrors = BuildExercice(i, theme, epath, dmap, exceptions)
|
||||
for _, e := range berrors {
|
||||
errs = append(errs, e)
|
||||
}
|
||||
|
@ -297,7 +328,7 @@ func SyncExercice(i Importer, theme *fic.Theme, epath string, dmap *map[int64]*f
|
|||
}
|
||||
|
||||
// SyncExercices imports new or updates existing exercices, in a given theme.
|
||||
func SyncExercices(i Importer, theme *fic.Theme) (errs []error) {
|
||||
func SyncExercices(i Importer, theme *fic.Theme, exceptions *CheckExceptions) (errs []error) {
|
||||
if exercices, err := GetExercices(i, theme); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
|
@ -306,7 +337,7 @@ func SyncExercices(i Importer, theme *fic.Theme) (errs []error) {
|
|||
dmap, _ := buildDependancyMap(i, theme)
|
||||
|
||||
for _, edir := range exercices {
|
||||
e, eid, cur_errs := SyncExercice(i, theme, path.Join(theme.Path, edir), &dmap)
|
||||
e, eid, cur_errs := SyncExercice(i, theme, path.Join(theme.Path, edir), &dmap, exceptions)
|
||||
if e != nil {
|
||||
emap[e.Title] = eid
|
||||
dmap[int64(eid)] = e
|
||||
|
@ -328,7 +359,7 @@ func SyncExercices(i Importer, theme *fic.Theme) (errs []error) {
|
|||
|
||||
// ApiListRemoteExercices is an accessor letting foreign packages to access remote exercices list.
|
||||
func ApiListRemoteExercices(c *gin.Context) {
|
||||
theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
theme, _, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if theme != nil {
|
||||
exercices, err := GetExercices(GlobalImporter, theme)
|
||||
if err != nil {
|
||||
|
@ -345,9 +376,9 @@ func ApiListRemoteExercices(c *gin.Context) {
|
|||
|
||||
// ApiListRemoteExercice is an accessor letting foreign packages to access remote exercice attributes.
|
||||
func ApiGetRemoteExercice(c *gin.Context) {
|
||||
theme, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
theme, exceptions, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if theme != nil {
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil)
|
||||
exercice, _, _, _, errs := BuildExercice(GlobalImporter, theme, path.Join(theme.Path, c.Params.ByName("exid")), nil, exceptions)
|
||||
if exercice != nil {
|
||||
c.JSON(http.StatusOK, exercice)
|
||||
return
|
||||
|
|
|
@ -60,7 +60,7 @@ func SpeedySyncDeep(i Importer) (errs SyncReport) {
|
|||
}
|
||||
|
||||
errs.DateStart = startTime
|
||||
sterrs := SyncThemes(i)
|
||||
exceptions, sterrs := SyncThemes(i)
|
||||
for _, sterr := range sterrs {
|
||||
errs.ThemesSync = append(errs.ThemesSync, sterr.Error())
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ func SpeedySyncDeep(i Importer) (errs SyncReport) {
|
|||
|
||||
for tid, theme := range themes {
|
||||
DeepSyncProgress = 3 + uint8(tid)*themeStep
|
||||
seerrs := SyncExercices(i, theme)
|
||||
seerrs := SyncExercices(i, theme, exceptions[theme.Path])
|
||||
for _, seerr := range seerrs {
|
||||
errs.Themes[theme.Name] = append(errs.Themes[theme.Name], seerr.Error())
|
||||
}
|
||||
|
@ -85,13 +85,13 @@ func SpeedySyncDeep(i Importer) (errs SyncReport) {
|
|||
log.Printf("Speedy synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path)
|
||||
|
||||
DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep
|
||||
flagsBindings, ferrs := SyncExerciceFlags(i, exercice)
|
||||
flagsBindings, ferrs := SyncExerciceFlags(i, exercice, exceptions[theme.Path])
|
||||
for _, ferr := range ferrs {
|
||||
errs.Themes[theme.Name] = append(errs.Themes[theme.Name], ferr.Error())
|
||||
}
|
||||
|
||||
DeepSyncProgress += exerciceStep / 2
|
||||
_, herrs := SyncExerciceHints(i, exercice, flagsBindings)
|
||||
_, herrs := SyncExerciceHints(i, exercice, flagsBindings, exceptions[theme.Path])
|
||||
for _, herr := range herrs {
|
||||
errs.Themes[theme.Name] = append(errs.Themes[theme.Name], herr.Error())
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func SyncDeep(i Importer) (errs SyncReport) {
|
|||
}
|
||||
|
||||
errs.DateStart = startTime
|
||||
sterrs := SyncThemes(i)
|
||||
exceptions, sterrs := SyncThemes(i)
|
||||
for _, sterr := range sterrs {
|
||||
errs.ThemesSync = append(errs.ThemesSync, sterr.Error())
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func SyncDeep(i Importer) (errs SyncReport) {
|
|||
var themeStep uint8 = uint8(250) / uint8(len(themes))
|
||||
|
||||
for tid, theme := range themes {
|
||||
stderrs := SyncThemeDeep(i, theme, tid, themeStep)
|
||||
stderrs := SyncThemeDeep(i, theme, tid, themeStep, exceptions[theme.Path])
|
||||
for _, stderr := range stderrs {
|
||||
errs.Themes[theme.Name] = append(errs.Themes[theme.Name], stderr.Error())
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ func EditDeepReport(errs *SyncReport, erase bool) {
|
|||
}
|
||||
|
||||
// SyncThemeDeep performs a recursive synchronisation: from challenges to challenge items.
|
||||
func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8) (errs []error) {
|
||||
func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8, exceptions *CheckExceptions) (errs []error) {
|
||||
oneThemeDeepSync.Lock()
|
||||
defer oneThemeDeepSync.Unlock()
|
||||
|
||||
|
@ -220,7 +220,7 @@ func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8) (errs
|
|||
}
|
||||
|
||||
DeepSyncProgress = 3 + uint8(tid)*themeStep
|
||||
errs = SyncExercices(i, theme)
|
||||
errs = SyncExercices(i, theme, exceptions)
|
||||
|
||||
if exercices, err := theme.GetExercices(); err == nil && len(exercices) > 0 {
|
||||
var exerciceStep uint8 = themeStep / uint8(len(exercices))
|
||||
|
@ -228,14 +228,14 @@ func SyncThemeDeep(i Importer, theme *fic.Theme, tid int, themeStep uint8) (errs
|
|||
log.Printf("Deep synchronization in progress: %d/255 - doing Theme %q, Exercice %q: %q\n", DeepSyncProgress, theme.Name, exercice.Title, exercice.Path)
|
||||
|
||||
DeepSyncProgress = 3 + uint8(tid)*themeStep + uint8(eid)*exerciceStep
|
||||
errs = append(errs, SyncExerciceFiles(i, exercice)...)
|
||||
errs = append(errs, SyncExerciceFiles(i, exercice, exceptions)...)
|
||||
|
||||
DeepSyncProgress += exerciceStep / 3
|
||||
flagsBindings, ferrs := SyncExerciceFlags(i, exercice)
|
||||
flagsBindings, ferrs := SyncExerciceFlags(i, exercice, exceptions)
|
||||
errs = append(errs, ferrs...)
|
||||
|
||||
DeepSyncProgress += exerciceStep / 3
|
||||
_, herrs := SyncExerciceHints(i, exercice, flagsBindings)
|
||||
_, herrs := SyncExerciceHints(i, exercice, flagsBindings, exceptions)
|
||||
errs = append(errs, herrs...)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,15 @@ import (
|
|||
|
||||
var hooks = &CheckHooks{}
|
||||
|
||||
type CheckFlagChoiceHook func(*fic.FlagChoice) []error
|
||||
type CheckFlagKeyHook func(*fic.FlagKey, string) []error
|
||||
type CheckFlagKeyWithChoicesHook func(*fic.FlagKey, string, []*fic.FlagChoice) []error
|
||||
type CheckFlagLabelHook func(*fic.FlagLabel) []error
|
||||
type CheckFlagMCQHook func(*fic.MCQ, []*fic.MCQ_entry) []error
|
||||
type CheckFileHook func(*fic.EFile) []error
|
||||
type CheckHintHook func(*fic.EHint) []error
|
||||
type CheckFlagChoiceHook func(*fic.FlagChoice, *CheckExceptions) []error
|
||||
type CheckFlagKeyHook func(*fic.FlagKey, string, *CheckExceptions) []error
|
||||
type CheckFlagKeyWithChoicesHook func(*fic.FlagKey, string, []*fic.FlagChoice, *CheckExceptions) []error
|
||||
type CheckFlagLabelHook func(*fic.FlagLabel, *CheckExceptions) []error
|
||||
type CheckFlagMCQHook func(*fic.MCQ, []*fic.MCQ_entry, *CheckExceptions) []error
|
||||
type CheckFileHook func(*fic.EFile, *CheckExceptions) []error
|
||||
type CheckHintHook func(*fic.EHint, *CheckExceptions) []error
|
||||
type CheckMDTextHook func(string, *CheckExceptions) []error
|
||||
type CheckExerciceHook func(*fic.Exercice, *CheckExceptions) []error
|
||||
|
||||
type CheckHooks struct {
|
||||
flagChoiceHooks []CheckFlagChoiceHook
|
||||
|
@ -25,6 +27,8 @@ type CheckHooks struct {
|
|||
flagMCQHooks []CheckFlagMCQHook
|
||||
fileHooks []CheckFileHook
|
||||
hintHooks []CheckHintHook
|
||||
mdTextHooks []CheckMDTextHook
|
||||
exerciceHooks []CheckExerciceHook
|
||||
}
|
||||
|
||||
func (h *CheckHooks) RegisterFlagChoiceHook(f CheckFlagChoiceHook) {
|
||||
|
@ -55,6 +59,14 @@ func (h *CheckHooks) RegisterHintHook(f CheckHintHook) {
|
|||
h.hintHooks = append(h.hintHooks, f)
|
||||
}
|
||||
|
||||
func (h *CheckHooks) RegisterMDTextHook(f CheckMDTextHook) {
|
||||
h.mdTextHooks = append(h.mdTextHooks, f)
|
||||
}
|
||||
|
||||
func (h *CheckHooks) RegisterExerciceHook(f CheckExerciceHook) {
|
||||
h.exerciceHooks = append(h.exerciceHooks, f)
|
||||
}
|
||||
|
||||
func LoadChecksPlugin(fname string) error {
|
||||
p, err := plugin.Open(fname)
|
||||
if err != nil {
|
||||
|
|
|
@ -104,11 +104,14 @@ func getAuthors(i Importer, tname string) ([]string, error) {
|
|||
}
|
||||
|
||||
// BuildTheme creates a Theme from a given importer.
|
||||
func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []error) {
|
||||
func BuildTheme(i Importer, tdir string) (th *fic.Theme, exceptions *CheckExceptions, errs []error) {
|
||||
th = &fic.Theme{}
|
||||
|
||||
th.Path = tdir
|
||||
|
||||
// Get exceptions
|
||||
exceptions = LoadException(i, th)
|
||||
|
||||
// Extract theme's label
|
||||
if tname, err := GetFileContent(i, path.Join(tdir, "title.txt")); err == nil {
|
||||
th.Name = fixnbsp(tname)
|
||||
|
@ -121,7 +124,7 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []error) {
|
|||
|
||||
if authors, err := getAuthors(i, tdir); err != nil {
|
||||
errs = append(errs, NewThemeError(th, fmt.Errorf("unable to get AUTHORS.txt: %w", err)))
|
||||
return nil, errs
|
||||
return nil, nil, errs
|
||||
} else {
|
||||
// Format authors
|
||||
th.Authors = strings.Join(authors, ", ")
|
||||
|
@ -139,6 +142,13 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []error) {
|
|||
if err != nil {
|
||||
errs = append(errs, NewThemeError(th, fmt.Errorf("unable to get theme's overview: %w", err)))
|
||||
} else {
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.mdTextHooks {
|
||||
for _, err := range h(intro, exceptions.GetFileExceptions("overview.md")) {
|
||||
errs = append(errs, NewThemeError(th, fmt.Errorf("overview.md: %w", err)))
|
||||
}
|
||||
}
|
||||
|
||||
// Split headline from intro
|
||||
ovrvw := strings.Split(fixnbsp(intro), "\n")
|
||||
th.Headline = ovrvw[0]
|
||||
|
@ -188,7 +198,7 @@ func BuildTheme(i Importer, tdir string) (th *fic.Theme, errs []error) {
|
|||
}
|
||||
|
||||
// SyncThemes imports new or updates existing themes.
|
||||
func SyncThemes(i Importer) (errs []error) {
|
||||
func SyncThemes(i Importer) (exceptions map[string]*CheckExceptions, errs []error) {
|
||||
if themes, err := GetThemes(i); err != nil {
|
||||
errs = append(errs, fmt.Errorf("Unable to list themes: %w", err))
|
||||
} else {
|
||||
|
@ -196,8 +206,10 @@ func SyncThemes(i Importer) (errs []error) {
|
|||
themes[i], themes[j] = themes[j], themes[i]
|
||||
})
|
||||
|
||||
exceptions = map[string]*CheckExceptions{}
|
||||
|
||||
for _, tdir := range themes {
|
||||
btheme, berrs := BuildTheme(i, tdir)
|
||||
btheme, excepts, berrs := BuildTheme(i, tdir)
|
||||
for _, e := range berrs {
|
||||
errs = append(errs, e)
|
||||
}
|
||||
|
@ -206,6 +218,8 @@ func SyncThemes(i Importer) (errs []error) {
|
|||
continue
|
||||
}
|
||||
|
||||
exceptions[tdir] = excepts
|
||||
|
||||
if len(btheme.Image) > 0 {
|
||||
if _, err := i.importFile(btheme.Image,
|
||||
func(filePath string, origin string) (interface{}, error) {
|
||||
|
@ -251,6 +265,10 @@ func SyncThemes(i Importer) (errs []error) {
|
|||
return
|
||||
}
|
||||
|
||||
func LoadThemeExceptions(i Importer, theme *fic.Theme) (*CheckExceptions, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ApiListRemoteThemes is an accessor letting foreign packages to access remote themes list.
|
||||
func ApiListRemoteThemes(c *gin.Context) {
|
||||
themes, err := GetThemes(GlobalImporter)
|
||||
|
@ -264,7 +282,7 @@ func ApiListRemoteThemes(c *gin.Context) {
|
|||
|
||||
// ApiListRemoteTheme is an accessor letting foreign packages to access remote main theme attributes.
|
||||
func ApiGetRemoteTheme(c *gin.Context) {
|
||||
r, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
r, _, errs := BuildTheme(GlobalImporter, c.Params.ByName("thid"))
|
||||
if r == nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": fmt.Errorf("%q", errs)})
|
||||
return
|
||||
|
|
|
@ -55,6 +55,16 @@ func GetTheme(id int64) (*Theme, error) {
|
|||
return t, nil
|
||||
}
|
||||
|
||||
// GetTheme retrieves a Theme from an given Exercice.
|
||||
func (e *Exercice) GetTheme() (*Theme, error) {
|
||||
t := &Theme{}
|
||||
if err := DBQueryRow("SELECT id_theme, name, url_id, path, authors, intro, headline, image, partner_img, partner_href, partner_text FROM themes WHERE id_theme=?", e.IdTheme).Scan(&t.Id, &t.Name, &t.URLId, &t.Path, &t.Authors, &t.Intro, &t.Headline, &t.Image, &t.PartnerImage, &t.PartnerLink, &t.PartnerText); err != nil {
|
||||
return t, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// GetThemeByName retrieves a Theme from its title
|
||||
func GetThemeByName(name string) (*Theme, error) {
|
||||
t := &Theme{}
|
||||
|
|
|
@ -4,10 +4,11 @@ import (
|
|||
"fmt"
|
||||
"path"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func EPITACheckFile(file *fic.EFile) (errs []error) {
|
||||
func EPITACheckFile(file *fic.EFile, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
// Enforce file format
|
||||
if path.Ext(file.Name) == "rar" || path.Ext(file.Name) == "7z" {
|
||||
errs = append(errs, fmt.Errorf("this file use a forbidden archive type."))
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"strings"
|
||||
"unicode"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
|
|
|
@ -26,10 +26,11 @@ func (e SpellingError) Error() string {
|
|||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%sspelling error %s %s\n%q\n%s%s",
|
||||
"%sspelling error %s %q (:spelling:%s)\n%q\n%s%s",
|
||||
e.Prefix,
|
||||
e.Type,
|
||||
e.Value,
|
||||
e.Value,
|
||||
e.Source,
|
||||
underline(1, e.Start, e.End),
|
||||
suggestions,
|
||||
|
@ -61,10 +62,10 @@ func (e GrammarError) Error() string {
|
|||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s%s (%s)\n%q\n%s%s",
|
||||
"%s%s (%d:%s)\n%q\n%s%s",
|
||||
e.Prefix,
|
||||
e.Message,
|
||||
e.RuleId,
|
||||
e.NSource, e.RuleId,
|
||||
e.Source,
|
||||
underline(1, e.Start, e.End),
|
||||
suggestions,
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
"srs.epita.fr/fic-server/libfic"
|
||||
)
|
||||
|
||||
func GrammalecteCheckKeyFlag(flag *fic.FlagKey, raw string) (errs []error) {
|
||||
func GrammalecteCheckKeyFlag(flag *fic.FlagKey, raw string, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
label, _, _, _ := flag.AnalyzeFlagLabel()
|
||||
errs = append(errs, grammalecte("label ", label, &CommonOpts)...)
|
||||
errs = append(errs, grammalecte("label ", label, exceptions, &CommonOpts)...)
|
||||
|
||||
if len(flag.Help) > 0 {
|
||||
errs = append(errs, grammalecte("help ", flag.Help, &CommonOpts)...)
|
||||
errs = append(errs, grammalecte("help ", flag.Help, exceptions, &CommonOpts)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GrammalecteCheckFlagChoice(choice *fic.FlagChoice) (errs []error) {
|
||||
errs = append(errs, grammalecte("label ", choice.Label, &CommonOpts)...)
|
||||
func GrammalecteCheckFlagChoice(choice *fic.FlagChoice, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
errs = append(errs, grammalecte("label ", choice.Label, exceptions, &CommonOpts)...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GrammalecteCheckHint(hint *fic.EHint) (errs []error) {
|
||||
func GrammalecteCheckHint(hint *fic.EHint, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
if len(hint.Title) > 0 {
|
||||
errs = append(errs, grammalecte("title ", hint.Title, &CommonOpts)...)
|
||||
errs = append(errs, grammalecte("title ", hint.Title, exceptions, &CommonOpts)...)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func GrammalecteCheckMDText(str string) (errs []error) {
|
||||
errs = append(errs, grammalecte("", str, &CommonOpts)...)
|
||||
func GrammalecteCheckMDText(str string, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
errs = append(errs, grammalecte("", str, exceptions, &CommonOpts)...)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"srs.epita.fr/fic-server/admin/sync"
|
||||
)
|
||||
|
||||
type GrammalecteOptions struct {
|
||||
|
@ -115,7 +118,7 @@ var (
|
|||
mdimg = regexp.MustCompile(`!\[([^\]]+)\]\([^)]+\)`)
|
||||
)
|
||||
|
||||
func grammalecte(name string, text string, options *GrammalecteOptions) (errs []error) {
|
||||
func grammalecte(name string, text string, exceptions *sync.CheckExceptions, options *GrammalecteOptions) (errs []error) {
|
||||
// Remove Markdown elements
|
||||
text = mdimg.ReplaceAllString(text, "Image : ${1}")
|
||||
|
||||
|
@ -161,7 +164,7 @@ func grammalecte(name string, text string, options *GrammalecteOptions) (errs []
|
|||
break
|
||||
}
|
||||
}
|
||||
if allowed {
|
||||
if allowed || exceptions.HasException(":spelling:"+serror.Value) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -183,6 +186,10 @@ func grammalecte(name string, text string, options *GrammalecteOptions) (errs []
|
|||
continue
|
||||
}
|
||||
|
||||
if exceptions.HasException(fmt.Sprintf(":%d:%s", data.Paragraph, gerror.RuleId)) {
|
||||
continue
|
||||
}
|
||||
|
||||
errs = append(errs, GrammarError{
|
||||
Prefix: name,
|
||||
Source: data.Text,
|
||||
|
|
|
@ -108,8 +108,8 @@ func searchBinaryInGit(edir string) (ret []string) {
|
|||
return
|
||||
}
|
||||
|
||||
func checkExercice(theme *fic.Theme, edir string, dmap *map[int64]*fic.Exercice) (errs []error) {
|
||||
e, _, eid, _, berrs := sync.BuildExercice(sync.GlobalImporter, theme, path.Join(theme.Path, edir), dmap)
|
||||
func checkExercice(theme *fic.Theme, edir string, dmap *map[int64]*fic.Exercice, exceptions *sync.CheckExceptions) (errs []error) {
|
||||
e, _, eid, _, berrs := sync.BuildExercice(sync.GlobalImporter, theme, path.Join(theme.Path, edir), dmap, exceptions)
|
||||
errs = append(errs, berrs...)
|
||||
|
||||
if e != nil {
|
||||
|
@ -117,7 +117,7 @@ func checkExercice(theme *fic.Theme, edir string, dmap *map[int64]*fic.Exercice)
|
|||
var files []string
|
||||
var cerrs []error
|
||||
if !skipFileChecks {
|
||||
files, cerrs = sync.CheckExerciceFiles(sync.GlobalImporter, e)
|
||||
files, cerrs = sync.CheckExerciceFiles(sync.GlobalImporter, e, exceptions)
|
||||
log.Printf("%d files checked.\n", len(files))
|
||||
} else {
|
||||
files, cerrs = sync.CheckExerciceFilesPresence(sync.GlobalImporter, e)
|
||||
|
@ -126,12 +126,12 @@ func checkExercice(theme *fic.Theme, edir string, dmap *map[int64]*fic.Exercice)
|
|||
errs = append(errs, cerrs...)
|
||||
|
||||
// Flags
|
||||
flags, cerrs := sync.CheckExerciceFlags(sync.GlobalImporter, e, files)
|
||||
flags, cerrs := sync.CheckExerciceFlags(sync.GlobalImporter, e, files, exceptions)
|
||||
errs = append(errs, cerrs...)
|
||||
log.Printf("%d flags checked.\n", len(flags))
|
||||
|
||||
// Hints
|
||||
hints, cerrs := sync.CheckExerciceHints(sync.GlobalImporter, e)
|
||||
hints, cerrs := sync.CheckExerciceHints(sync.GlobalImporter, e, exceptions)
|
||||
errs = append(errs, cerrs...)
|
||||
log.Printf("%d hints checked.\n", len(hints))
|
||||
|
||||
|
@ -242,7 +242,7 @@ func main() {
|
|||
}
|
||||
|
||||
nberr := 0
|
||||
theme, errs := sync.BuildTheme(sync.GlobalImporter, p)
|
||||
theme, exceptions, errs := sync.BuildTheme(sync.GlobalImporter, p)
|
||||
|
||||
if theme != nil {
|
||||
nberr += len(errs)
|
||||
|
@ -260,7 +260,7 @@ func main() {
|
|||
dmap := map[int64]*fic.Exercice{}
|
||||
|
||||
for _, edir := range exercices {
|
||||
for _, err := range checkExercice(theme, edir, &dmap) {
|
||||
for _, err := range checkExercice(theme, edir, &dmap, exceptions) {
|
||||
log.Println(err.Error())
|
||||
|
||||
if logMissingResolution {
|
||||
|
@ -290,7 +290,7 @@ func main() {
|
|||
} else {
|
||||
log.Printf("This is not a theme directory, run checks for exercice.\n\n")
|
||||
|
||||
for _, err := range checkExercice(&fic.Theme{}, p, &map[int64]*fic.Exercice{}) {
|
||||
for _, err := range checkExercice(&fic.Theme{}, p, &map[int64]*fic.Exercice{}, exceptions) {
|
||||
nberr += 1
|
||||
log.Println(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue