Add issue field for exercice, to be able to communicate about problem with exercice

This commit is contained in:
nemunaire 2018-11-21 02:20:37 +01:00 committed by Pierre-Olivier Mercier
parent 0654033721
commit 44d335bc9f
8 changed files with 43 additions and 17 deletions

View File

@ -139,6 +139,14 @@ func partUpdateExercice(exercice fic.Exercice, body []byte) (interface{}, error)
exercice.Overview = ue.Overview exercice.Overview = ue.Overview
} }
if len(ue.Issue) > 0 {
exercice.Issue = ue.Issue
}
if len(ue.IssueKind) > 0 {
exercice.IssueKind = ue.IssueKind
}
if ue.Depend != nil { if ue.Depend != nil {
exercice.Depend = ue.Depend exercice.Depend = ue.Depend
} }

View File

@ -1044,7 +1044,7 @@ angular.module("FICApp")
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId }); $scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
} }
$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", "issue", "issuekind"];
$scope.showTags = false; $scope.showTags = false;
$scope.toggleTags = function(val) { $scope.toggleTags = function(val) {

View File

@ -48,16 +48,17 @@
<form ng-submit="updateExercices()"> <form ng-submit="updateExercices()">
<fieldset> <fieldset>
<legend class="text-dark">Édition de masse <button type="submit" class="float-right btn btn-sm btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button></legend> <legend class="text-dark">Édition de masse <button type="submit" class="float-right btn btn-sm btn-success"><span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button></legend>
<div class="form-group row" ng-repeat="field in ['gain','coefficient']"> <div class="form-group row" ng-repeat="field in ['gain','coefficient','issue','issuekind']">
<label for="{{ field }}" class="col-sm-1 col-form-label-sm">{{ field | capitalize }}</label> <label for="{{ field }}" class="col-sm-1 col-form-label-sm">{{ field | capitalize }}</label>
<div class="col-sm-11"> <div class="col-sm-11">
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field != 'statement' && field != 'overview' && field != 'depend' && field != 'gain' && field != 'coefficient'"> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field != 'statement' && field != 'issue' && field != 'issuekind' && field != 'overview' && field != 'depend' && field != 'gain' && field != 'coefficient'">
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'gain'" integer> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'gain'" integer>
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'coefficient'" float> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'coefficient'" float>
<textarea class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-if="field == 'statement' || field == 'overview'"></textarea> <textarea class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-if="field == 'statement' || field == 'overview' || field == 'issue'"></textarea>
<select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'depend'"> <select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'depend'">
<option value="">Aucune</option> <option value="">Aucune</option>
</select> </select>
<select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="v for v in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark']" ng-if="field == 'issuekind'"></select>
</div> </div>
</div> </div>
</fieldset> </fieldset>

View File

@ -6,13 +6,14 @@
<div class="form-group row" ng-repeat="field in fields"> <div class="form-group row" ng-repeat="field in fields">
<label for="{{ field }}" class="col-sm-1 col-form-label-sm">{{ field | capitalize }}</label> <label for="{{ field }}" class="col-sm-1 col-form-label-sm">{{ field | capitalize }}</label>
<div class="col-sm-11"> <div class="col-sm-11">
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field != 'statement' && field != 'overview' && field != 'depend' && field != 'gain' && field != 'coefficient'"> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field != 'statement' && field != 'issue' && field != 'issuekind' && field != 'overview' && field != 'depend' && field != 'gain' && field != 'coefficient'">
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'gain'" integer> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'gain'" integer>
<input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'coefficient'" float> <input type="text" class="form-control form-control-sm" id="{{ field }}" ng-model="exercice[field]" ng-if="field == 'coefficient'" float>
<textarea class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-if="field == 'statement' || field == 'overview'"></textarea> <textarea class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-if="field == 'statement' || field == 'overview' || field == 'issue'"></textarea>
<select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'depend'"> <select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="ex.id as ex.title group by ex.path.split('/')[0] for ex in exercices" ng-if="field == 'depend'">
<option value="">Aucune</option> <option value="">Aucune</option>
</select> </select>
<select class="form-control form-control-sm" id="{{field}}" ng-model="exercice[field]" ng-options="v for v in ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark']" ng-if="field == 'issuekind'"></select>
</div> </div>
</div> </div>
<div class="text-right" ng-show="exercice.id"> <div class="text-right" ng-show="exercice.id">

View File

@ -21,6 +21,7 @@
<div class="jumbotron text-indent" style="margin-top: 15px" class="well well-lg" ng-if="(my.exercices[current_exercice])"> <div class="jumbotron text-indent" style="margin-top: 15px" class="well well-lg" ng-if="(my.exercices[current_exercice])">
<h3 class="display-4">{{ themes[current_theme].exercices[current_exercice].title }}</h3> <h3 class="display-4">{{ themes[current_theme].exercices[current_exercice].title }}</h3>
<p class="lead text-justify" ng-bind-html="my.exercices[current_exercice].statement"></p> <p class="lead text-justify" ng-bind-html="my.exercices[current_exercice].statement"></p>
<div class="alert alert-{{my.exercices[current_exercice].issuekind}}" ng-if="my.exercices[current_exercice].issue" ng-bind-html="my.exercices[current_exercice].issue"></div>
<hr class="my-3"> <hr class="my-3">
<ul> <ul>
<li><strong>Gain&nbsp;:</strong> <ng-pluralize count="themes[current_theme].exercices[current_exercice].gain" when="{'one': '{} point', 'other': '{} points'}"></ng-pluralize> <em ng-if="settings.firstBlood && themes[current_theme].exercices[current_exercice].solved < 1">{{ 1 + settings.firstBlood | coeff }} prem's</em> <em ng-if="themes[current_theme].exercices[current_exercice].curcoeff != 1.0">{{ themes[current_theme].exercices[current_exercice].curcoeff | coeff }} bonus</em></li> <li><strong>Gain&nbsp;:</strong> <ng-pluralize count="themes[current_theme].exercices[current_exercice].gain" when="{'one': '{} point', 'other': '{} points'}"></ng-pluralize> <em ng-if="settings.firstBlood && themes[current_theme].exercices[current_exercice].solved < 1">{{ 1 + settings.firstBlood | coeff }} prem's</em> <em ng-if="themes[current_theme].exercices[current_exercice].curcoeff != 1.0">{{ themes[current_theme].exercices[current_exercice].curcoeff | coeff }} bonus</em></li>

View File

@ -119,6 +119,8 @@ CREATE TABLE IF NOT EXISTS exercices(
path VARCHAR(191) NOT NULL UNIQUE, path VARCHAR(191) NOT NULL UNIQUE,
statement TEXT NOT NULL, statement TEXT NOT NULL,
overview TEXT NOT NULL, overview TEXT NOT NULL,
issue TEXT NOT NULL,
issue_kind ENUM('primary', 'secondary', 'success', 'danger', 'warning', 'info', 'light', 'dark') NOT NULL DEFAULT 'info',
depend INTEGER, depend INTEGER,
gain INTEGER NOT NULL, gain INTEGER NOT NULL,
coefficient_cur FLOAT NOT NULL DEFAULT 1.0, coefficient_cur FLOAT NOT NULL DEFAULT 1.0,

View File

@ -26,6 +26,10 @@ type Exercice struct {
Statement string `json:"statement"` Statement string `json:"statement"`
// Overview is the challenge description shown to public // Overview is the challenge description shown to public
Overview string `json:"overview"` Overview string `json:"overview"`
// Issue is an optional text describing an issue with the exercice
Issue string `json:"issue"`
// IssueKind is the criticity level of the previous issue
IssueKind string `json:"issuekind"`
Depend *int64 `json:"depend"` Depend *int64 `json:"depend"`
// Gain is the basis amount of points player will earn if it solve the challenge // Gain is the basis amount of points player will earn if it solve the challenge
// If this value fluctuate during the challenge, players' scores will follow changes. // If this value fluctuate during the challenge, players' scores will follow changes.
@ -41,7 +45,7 @@ type Exercice struct {
// GetExercice retrieves the challenge with the given id. // GetExercice retrieves the challenge with the given id.
func GetExercice(id int64) (Exercice, error) { func GetExercice(id int64) (Exercice, error) {
var e Exercice var e Exercice
if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_exercice = ?", id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil {
return Exercice{}, err return Exercice{}, err
} }
@ -51,7 +55,7 @@ func GetExercice(id int64) (Exercice, error) {
// GetExercice retrieves the challenge with the given id. // GetExercice retrieves the challenge with the given id.
func (t Theme) GetExercice(id int) (Exercice, error) { func (t Theme) GetExercice(id int) (Exercice, error) {
var e Exercice var e Exercice
if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND id_exercice = ?", t.Id, id).Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil {
return Exercice{}, err return Exercice{}, err
} }
@ -61,7 +65,7 @@ func (t Theme) GetExercice(id int) (Exercice, error) {
// GetExerciceByTitle retrieves the challenge with the given title. // GetExerciceByTitle retrieves the challenge with the given title.
func (t Theme) GetExerciceByTitle(title string) (Exercice, error) { func (t Theme) GetExerciceByTitle(title string) (Exercice, error) {
var e Exercice var e Exercice
if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND title = ?", t.Id, title).Scan(&e.Id, &e.Title, &t.URLId, &e.Path, &e.Statement, &e.Overview, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { if err := DBQueryRow("SELECT id_exercice, title, url_id, path, statement, overview, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ? AND title = ?", t.Id, title).Scan(&e.Id, &e.Title, &t.URLId, &e.Path, &e.Statement, &e.Overview, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil {
return Exercice{}, err return Exercice{}, err
} }
@ -70,7 +74,7 @@ func (t Theme) GetExerciceByTitle(title string) (Exercice, error) {
// GetExercices returns the list of all challenges present in the database. // GetExercices returns the list of all challenges present in the database.
func GetExercices() ([]Exercice, error) { func GetExercices() ([]Exercice, error) {
if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, depend, gain, coefficient_cur, video_uri FROM exercices"); err != nil { if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices"); err != nil {
return nil, err return nil, err
} else { } else {
defer rows.Close() defer rows.Close()
@ -78,7 +82,7 @@ func GetExercices() ([]Exercice, error) {
var exos = make([]Exercice, 0) var exos = make([]Exercice, 0)
for rows.Next() { for rows.Next() {
var e Exercice var e Exercice
if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil {
return nil, err return nil, err
} }
exos = append(exos, e) exos = append(exos, e)
@ -93,7 +97,7 @@ func GetExercices() ([]Exercice, error) {
// GetExercices returns the list of all challenges in the Theme. // GetExercices returns the list of all challenges in the Theme.
func (t Theme) GetExercices() ([]Exercice, error) { func (t Theme) GetExercices() ([]Exercice, error) {
if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ?", t.Id); err != nil { if rows, err := DBQuery("SELECT id_exercice, title, url_id, path, statement, overview, issue, issue_kind, depend, gain, coefficient_cur, video_uri FROM exercices WHERE id_theme = ?", t.Id); err != nil {
return nil, err return nil, err
} else { } else {
defer rows.Close() defer rows.Close()
@ -101,7 +105,7 @@ func (t Theme) GetExercices() ([]Exercice, error) {
var exos = make([]Exercice, 0) var exos = make([]Exercice, 0)
for rows.Next() { for rows.Next() {
var e Exercice var e Exercice
if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil { if err := rows.Scan(&e.Id, &e.Title, &e.URLId, &e.Path, &e.Statement, &e.Overview, &e.Issue, &e.IssueKind, &e.Depend, &e.Gain, &e.Coefficient, &e.VideoURI); err != nil {
return nil, err return nil, err
} }
exos = append(exos, e) exos = append(exos, e)
@ -122,22 +126,22 @@ func (t Theme) AddExercice(title string, urlId string, path string, statement st
} else { } else {
dpd = depend.Id dpd = depend.Id
} }
if res, err := DBExec("INSERT INTO exercices (id_theme, title, url_id, path, statement, overview, depend, gain, video_uri) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", t.Id, title, urlId, path, statement, overview, dpd, gain, videoURI); err != nil { if res, err := DBExec("INSERT INTO exercices (id_theme, title, url_id, path, statement, overview, issue, depend, gain, video_uri) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", t.Id, title, urlId, path, statement, overview, "", dpd, gain, videoURI); err != nil {
return Exercice{}, err return Exercice{}, err
} else if eid, err := res.LastInsertId(); err != nil { } else if eid, err := res.LastInsertId(); err != nil {
return Exercice{}, err return Exercice{}, err
} else { } else {
if depend == nil { if depend == nil {
return Exercice{eid, title, urlId, path, statement, overview, nil, gain, 1.0, videoURI}, nil return Exercice{eid, title, urlId, path, statement, overview, "", "info", nil, gain, 1.0, videoURI}, nil
} else { } else {
return Exercice{eid, title, urlId, path, statement, overview, &depend.Id, gain, 1.0, videoURI}, nil return Exercice{eid, title, urlId, path, statement, overview, "", "info", &depend.Id, gain, 1.0, videoURI}, nil
} }
} }
} }
// Update applies modifications back to the database. // Update applies modifications back to the database.
func (e Exercice) Update() (int64, error) { func (e Exercice) Update() (int64, error) {
if res, err := DBExec("UPDATE exercices SET title = ?, url_id = ?, path = ?, statement = ?, overview = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ? WHERE id_exercice = ?", e.Title, e.URLId, e.Path, e.Statement, e.Overview, e.Depend, e.Gain, e.Coefficient, e.VideoURI, e.Id); err != nil { if res, err := DBExec("UPDATE exercices SET title = ?, url_id = ?, path = ?, statement = ?, overview = ?, issue = ?, issue_kind = ?, depend = ?, gain = ?, coefficient_cur = ?, video_uri = ? WHERE id_exercice = ?", e.Title, e.URLId, e.Path, e.Statement, e.Overview, e.Issue, e.IssueKind, e.Depend, e.Gain, e.Coefficient, e.VideoURI, 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

View File

@ -40,6 +40,8 @@ type myTeamExercice struct {
SolvedRank int64 `json:"solved_rank,omitempty"` SolvedRank int64 `json:"solved_rank,omitempty"`
Tries int64 `json:"tries,omitempty"` Tries int64 `json:"tries,omitempty"`
VideoURI string `json:"video_uri,omitempty"` VideoURI string `json:"video_uri,omitempty"`
Issue string `json:"issue,omitempty"`
IssueKind string `json:"issuekind,omitempty"`
} }
type myTeam struct { type myTeam struct {
Id int64 `json:"team_id"` Id int64 `json:"team_id"`
@ -77,7 +79,14 @@ func MyJSONTeam(t *Team, started bool) (interface{}, error) {
if tid, err := e.GetThemeId(); err == nil { if tid, err := e.GetThemeId(); err == nil {
exercice.ThemeId = tid exercice.ThemeId = tid
} }
exercice.Statement = e.Statement exercice.Statement = e.Statement
if len(e.Issue) > 0 {
exercice.Issue = e.Issue
exercice.IssueKind = e.IssueKind
}
if t == nil { if t == nil {
if e.Overview != "" { if e.Overview != "" {
exercice.Statement = e.Overview exercice.Statement = e.Overview