sync: add dependency on flag to download file
This commit is contained in:
parent
dcfb34c6fd
commit
1e2a74f3ca
9 changed files with 112 additions and 16 deletions
|
@ -20,18 +20,18 @@ type ExerciceDependency struct {
|
||||||
Theme string `toml:",omitempty"`
|
Theme string `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExerciceUnlockFile holds parameters related to a locked file.
|
||||||
|
type ExerciceUnlockFile struct {
|
||||||
|
Filename string `toml:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// ExerciceFlag holds informations about a "classic" flag.
|
// ExerciceFlag holds informations about a "classic" flag.
|
||||||
type ExerciceFlag struct {
|
type ExerciceFlag struct {
|
||||||
Label string `toml:",omitempty"`
|
Label string `toml:",omitempty"`
|
||||||
Raw string
|
Raw string
|
||||||
IgnoreCase bool `toml:",omitempty"`
|
IgnoreCase bool `toml:",omitempty"`
|
||||||
Help string `toml:",omitempty"`
|
Help string `toml:",omitempty"`
|
||||||
LockedFile []ExerciceFlag `toml:unlock_file",omitempty"`
|
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
||||||
}
|
|
||||||
|
|
||||||
// ExerciceUnlockFile holds parameters related to a locked file.
|
|
||||||
type ExerciceFlag struct {
|
|
||||||
Filename string `toml:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExerciceFlagMCQChoice holds a choice for an MCQ flag.
|
// ExerciceFlagMCQChoice holds a choice for an MCQ flag.
|
||||||
|
@ -60,7 +60,7 @@ type ExerciceFlagUCQ struct {
|
||||||
DisplayAs string `toml:",omitempty"`
|
DisplayAs string `toml:",omitempty"`
|
||||||
Choices_Cost int64 `toml:",omitempty"`
|
Choices_Cost int64 `toml:",omitempty"`
|
||||||
Choice []ExerciceFlagUCQChoice
|
Choice []ExerciceFlagUCQChoice
|
||||||
LockedFile []ExerciceFlag `toml:unlock_file",omitempty"`
|
LockedFile []ExerciceUnlockFile `toml:"unlock_file,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExerciceParams contains values parsed from defines.txt.
|
// ExerciceParams contains values parsed from defines.txt.
|
||||||
|
|
|
@ -40,9 +40,20 @@ func SyncExerciceKeys(i Importer, exercice fic.Exercice) (errs []string) {
|
||||||
errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
|
errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := exercice.AddRawKey(flag.Label, flag.Raw); err != nil {
|
if k, err := exercice.AddRawKey(flag.Label, flag.Raw); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
// Import dependency to file
|
||||||
|
for _, lf := range flag.LockedFile {
|
||||||
|
if rf, err := exercice.GetFileByFilename(lf.Filename); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), nline + 1, lf.Filename, err))
|
||||||
|
continue
|
||||||
|
} else if err := rf.AddDepend(k); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), nline + 1, lf.Filename, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,9 +67,20 @@ func SyncExerciceKeys(i Importer, exercice fic.Exercice) (errs []string) {
|
||||||
errs = append(errs, fmt.Sprintf("%q: WARNING flag UCQ #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
|
errs = append(errs, fmt.Sprintf("%q: WARNING flag UCQ #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), nline + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := exercice.AddRawKey(flag.Label, flag.Raw); err != nil {
|
if k, err := exercice.AddRawKey(flag.Label, flag.Raw); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error flag UCQ #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
errs = append(errs, fmt.Sprintf("%q: error flag UCQ #%d: %s", path.Base(exercice.Path), nline + 1, err))
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
// Import dependency to file
|
||||||
|
for _, lf := range flag.LockedFile {
|
||||||
|
if rf, err := exercice.GetFileByFilename(lf.Filename); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error flag UCQ #%d dependency to %s: %s", path.Base(exercice.Path), nline + 1, lf.Filename, err))
|
||||||
|
continue
|
||||||
|
} else if err := rf.AddDepend(k); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: error flag UCQ #%d dependency to %s: %s", path.Base(exercice.Path), nline + 1, lf.Filename, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,9 @@ func SyncDeep(i Importer) (errs map[string][]string) {
|
||||||
|
|
||||||
if exercices, err := theme.GetExercices(); err == nil {
|
if exercices, err := theme.GetExercices(); err == nil {
|
||||||
for _, exercice := range exercices {
|
for _, exercice := range exercices {
|
||||||
|
errs[theme.Name] = append(errs[theme.Name], SyncExerciceKeys(i, exercice)...)
|
||||||
errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...)
|
errs[theme.Name] = append(errs[theme.Name], SyncExerciceFiles(i, exercice)...)
|
||||||
errs[theme.Name] = append(errs[theme.Name], SyncExerciceHints(i, exercice)...)
|
errs[theme.Name] = append(errs[theme.Name], SyncExerciceHints(i, exercice)...)
|
||||||
errs[theme.Name] = append(errs[theme.Name], SyncExerciceKeys(i, exercice)...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
libfic/db.go
10
libfic/db.go
|
@ -163,6 +163,16 @@ CREATE TABLE IF NOT EXISTS exercice_keys(
|
||||||
cksum BINARY(64) NOT NULL,
|
cksum BINARY(64) NOT NULL,
|
||||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||||
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := db.Exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS exercice_files_deps(
|
||||||
|
id_file INTEGER NOT NULL,
|
||||||
|
id_key INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(id_file) REFERENCES exercice_files(id_file),
|
||||||
|
FOREIGN KEY(id_key) REFERENCES exercice_keys(id_key)
|
||||||
|
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||||
`); err != nil {
|
`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,15 @@ func GetFileByPath(path string) (EFile, error) {
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFileByFilename retrieves the file that should be called so.
|
||||||
|
func (e Exercice) GetFileByFilename(filename string) (f EFile, err error) {
|
||||||
|
filename = path.Base(filename)
|
||||||
|
|
||||||
|
err = DBQueryRow("SELECT id_file, origin, path, id_exercice, name, cksum, size FROM exercice_files WHERE id_exercice = ? AND origin LIKE ?", e.Id, "%/" + filename).Scan(&f.Id, &f.origin, &f.Path, &f.IdExercice, &f.Name, &f.Checksum, &f.Size)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// GetFiles returns a list of files coming with the challenge.
|
// GetFiles returns a list of files coming with the challenge.
|
||||||
func (e Exercice) GetFiles() ([]EFile, error) {
|
func (e Exercice) GetFiles() ([]EFile, error) {
|
||||||
if rows, err := DBQuery("SELECT id_file, origin, path, name, cksum, size FROM exercice_files WHERE id_exercice = ?", e.Id); err != nil {
|
if rows, err := DBQuery("SELECT id_file, origin, path, name, cksum, size FROM exercice_files WHERE id_exercice = ?", e.Id); err != nil {
|
||||||
|
@ -262,6 +271,35 @@ func (f EFile) GetOrigin() string {
|
||||||
return f.origin
|
return f.origin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddDepend insert a new dependency to a given flag.
|
||||||
|
func (f EFile) AddDepend(k Key) (err error) {
|
||||||
|
_, err = DBExec("INSERT INTO exercice_files_deps (id_file, id_key) VALUES (?, ?)", f.Id, k.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDepends retrieve the flag's dependency list.
|
||||||
|
func (f EFile) GetDepends() ([]Key, error) {
|
||||||
|
if rows, err := DBQuery("SELECT id_key FROM exercice_files_deps WHERE id_file = ?", f.Id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var deps = make([]Key, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var d int64
|
||||||
|
if err := rows.Scan(&d); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
deps = append(deps, Key{d, f.IdExercice, "", []byte{}})
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return deps, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CheckFileOnDisk recalculates the hash of the file on disk.
|
// CheckFileOnDisk recalculates the hash of the file on disk.
|
||||||
func (f EFile) CheckFileOnDisk() error {
|
func (f EFile) CheckFileOnDisk() error {
|
||||||
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), f.Checksum); err != nil {
|
if _, size, err := checkFileHash(path.Join(FilesDir, f.Path), f.Checksum); err != nil {
|
||||||
|
|
|
@ -79,7 +79,9 @@ func (k Key) Update() (int64, error) {
|
||||||
|
|
||||||
// Delete the flag from the database.
|
// Delete the flag from the database.
|
||||||
func (k Key) Delete() (int64, error) {
|
func (k Key) Delete() (int64, error) {
|
||||||
if res, err := DBExec("DELETE FROM exercice_keys WHERE id_key = ?", k.Id); err != nil {
|
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_key = ?", k.Id); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if res, err := DBExec("DELETE FROM exercice_keys WHERE id_key = ?", k.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -90,7 +92,9 @@ func (k Key) Delete() (int64, error) {
|
||||||
|
|
||||||
// WipeKeys deletes keys coming with the challenge.
|
// WipeKeys deletes keys coming with the challenge.
|
||||||
func (e Exercice) WipeKeys() (int64, error) {
|
func (e Exercice) WipeKeys() (int64, error) {
|
||||||
if res, err := DBExec("DELETE FROM exercice_keys WHERE id_exercice = ?", e.Id); err != nil {
|
if _, err := DBExec("DELETE FROM exercice_files_deps WHERE id_key IN (SELECT id_key FROM exercice_keys WHERE id_exercice = ?)", e.Id); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if res, err := DBExec("DELETE FROM exercice_keys WHERE id_exercice = ?", e.Id); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else if nb, err := res.RowsAffected(); err != nil {
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
|
@ -34,7 +34,7 @@ func ResetGame() (error) {
|
||||||
|
|
||||||
// ResetExercices wipes out all challenges (both attempts and statements).
|
// ResetExercices wipes out all challenges (both attempts and statements).
|
||||||
func ResetExercices() (error) {
|
func ResetExercices() (error) {
|
||||||
return truncateTable("team_hints", "exercice_files", "key_found", "exercice_keys", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
|
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "key_found", "exercice_keys", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetTeams wipes out all teams, incluings members and attempts.
|
// ResetTeams wipes out all teams, incluings members and attempts.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package fic
|
package fic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -104,6 +105,25 @@ func (t Team) HasAccess(e Exercice) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanDownload checks if the Team has access to the given file.
|
||||||
|
func (t Team) CanDownload(f EFile) bool {
|
||||||
|
if deps, err := f.GetDepends(); err != nil {
|
||||||
|
log.Printf("Unable to retrieve file dependencies: %s\n", err)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
res := true
|
||||||
|
|
||||||
|
for _, dep := range deps {
|
||||||
|
if t.HasPartiallySolved(dep) == nil {
|
||||||
|
res = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NbTry retrieves the number of attempts made by the Team to the given challenge.
|
// NbTry retrieves the number of attempts made by the Team to the given challenge.
|
||||||
func NbTry(t *Team, e Exercice) int {
|
func NbTry(t *Team, e Exercice) int {
|
||||||
var cnt *int
|
var cnt *int
|
||||||
|
|
|
@ -115,7 +115,9 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(f.Checksum), f.Size})
|
if t == nil || t.CanDownload(f) {
|
||||||
|
exercice.Files = append(exercice.Files, myTeamFile{path.Join(FilesDir, f.Path), f.Name, hex.EncodeToString(f.Checksum), f.Size})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue