QA: add a list of team's exercices

This commit is contained in:
nemunaire 2020-11-13 13:11:58 +01:00
parent 911bcb032e
commit ea334a8a2f
5 changed files with 154 additions and 23 deletions

View File

@ -457,6 +457,17 @@ CREATE TABLE IF NOT EXISTS teams_qa_todo(
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS teams_qa_view(
id_view INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_team INTEGER NOT NULL,
id_exercice INTEGER NOT NULL,
FOREIGN KEY(id_exercice) REFERENCES exercices(id_exercice),
FOREIGN KEY(id_team) REFERENCES teams(id_team)
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err
}

View File

@ -238,3 +238,34 @@ func (t Team) NewQATodo(idExercice int64) (QATodo, error) {
return QATodo{tid, t.Id, idExercice}, nil
}
}
// QAView
func (t Team) GetQAView() (res []QATodo, err error) {
var rows *sql.Rows
if rows, err = DBQuery("SELECT id_view, id_exercice FROM teams_qa_view WHERE id_team = ?", t.Id); err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var t QATodo
if err = rows.Scan(&t.Id, &t.IdExercice); err != nil {
return
}
res = append(res, t)
}
err = rows.Err()
return
}
func (t Team) NewQAView(idExercice int64) (QATodo, error) {
if res, err := DBExec("INSERT INTO teams_qa_view (id_team, id_exercice) VALUES (?, ?)", t.Id, idExercice); err != nil {
return QATodo{}, err
} else if tid, err := res.LastInsertId(); err != nil {
return QATodo{}, err
} else {
return QATodo{tid, t.Id, idExercice}, nil
}
}

View File

