admin: Add exercice's tags: sync, api, interface done
This commit is contained in:
parent
665fd301c6
commit
f183985982
@ -41,6 +41,10 @@ func init() {
|
|||||||
router.PUT("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(updateExerciceQuiz)))
|
router.PUT("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(updateExerciceQuiz)))
|
||||||
router.DELETE("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(deleteExerciceQuiz)))
|
router.DELETE("/api/exercices/:eid/quiz/:qid", apiHandler(quizHandler(deleteExerciceQuiz)))
|
||||||
|
|
||||||
|
router.GET("/api/exercices/:eid/tags", apiHandler(exerciceHandler(listExerciceTags)))
|
||||||
|
router.POST("/api/exercices/:eid/tags", apiHandler(exerciceHandler(addExerciceTag)))
|
||||||
|
router.PUT("/api/exercices/:eid/tags", apiHandler(exerciceHandler(updateExerciceTags)))
|
||||||
|
|
||||||
// Synchronize
|
// Synchronize
|
||||||
router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler(
|
router.POST("/api/sync/exercices/:eid/files", apiHandler(exerciceHandler(
|
||||||
func(exercice fic.Exercice, _ []byte) (interface{}, error) {
|
func(exercice fic.Exercice, _ []byte) (interface{}, error) {
|
||||||
@ -352,3 +356,29 @@ func showExerciceFile(file fic.EFile, body []byte) (interface{}, error) {
|
|||||||
func deleteExerciceFile(file fic.EFile, _ []byte) (interface{}, error) {
|
func deleteExerciceFile(file fic.EFile, _ []byte) (interface{}, error) {
|
||||||
return file.Delete()
|
return file.Delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func listExerciceTags(exercice fic.Exercice, _ []byte) (interface{}, error) {
|
||||||
|
return exercice.GetTags()
|
||||||
|
}
|
||||||
|
|
||||||
|
func addExerciceTag(exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||||
|
var ut []string
|
||||||
|
if err := json.Unmarshal(body, &ut); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: a DB transaction should be done here: on error we should rollback
|
||||||
|
for _, t := range ut {
|
||||||
|
if _, err := exercice.AddTag(t); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ut, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateExerciceTags(exercice fic.Exercice, body []byte) (interface{}, error) {
|
||||||
|
exercice.WipeTags()
|
||||||
|
return addExerciceTag(exercice, body)
|
||||||
|
}
|
||||||
|
@ -208,6 +208,11 @@ angular.module("FICApp")
|
|||||||
update: {method: 'PUT'}
|
update: {method: 'PUT'}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.factory("ExerciceTags", function($resource) {
|
||||||
|
return $resource("/api/exercices/:exerciceId/tags", { exerciceId: '@idExercice'}, {
|
||||||
|
update: {method: 'PUT'}
|
||||||
|
})
|
||||||
|
})
|
||||||
.factory("ExerciceFile", function($resource) {
|
.factory("ExerciceFile", function($resource) {
|
||||||
return $resource("/api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, {
|
return $resource("/api/exercices/:exerciceId/files/:fileId", { exerciceId: '@idExercice', fileId: '@id' }, {
|
||||||
update: {method: 'PUT'}
|
update: {method: 'PUT'}
|
||||||
@ -1023,6 +1028,11 @@ angular.module("FICApp")
|
|||||||
$scope.exercices = Exercice.query();
|
$scope.exercices = Exercice.query();
|
||||||
$scope.fields = ["title", "urlid", "statement", "overview", "depend", "gain", "coefficient", "videoURI"];
|
$scope.fields = ["title", "urlid", "statement", "overview", "depend", "gain", "coefficient", "videoURI"];
|
||||||
|
|
||||||
|
$scope.showTags = false;
|
||||||
|
$scope.toggleTags = function(val) {
|
||||||
|
$scope.showTags = val ||!$scope.showTags;
|
||||||
|
}
|
||||||
|
|
||||||
$scope.showDownloads = false;
|
$scope.showDownloads = false;
|
||||||
$scope.toggleDownloads = function(val) {
|
$scope.toggleDownloads = function(val) {
|
||||||
$scope.showDownloads = val ||!$scope.showDownloads;
|
$scope.showDownloads = val ||!$scope.showDownloads;
|
||||||
@ -1054,6 +1064,22 @@ angular.module("FICApp")
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.controller("ExerciceTagsController", function($scope, ExerciceTags, $routeParams, $rootScope, $http) {
|
||||||
|
$scope.tags = ExerciceTags.query({ exerciceId: $routeParams.exerciceId });
|
||||||
|
|
||||||
|
$scope.addTag = function() {
|
||||||
|
$scope.toggleTags(true);
|
||||||
|
$scope.tags.push("");
|
||||||
|
}
|
||||||
|
$scope.deleteTag = function() {
|
||||||
|
$scope.tags.splice($scope.tags.indexOf(this.tag), 1);
|
||||||
|
return $scope.saveTags();
|
||||||
|
}
|
||||||
|
$scope.saveTags = function() {
|
||||||
|
ExerciceTags.update({ exerciceId: $routeParams.exerciceId }, this.tags);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
.controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams, $rootScope, $http) {
|
.controller("ExerciceFilesController", function($scope, ExerciceFile, $routeParams, $rootScope, $http) {
|
||||||
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
$scope.files = ExerciceFile.query({ exerciceId: $routeParams.exerciceId });
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="col-md-4" ng-controller="ExerciceFilesController" ng-show="exercice.id">
|
<div class="col-md-4" ng-show="exercice.id">
|
||||||
<div class="card border-secondary">
|
<div class="card border-secondary" ng-controller="ExerciceFilesController">
|
||||||
<div class="card-header bg-secondary text-light">
|
<div class="card-header bg-secondary text-light">
|
||||||
<h4 class="m-0" ng-click="toggleDownloads()"><small class="glyphicon" ng-class="{'glyphicon-chevron-right': !showDownloads, 'glyphicon-chevron-down': showDownloads}" aria-hidden="true"></small> Téléchargements</h4>
|
<h4 class="m-0" ng-click="toggleDownloads()"><small class="glyphicon" ng-class="{'glyphicon-chevron-right': !showDownloads, 'glyphicon-chevron-down': showDownloads}" aria-hidden="true"></small> Téléchargements</h4>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 card border-info" ng-controller="ExerciceHintsController" ng-show="exercice.id">
|
<div class="mt-2 card border-info" ng-controller="ExerciceHintsController">
|
||||||
<div class="card-header bg-info text-light">
|
<div class="card-header bg-info text-light">
|
||||||
<button type="button" ng-click="addHint()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
<button type="button" ng-click="addHint()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
||||||
<button type="button" ng-click="syncHints()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
<button type="button" ng-click="syncHints()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||||
@ -79,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 card border-success" ng-controller="ExerciceFlagsController" ng-show="exercice.id">
|
<div class="mt-2 card border-success" ng-controller="ExerciceFlagsController">
|
||||||
<div class="card-header bg-success border-success text-light">
|
<div class="card-header bg-success border-success text-light">
|
||||||
<button type="button" ng-click="addFlag()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
<button type="button" ng-click="addFlag()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
||||||
<button type="button" ng-click="syncFlags()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
<button type="button" ng-click="syncFlags()" class="float-right btn btn-sm btn-light ml-2"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
|
||||||
@ -114,7 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-2 card border-success" ng-controller="ExerciceMCQFlagsController" ng-show="exercice.id">
|
<div class="mt-2 card border-success" ng-controller="ExerciceMCQFlagsController">
|
||||||
<div class="card-header bg-success text-light">
|
<div class="card-header bg-success text-light">
|
||||||
<button type="button" ng-click="addQuiz()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
<button type="button" ng-click="addQuiz()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
||||||
<h4 class="m-0" ng-click="toggleQuizz()"><small class="glyphicon" ng-class="{'glyphicon-chevron-right': !showQuizz, 'glyphicon-chevron-down': showQuizz}" aria-hidden="true"></small> Quizz</h4>
|
<h4 class="m-0" ng-click="toggleQuizz()"><small class="glyphicon" ng-class="{'glyphicon-chevron-right': !showQuizz, 'glyphicon-chevron-down': showQuizz}" aria-hidden="true"></small> Quizz</h4>
|
||||||
@ -152,6 +152,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 card border-warning" ng-controller="ExerciceTagsController">
|
||||||
|
<div class="card-header bg-warning text-light">
|
||||||
|
<button type="button" ng-click="addTag()" class="float-right btn btn-sm btn-primary ml-2"><span class="glyphicon glyphicon-plus" aria-hidden="true"></span></button>
|
||||||
|
<button type="button" ng-click="saveTags()" class="float-right btn btn-sm btn-success ml-2" ng-show="showTags"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||||
|
<h4 class="m-0" ng-click="toggleTags()"><small class="glyphicon" ng-class="{'glyphicon-chevron-right': !showTags, 'glyphicon-chevron-down': showTags}" aria-hidden="true"></small> Tags</h4>
|
||||||
|
</div>
|
||||||
|
<div class="list-group" ng-show="showTags">
|
||||||
|
<form ng-submit="saveTags()" class="list-group-item bg-light text-dark">
|
||||||
|
<div class="row form-group" ng-repeat="(k, tag) in tags track by $index">
|
||||||
|
<input type="text" ng-model="tags[k]" class="col form-control form-control-sm" placeholder="#tag">
|
||||||
|
<button type="button" ng-click="deleteTag()" class="btn btn-sm btn-danger col-auto"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ Tous les textes doivent utiliser l'encodage UTF8.
|
|||||||
+ `statement.txt` contenant le scénario du challenge, tel qu'il sera affiché sur le site, à destination des participants
|
+ `statement.txt` contenant le scénario du challenge, tel qu'il sera affiché sur le site, à destination des participants
|
||||||
+ `challenge.txt` définitions des paramètres de votre challenge (au format [toml](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)) :
|
+ `challenge.txt` définitions des paramètres de votre challenge (au format [toml](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)) :
|
||||||
- `gain = 42` : nombre de points que rapporte cet exercice ;
|
- `gain = 42` : nombre de points que rapporte cet exercice ;
|
||||||
|
- `tags = ["Android", "RAT", "ROM"]` : mots-clefs de l'exercice ;
|
||||||
- `[[depend]]` : dépendance à un autre exercice :
|
- `[[depend]]` : dépendance à un autre exercice :
|
||||||
* `id = CHID` : identifiant du challenge ;
|
* `id = CHID` : identifiant du challenge ;
|
||||||
* `theme = "NomDuTheme"` : (facultatif) nom du thème dans lequel aller chercher l'identifiant (par défaut, on prend le thème courant) ;
|
* `theme = "NomDuTheme"` : (facultatif) nom du thème dans lequel aller chercher l'identifiant (par défaut, on prend le thème courant) ;
|
||||||
|
@ -69,6 +69,7 @@ type ExerciceFlagUCQ struct {
|
|||||||
// ExerciceParams contains values parsed from defines.txt.
|
// ExerciceParams contains values parsed from defines.txt.
|
||||||
type ExerciceParams struct {
|
type ExerciceParams struct {
|
||||||
Gain int64
|
Gain int64
|
||||||
|
Tags []string
|
||||||
Hints []ExerciceHintParams `toml:"hint"`
|
Hints []ExerciceHintParams `toml:"hint"`
|
||||||
Dependencies []ExerciceDependency `toml:"depend"`
|
Dependencies []ExerciceDependency `toml:"depend"`
|
||||||
Flags []ExerciceFlag `toml:"flag"`
|
Flags []ExerciceFlag `toml:"flag"`
|
||||||
|
@ -69,6 +69,7 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
|
|||||||
// Handle score gain
|
// Handle score gain
|
||||||
var gain int64
|
var gain int64
|
||||||
var depend *fic.Exercice
|
var depend *fic.Exercice
|
||||||
|
var tags []string
|
||||||
if p, err := parseExerciceParams(i, path.Join(theme.Path, edir)); err != nil {
|
if p, err := parseExerciceParams(i, path.Join(theme.Path, edir)); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", edir, err))
|
errs = append(errs, fmt.Sprintf("%q: challenge.txt: %s", edir, err))
|
||||||
continue
|
continue
|
||||||
@ -76,6 +77,7 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
|
|||||||
errs = append(errs, fmt.Sprintf("%q: challenge.txt: Undefined gain for challenge", edir))
|
errs = append(errs, fmt.Sprintf("%q: challenge.txt: Undefined gain for challenge", edir))
|
||||||
} else {
|
} else {
|
||||||
gain = p.Gain
|
gain = p.Gain
|
||||||
|
tags = p.Tags
|
||||||
|
|
||||||
// Handle dependency
|
// Handle dependency
|
||||||
if len(p.Dependencies) > 0 {
|
if len(p.Dependencies) > 0 {
|
||||||
@ -110,12 +112,11 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
|
|||||||
statement = string(blackfriday.Run([]byte(statement)))
|
statement = string(blackfriday.Run([]byte(statement)))
|
||||||
overview = string(blackfriday.Run([]byte(overview)))
|
overview = string(blackfriday.Run([]byte(overview)))
|
||||||
|
|
||||||
if e, err := theme.GetExerciceByTitle(ename); err != nil {
|
e, err := theme.GetExerciceByTitle(ename)
|
||||||
if ex, err := theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, depend, gain, videoURI); err != nil {
|
if err != nil {
|
||||||
|
if e, err = theme.AddExercice(ename, fic.ToURLid(ename), path.Join(theme.Path, edir), statement, overview, depend, gain, videoURI); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error on exercice add: %s", edir, err))
|
errs = append(errs, fmt.Sprintf("%q: error on exercice add: %s", edir, err))
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
dmap[int64(eid)] = ex
|
|
||||||
}
|
}
|
||||||
} else if e.Title != ename || e.URLId == "" || e.Statement != statement || e.Overview != overview || e.Gain != gain || e.VideoURI != videoURI {
|
} else if e.Title != ename || e.URLId == "" || e.Statement != statement || e.Overview != overview || e.Gain != gain || e.VideoURI != videoURI {
|
||||||
e.Title = ename
|
e.Title = ename
|
||||||
@ -127,11 +128,18 @@ func SyncExercices(i Importer, theme fic.Theme) []string {
|
|||||||
if _, err := e.Update(); err != nil {
|
if _, err := e.Update(); err != nil {
|
||||||
errs = append(errs, fmt.Sprintf("%q: error on exercice update: %s", edir, err))
|
errs = append(errs, fmt.Sprintf("%q: error on exercice update: %s", edir, err))
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
dmap[int64(eid)] = e
|
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
dmap[int64(eid)] = e
|
dmap[int64(eid)] = e
|
||||||
|
|
||||||
|
if _, err := e.WipeTags(); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: Unable to wipe tags: %s", edir, err))
|
||||||
|
}
|
||||||
|
for _, tag := range tags {
|
||||||
|
if _, err := e.AddTag(tag); err != nil {
|
||||||
|
errs = append(errs, fmt.Sprintf("%q: Unable to add tag: %s", edir, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +246,15 @@ CREATE TABLE IF NOT EXISTS exercice_tries(
|
|||||||
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
|
||||||
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
FOREIGN KEY(id_team) REFERENCES teams(id_team)
|
||||||
) 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_tags(
|
||||||
|
id_exercice INTEGER NOT NULL,
|
||||||
|
tag VARCHAR(255) NOT NULL,
|
||||||
|
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice)
|
||||||
|
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||||
`); err != nil {
|
`); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
52
libfic/exercice_tag.go
Normal file
52
libfic/exercice_tag.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package fic
|
||||||
|
|
||||||
|
// GetTags returns tags associated with this exercice.
|
||||||
|
func (e Exercice) GetTags() (tags []string, err error) {
|
||||||
|
if rows, errr := DBQuery("SELECT tag FROM exercice_tags WHERE id_exercice = ?", e.Id); errr != nil {
|
||||||
|
return nil, errr
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
tags = make([]string, 0)
|
||||||
|
for rows.Next() {
|
||||||
|
var t string
|
||||||
|
if err = rows.Scan(&t); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tags = append(tags, t)
|
||||||
|
}
|
||||||
|
err = rows.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTag assign a new tag to the exercice and registers it into the database.
|
||||||
|
func (e Exercice) AddTag(tag string) (string, error) {
|
||||||
|
if _, err := DBExec("INSERT INTO exercice_tags (id_exercice, tag) VALUES (?, ?)", e.Id, tag); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTag delete a tag assigned to the current exercice from the database.
|
||||||
|
func (e Exercice) DeleteTag(tag string) (int64, error) {
|
||||||
|
if res, err := DBExec("DELETE FROM exercice_tags WHERE id_exercice = ? AND tag = ?", e.Id, tag); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WipeTags delete all tag assigned to the current exercice from the database.
|
||||||
|
func (e Exercice) WipeTags() (int64, error) {
|
||||||
|
if res, err := DBExec("DELETE FROM exercice_tags WHERE id_exercice = ?", e.Id); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if nb, err := res.RowsAffected(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return nb, 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_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercices", "themes")
|
return truncateTable("team_hints", "exercice_files_deps", "exercice_files", "flag_found", "exercice_flags", "exercice_solved", "exercice_tries", "exercice_hints", "mcq_found", "mcq_entries", "exercice_mcq", "exercice_tags", "exercices", "themes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetTeams wipes out all teams, incluings members and attempts.
|
// ResetTeams wipes out all teams, incluings members and attempts.
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
type exportedExercice struct {
|
type exportedExercice struct {
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
URLId string `json:"urlid"`
|
URLId string `json:"urlid"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
Gain int64 `json:"gain"`
|
Gain int64 `json:"gain"`
|
||||||
Coeff float64 `json:"curcoeff"`
|
Coeff float64 `json:"curcoeff"`
|
||||||
Solved int64 `json:"solved"`
|
Solved int64 `json:"solved"`
|
||||||
@ -35,9 +36,11 @@ func ExportThemes() (interface{}, error) {
|
|||||||
} else {
|
} else {
|
||||||
exos := map[string]exportedExercice{}
|
exos := map[string]exportedExercice{}
|
||||||
for _, exercice := range exercices {
|
for _, exercice := range exercices {
|
||||||
|
tags, _ := exercice.GetTags()
|
||||||
exos[fmt.Sprintf("%d", exercice.Id)] = exportedExercice{
|
exos[fmt.Sprintf("%d", exercice.Id)] = exportedExercice{
|
||||||
exercice.Title,
|
exercice.Title,
|
||||||
exercice.URLId,
|
exercice.URLId,
|
||||||
|
tags,
|
||||||
exercice.Gain,
|
exercice.Gain,
|
||||||
exercice.Coefficient,
|
exercice.Coefficient,
|
||||||
exercice.SolvedCount(),
|
exercice.SolvedCount(),
|
||||||
|
Loading…
Reference in New Issue
Block a user