diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index 68177c832..7d112e6aa 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -27,11 +27,11 @@ class Analytic extends Base } /** - * Show task distribution graph + * Show tasks distribution graph * * @access public */ - public function repartition() + public function tasks() { $project = $this->getProject(); $metrics = $this->projectAnalytic->getTaskRepartition($project['id']); @@ -46,11 +46,39 @@ class Analytic extends Base )); } else { - $this->response->html($this->layout('analytic/repartition', array( + $this->response->html($this->layout('analytic/tasks', array( 'project' => $project, 'metrics' => $metrics, 'title' => t('Task repartition for "%s"', $project['name']), ))); } } + + /** + * Show users repartition + * + * @access public + */ + public function users() + { + $project = $this->getProject(); + $metrics = $this->projectAnalytic->getUserRepartition($project['id']); + + if ($this->request->isAjax()) { + $this->response->json(array( + 'metrics' => $metrics, + 'labels' => array( + 'user' => t('User'), + 'nb_tasks' => t('Number of tasks'), + ) + )); + } + else { + $this->response->html($this->layout('analytic/users', array( + 'project' => $project, + 'metrics' => $metrics, + 'title' => t('User repartition for "%s"', $project['name']), + ))); + } + } } diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 2cb772402..6e355f51b 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index fb03b6164..1a8109c18 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index c54267ffc..e0b47923f 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 59cea9f76..200b74e7d 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index d36b0e934..e4edb4338 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -573,4 +573,6 @@ return array( 'Analytics' => 'Analytique', 'Subtask' => 'Sous-tâche', 'My subtasks' => 'Mes sous-tâches', + 'User repartition' => 'Répartition des utilisateurs', + 'User repartition for "%s"' => 'Répartition des utilisateurs pour « %s »', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index e4f9fc8b0..7ef579a74 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 0e75fc237..c1625c485 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index f02774297..0c7b4834f 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index fd1fc1cfd..e0969b6d8 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index 9c486ff4a..27f9644e0 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index a56c7db58..f5f4f080b 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index f2e51d125..7099891f5 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 4b86a7e13..2490709b1 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -573,4 +573,6 @@ return array( // 'Analytics' => '', // 'Subtask' => '', // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', ); diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 52957130b..25e988500 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -41,7 +41,7 @@ class Acl extends Base 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy', 'time'), 'category' => array('index', 'save', 'edit', 'update', 'confirm', 'remove'), 'action' => array('index', 'event', 'params', 'create', 'confirm', 'remove'), - 'analytic' => array('repartition'), + 'analytic' => array('tasks', 'users'), ); /** diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php index ccd2c4c99..8ecbf1f08 100644 --- a/app/Model/ProjectAnalytic.php +++ b/app/Model/ProjectAnalytic.php @@ -11,7 +11,7 @@ namespace Model; class ProjectAnalytic extends Base { /** - * Get task repartition + * Get tasks repartition * * @access public * @param integer $project_id Project id @@ -40,4 +40,41 @@ class ProjectAnalytic extends Base return $metrics; } + + /** + * Get users repartition + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getUserRepartition($project_id) + { + $metrics = array(); + $total = 0; + $tasks = $this->taskFinder->getAll($project_id); + $users = $this->projectPermission->getMemberList($project_id); + + foreach ($tasks as $task) { + + $user = $users[$task['owner_id']]; + $total++; + + if (! isset($metrics[$user])) { + $metrics[$user] = array( + 'nb_tasks' => 0, + 'percentage' => 0, + 'user' => $user, + ); + } + + $metrics[$user]['nb_tasks']++; + } + + foreach ($metrics as &$metric) { + $metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2); + } + + return array_values($metrics); + } } diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php index df6835ee3..dded245a6 100644 --- a/app/Template/analytic/sidebar.php +++ b/app/Template/analytic/sidebar.php @@ -2,7 +2,10 @@

\ No newline at end of file diff --git a/app/Template/analytic/repartition.php b/app/Template/analytic/tasks.php similarity index 78% rename from app/Template/analytic/repartition.php rename to app/Template/analytic/tasks.php index f20b6a0c1..7ae9c254d 100644 --- a/app/Template/analytic/repartition.php +++ b/app/Template/analytic/tasks.php @@ -1,9 +1,9 @@ -
+
-
+
diff --git a/app/Template/analytic/users.php b/app/Template/analytic/users.php new file mode 100644 index 000000000..89f00686f --- /dev/null +++ b/app/Template/analytic/users.php @@ -0,0 +1,29 @@ + +
+ +
+ +
+ + + + + + + + + + + + +
+ + + + + % +
+ +
diff --git a/app/Template/board/filters.php b/app/Template/board/filters.php index 21e51d98c..76cbca2d9 100644 --- a/app/Template/board/filters.php +++ b/app/Template/board/filters.php @@ -25,7 +25,7 @@
  • - $project['id'])) ?> + $project['id'])) ?>
  • diff --git a/assets/js/analytic.js b/assets/js/analytic.js index 32d3037ad..22fb672e2 100644 --- a/assets/js/analytic.js +++ b/assets/js/analytic.js @@ -4,15 +4,18 @@ Kanboard.Analytic = (function() { return { Init: function() { - if (Kanboard.Exists("analytic-repartition")) { - Kanboard.Analytic.Repartition.Init(); + if (Kanboard.Exists("analytic-task-repartition")) { + Kanboard.Analytic.TaskRepartition.Init(); + } + else if (Kanboard.Exists("analytic-user-repartition")) { + Kanboard.Analytic.UserRepartition.Init(); } } }; })(); -Kanboard.Analytic.Repartition = (function() { +Kanboard.Analytic.TaskRepartition = (function() { function fetchData() { @@ -56,3 +59,48 @@ Kanboard.Analytic.Repartition = (function() { }; })(); + +Kanboard.Analytic.UserRepartition = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels); + }); + } + + function drawGraph(metrics, labels) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", 700, 350); + + var chart = new dimple.chart(svg, series); + chart.addMeasureAxis("p", labels["nb_tasks"]); + var ring = chart.addSeries(labels["user"], dimple.plot.pie); + ring.innerRadius = "50%"; + chart.addLegend(0, 0, 100, 100, "left"); + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var serie = {}; + serie[labels["nb_tasks"]] = metrics[i]["nb_tasks"]; + serie[labels["user"]] = metrics[i]["user"]; + + series.push(serie); + } + + return series; + } + + return { + Init: fetchData + }; + +})(); diff --git a/assets/js/app.js b/assets/js/app.js index 55bc4039a..ed017b0be 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -273,15 +273,18 @@ Kanboard.Analytic = (function() { return { Init: function() { - if (Kanboard.Exists("analytic-repartition")) { - Kanboard.Analytic.Repartition.Init(); + if (Kanboard.Exists("analytic-task-repartition")) { + Kanboard.Analytic.TaskRepartition.Init(); + } + else if (Kanboard.Exists("analytic-user-repartition")) { + Kanboard.Analytic.UserRepartition.Init(); } } }; })(); -Kanboard.Analytic.Repartition = (function() { +Kanboard.Analytic.TaskRepartition = (function() { function fetchData() { @@ -324,6 +327,51 @@ Kanboard.Analytic.Repartition = (function() { Init: fetchData }; +})(); + +Kanboard.Analytic.UserRepartition = (function() { + + function fetchData() + { + jQuery.getJSON($("#chart").attr("data-url"), function(data) { + drawGraph(data.metrics, data.labels); + }); + } + + function drawGraph(metrics, labels) + { + var series = prepareSeries(metrics, labels); + + var svg = dimple.newSvg("#chart", 700, 350); + + var chart = new dimple.chart(svg, series); + chart.addMeasureAxis("p", labels["nb_tasks"]); + var ring = chart.addSeries(labels["user"], dimple.plot.pie); + ring.innerRadius = "50%"; + chart.addLegend(0, 0, 100, 100, "left"); + chart.draw(); + } + + function prepareSeries(metrics, labels) + { + var series = []; + + for (var i = 0; i < metrics.length; i++) { + + var serie = {}; + serie[labels["nb_tasks"]] = metrics[i]["nb_tasks"]; + serie[labels["user"]] = metrics[i]["user"]; + + series.push(serie); + } + + return series; + } + + return { + Init: fetchData + }; + })(); // Initialization $(function() {