@ -12,6 +12,8 @@ import (
func init() {
router.GET("/api/qa_exercices.json", apiHandler(getExerciceTested))
router.GET("/api/qa_mywork.json", apiHandler(getQAWork))
router.GET("/api/qa_myexercices.json", apiHandler(getQAView))
router.POST("/api/qa_my_exercices.json", apiHandler(addQAView))
router.GET("/api/qa_work.json", apiHandler(getQATodo))
router.POST("/api/qa_work.json", apiHandler(createQATodo))
}
@ -42,6 +44,14 @@ func getExerciceTested(u QAUser, ps httprouter.Params, body []byte) (interface{}
}
}
func getQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
if team, err := fic.GetTeam(u.TeamId); err != nil {
return nil, err
} else {
return team.GetQAView()
}
}
func getQAWork(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
if team, err := fic.GetTeam(u.TeamId); err != nil {
return nil, err
@ -74,3 +84,20 @@ func createQATodo(u QAUser, ps httprouter.Params, body []byte) (interface{}, err
return team.NewQATodo(ut.IdExercice)
}
}
func addQAView(u QAUser, ps httprouter.Params, body []byte) (interface{}, error) {
if u.User != "nemunaire" {
return nil, errors.New("Restricted")
}
var ut fic.QATodo
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
if team, err := fic.GetTeam(ut.IdTeam); err != nil {
return nil, err
} else {
return team.NewQAView(ut.IdExercice)
}
}

View File

@ -96,6 +96,9 @@ angular.module("FICApp")
.factory("TodoWorked", function($resource) {
return $resource("api/qa_mywork.json")
})
.factory("MyExercices", function($resource) {
return $resource("api/qa_myexercices.json")
})
.factory("ExercicesTested", function($resource) {
return $resource("api/qa_exercices.json")
})
@ -240,6 +243,14 @@ angular.module("FICApp")
};
})
.controller("MyExercicesController", function($scope, MyExercices, $location) {
$scope.my_exercices = MyExercices.query();
$scope.show = function(id) {
$location.url("/exercices/" + id);
};
})
.controller("ThemesListController", function($scope, Theme, $location, $rootScope, $http) {
$scope.themes = Theme.query();
$scope.fields = ["name", "authors", "headline"];
@ -322,7 +333,7 @@ angular.module("FICApp")
$scope.exercice = new ThemedExercice();
} else {
$scope.exercice = Exercice.get({ exerciceId: $routeParams.exerciceId });
}
}
$http({
url: "api/themes.json",
method: "GET"
@ -342,7 +353,27 @@ angular.module("FICApp")
})
.controller("ExerciceQAController", function($scope, $rootScope, ExerciceQA, $routeParams, $location, $http) {
$scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId });
if ($routeParams.exerciceId) {
$scope.queries = ExerciceQA.query({ exerciceId: $routeParams.exerciceId });
} else {
$scope.queries = ExerciceQA.query({ exerciceId: $scope.todo.id_exercice });
}
$scope.queriesNSolved = "N/A"
$scope.queriesNClosed = "N/A"
$scope.queries.$promise.then(function (queries) {
var nbResolved = 0;
var nbClosed = 0;
queries.forEach(function(q) {
if (q.solved) {
nbResolved++;
}
if (q.closed) {
nbClosed++;
}
})
$scope.queriesNSolved = queries.length - nbResolved
$scope.queriesNClosed = queries.length - nbClosed
})
$scope.fields = ["state", "subject", "user", "creation"];
$scope.namedFields = {
"state": "État",

View File

@ -1,25 +1,56 @@
<div class="jumbotron text-light bg-dark">
<h1 class="display-5">Interface QA du challenge</h1>
<div class="jumbotron">
<div class="row">
<div class="col" ng-controller="ToDoController">
<table class="table table-stripped">
<tr ng-repeat="todo in todos" ng-controller="MyTodoExerciceController" ng-class="{'bg-dark': !tododone[todo.id_exercice] && !exo_done[todo.id_exercice], 'bg-warning': !tododone[todo.id_exercice] && exo_done[todo.id_exercice] == 'access', 'bg-info': !tododone[todo.id_exercice] && (exo_done[todo.id_exercice] == 'tried' || exo_done[todo.id_exercice] == 'solved'), 'bg-success': tododone[todo.id_exercice]}" ng-click="show(todo.id_exercice)">
<td ng-if="!tododone[todo.id_exercice] && (!exo_done[todo.id_exercice] || exo_done[todo.id_exercice] == 'access')">
À tester
</td>
<td ng-if="!tododone[todo.id_exercice] && exo_done[todo.id_exercice] && exo_done[todo.id_exercice] != 'access'">
À commenter
</td>
<td ng-if="tododone[todo.id_exercice]">
Commenté <span ng-if="!exo_done[todo.id_exercice] || exo_done[todo.id_exercice] != 'solved'">mais pas testé</span>
</td>
<td>
{{ mytheme.name }}
</td>
<td>
{{ myexercice.title }}
</td>
</tr>
<div class="col-6" ng-controller="ToDoController">
<h3>Challenges à valider</h3>
<table class="table table-stripped table-hover">
<thead>
<tr>
<th>Avancement</th>
<th>Scénario</th>
<th>Défi</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in todos" ng-controller="MyTodoExerciceController" ng-class="{'text-light': !tododone[todo.id_exercice] && !exo_done[todo.id_exercice], 'table-dark': !tododone[todo.id_exercice] && !exo_done[todo.id_exercice], 'table-warning': !tododone[todo.id_exercice] && exo_done[todo.id_exercice] == 'access', 'table-info': !tododone[todo.id_exercice] && (exo_done[todo.id_exercice] == 'tried' || exo_done[todo.id_exercice] == 'solved'), 'table-success': tododone[todo.id_exercice]}" ng-click="show(todo.id_exercice)">
<td ng-if="!tododone[todo.id_exercice] && (!exo_done[todo.id_exercice] || exo_done[todo.id_exercice] == 'access')">
À tester
</td>
<td ng-if="!tododone[todo.id_exercice] && exo_done[todo.id_exercice] && exo_done[todo.id_exercice] != 'access'">
À commenter
</td>
<td ng-if="tododone[todo.id_exercice]">
Commenté <span ng-if="!exo_done[todo.id_exercice] || exo_done[todo.id_exercice] != 'solved'">mais pas testé</span>
</td>
<td>
{{ mytheme.name }}
</td>
<td>
{{ myexercice.title }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-6" ng-controller="MyExercicesController">
<h3>Vos challenges</h3>
<table class="table table-stripped table-hover">
<thead>
<tr>
<th>Défi</th>
<th>Requêtes</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in my_exercices" ng-controller="ExerciceQAController" ng-class="{'table-success': queries.length > 0, 'table-warning': queriesNSolved > 0}" ng-click="show(todo.id_exercice)">
<td ng-controller="MyTodoExerciceController">
<span ng-if="mytheme.name">{{ mytheme.name }} &ndash;</span>
{{ myexercice.title }}
</td>
<td>
{{ queriesNSolved }} / {{ queriesNClosed }}
</td>
</tr>
</tbody>
</table>
</div>
</div>