Add new analytic page: Average time spent into each column
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
namespace Controller;
|
namespace Controller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Project Anaytic controller
|
* Project Analytic controller
|
||||||
*
|
*
|
||||||
* @package controller
|
* @package controller
|
||||||
* @author Frederic Guillot
|
* @author Frederic Guillot
|
||||||
@@ -26,6 +26,22 @@ class Analytic extends Base
|
|||||||
return $this->template->layout('analytic/layout', $params);
|
return $this->template->layout('analytic/layout', $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show average time spent by column
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
public function averageTimeByColumn()
|
||||||
|
{
|
||||||
|
$project = $this->getProject();
|
||||||
|
|
||||||
|
$this->response->html($this->layout('analytic/avg_time_columns', array(
|
||||||
|
'project' => $project,
|
||||||
|
'metrics' => $this->projectAnalytic->getAverageTimeSpentByColumn($project['id']),
|
||||||
|
'title' => t('Average time spent into each column for "%s"', $project['name']),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show tasks distribution graph
|
* Show tasks distribution graph
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class Task extends Base
|
|||||||
'task' => $task,
|
'task' => $task,
|
||||||
'lead_time' => $this->taskAnalytic->getLeadTime($task),
|
'lead_time' => $this->taskAnalytic->getLeadTime($task),
|
||||||
'cycle_time' => $this->taskAnalytic->getCycleTime($task),
|
'cycle_time' => $this->taskAnalytic->getCycleTime($task),
|
||||||
'column_averages' => $this->taskAnalytic->getAverageTimeByColumn($task),
|
'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class Dt extends \Core\Base
|
|||||||
*/
|
*/
|
||||||
public function duration($seconds)
|
public function duration($seconds)
|
||||||
{
|
{
|
||||||
|
if ($seconds == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
$dtF = new DateTime("@0");
|
$dtF = new DateTime("@0");
|
||||||
$dtT = new DateTime("@$seconds");
|
$dtT = new DateTime("@$seconds");
|
||||||
return $dtF->diff($dtT)->format('%a days, %h hours, %i minutes and %s seconds');
|
return $dtF->diff($dtT)->format('%a days, %h hours, %i minutes and %s seconds');
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class ProjectAnalytic extends Base
|
|||||||
* Get users repartition
|
* Get users repartition
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param integer $project_id Project id
|
* @param integer $project_id
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getUserRepartition($project_id)
|
public function getUserRepartition($project_id)
|
||||||
@@ -87,4 +87,59 @@ class ProjectAnalytic extends Base
|
|||||||
|
|
||||||
return array_values($metrics);
|
return array_values($metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the average time spent into each column
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param integer $project_id
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAverageTimeSpentByColumn($project_id)
|
||||||
|
{
|
||||||
|
$stats = array();
|
||||||
|
$columns = $this->board->getColumnsList($project_id);
|
||||||
|
|
||||||
|
// Get the time spent of the last move for each tasks
|
||||||
|
$tasks = $this->db
|
||||||
|
->table(Task::TABLE)
|
||||||
|
->columns('id', 'date_completed', 'date_moved', 'column_id')
|
||||||
|
->eq('project_id', $project_id)
|
||||||
|
->desc('id')
|
||||||
|
->limit(1000)
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
// Init values
|
||||||
|
foreach ($columns as $column_id => $column_title) {
|
||||||
|
$stats[$column_id] = array(
|
||||||
|
'count' => 0,
|
||||||
|
'time_spent' => 0,
|
||||||
|
'average' => 0,
|
||||||
|
'title' => $column_title,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get time spent foreach task/column and take into account the last move
|
||||||
|
foreach ($tasks as &$task) {
|
||||||
|
$sums = $this->transition->getTimeSpentByTask($task['id']);
|
||||||
|
|
||||||
|
if (! isset($sums[$task['column_id']])) {
|
||||||
|
$sums[$task['column_id']] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sums[$task['column_id']] += ($task['date_completed'] ?: time()) - $task['date_moved'];
|
||||||
|
|
||||||
|
foreach ($sums as $column_id => $time_spent) {
|
||||||
|
$stats[$column_id]['count']++;
|
||||||
|
$stats[$column_id]['time_spent'] += $time_spent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate average for each column
|
||||||
|
foreach ($columns as $column_id => $column_title) {
|
||||||
|
$stats[$column_id]['average'] = (int) ($stats[$column_id]['time_spent'] / $stats[$column_id]['count']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,21 +45,18 @@ class TaskAnalytic extends Base
|
|||||||
* @param array $task
|
* @param array $task
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAverageTimeByColumn(array $task)
|
public function getTimeSpentByColumn(array $task)
|
||||||
{
|
{
|
||||||
$result = array();
|
$result = array();
|
||||||
$columns = $this->board->getColumnsList($task['project_id']);
|
$columns = $this->board->getColumnsList($task['project_id']);
|
||||||
$averages = $this->transition->getAverageTimeSpentByTask($task['id']);
|
$sums = $this->transition->getTimeSpentByTask($task['id']);
|
||||||
|
|
||||||
foreach ($columns as $column_id => $column_title) {
|
foreach ($columns as $column_id => $column_title) {
|
||||||
|
|
||||||
$time_spent = 0;
|
$time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0;
|
||||||
|
|
||||||
if (empty($averages) && $task['column_id'] == $column_id) {
|
if ($task['column_id'] == $column_id) {
|
||||||
$time_spent = time() - $task['date_creation'];
|
$time_spent += ($task['date_completed'] ?: time()) - $task['date_moved'];
|
||||||
}
|
|
||||||
else {
|
|
||||||
$time_spent = isset($averages[$column_id]) ? $averages[$column_id] : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$result[] = array(
|
$result[] = array(
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ class Transition extends Base
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get average time spent by task for each column
|
* Get time spent by task for each column
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @param integer $task_id
|
* @param integer $task_id
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getAverageTimeSpentByTask($task_id)
|
public function getTimeSpentByTask($task_id)
|
||||||
{
|
{
|
||||||
return $this->db
|
return $this->db
|
||||||
->hashtable(self::TABLE)
|
->hashtable(self::TABLE)
|
||||||
|
|||||||
29
app/Template/analytic/avg_time_columns.php
Normal file
29
app/Template/analytic/avg_time_columns.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<h2><?= t('Average time spent into each column') ?></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (empty($metrics)): ?>
|
||||||
|
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
|
||||||
|
<?php else: ?>
|
||||||
|
<section id="analytic-avg-time-column">
|
||||||
|
|
||||||
|
<div id="chart" data-metrics='<?= json_encode($metrics) ?>' data-label="<?= t('Average time spent') ?>"></div>
|
||||||
|
|
||||||
|
<table class="table-stripped">
|
||||||
|
<tr>
|
||||||
|
<th><?= t('Column') ?></th>
|
||||||
|
<th><?= t('Average time spent') ?></th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($metrics as $column): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $this->e($column['title']) ?></td>
|
||||||
|
<td><?= $this->dt->duration($column['average']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach ?>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p class="alert alert-info">
|
||||||
|
<?= t('This chart show the average time spent into each column for the last %d tasks.', 1000) ?>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<?php endif ?>
|
||||||
@@ -13,5 +13,8 @@
|
|||||||
<li>
|
<li>
|
||||||
<?= $this->url->link(t('Burndown chart'), 'analytic', 'burndown', array('project_id' => $project['id'])) ?>
|
<?= $this->url->link(t('Burndown chart'), 'analytic', 'burndown', array('project_id' => $project['id'])) ?>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<?= $this->url->link(t('Average time into each column'), 'analytic', 'averageTimeByColumn', array('project_id' => $project['id'])) ?>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -9,13 +9,14 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3><?= t('Average time spent for each column') ?></h3>
|
<h3 id="analytic-task-time-column"><?= t('Time spent into each column') ?></h3>
|
||||||
|
<div id="chart" data-metrics='<?= json_encode($time_spent_columns) ?>' data-label="<?= t('Time spent') ?>"></div>
|
||||||
<table class="table-stripped">
|
<table class="table-stripped">
|
||||||
<tr>
|
<tr>
|
||||||
<th><?= t('Column') ?></th>
|
<th><?= t('Column') ?></th>
|
||||||
<th><?= t('Average time spent') ?></th>
|
<th><?= t('Time spent') ?></th>
|
||||||
</tr>
|
</tr>
|
||||||
<?php foreach ($column_averages as $column): ?>
|
<?php foreach ($time_spent_columns as $column): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= $this->e($column['title']) ?></td>
|
<td><?= $this->e($column['title']) ?></td>
|
||||||
<td><?= $this->dt->duration($column['time_spent']) ?></td>
|
<td><?= $this->dt->duration($column['time_spent']) ?></td>
|
||||||
@@ -25,8 +26,11 @@
|
|||||||
|
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<ul>
|
<ul>
|
||||||
<li><?= t('The lead time is the time between the task creation and the completion.') ?></li>
|
<li><?= t('The lead time is the duration between the task creation and the completion.') ?></li>
|
||||||
<li><?= t('The cycle time is the time between the start date and the completion.') ?></li>
|
<li><?= t('The cycle time is the duration between the start date and the completion.') ?></li>
|
||||||
<li><?= t('If the task is not closed the current time is used.') ?></li>
|
<li><?= t('If the task is not closed the current time is used instead of the completion date.') ?></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?= $this->asset->js('assets/js/vendor/d3.v3.min.js') ?>
|
||||||
|
<?= $this->asset->js('assets/js/vendor/c3.min.js') ?>
|
||||||
File diff suppressed because one or more lines are too long
@@ -165,6 +165,99 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw chart for average time spent into each column
|
||||||
|
function drawAvgTimeColumn()
|
||||||
|
{
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var plots = [$("#chart").data("label")];
|
||||||
|
var categories = [];
|
||||||
|
|
||||||
|
for (var column_id in metrics) {
|
||||||
|
plots.push(metrics[column_id].average);
|
||||||
|
categories.push(metrics[column_id].title);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: [plots],
|
||||||
|
type: 'bar'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
width: {
|
||||||
|
ratio: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: {
|
||||||
|
format: formatDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw chart for average time spent into each column
|
||||||
|
function drawTaskTimeColumn()
|
||||||
|
{
|
||||||
|
var metrics = $("#chart").data("metrics");
|
||||||
|
var plots = [$("#chart").data("label")];
|
||||||
|
var categories = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < metrics.length; i++) {
|
||||||
|
plots.push(metrics[i].time_spent);
|
||||||
|
categories.push(metrics[i].title);
|
||||||
|
}
|
||||||
|
|
||||||
|
c3.generate({
|
||||||
|
data: {
|
||||||
|
columns: [plots],
|
||||||
|
type: 'bar'
|
||||||
|
},
|
||||||
|
bar: {
|
||||||
|
width: {
|
||||||
|
ratio: 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
type: 'category',
|
||||||
|
categories: categories
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: {
|
||||||
|
format: formatDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDuration(d)
|
||||||
|
{
|
||||||
|
if (d >= 86400) {
|
||||||
|
return Math.round(d/86400) + "d";
|
||||||
|
}
|
||||||
|
else if (d >= 3600) {
|
||||||
|
return Math.round(d/3600) + "h";
|
||||||
|
}
|
||||||
|
else if (d >= 60) {
|
||||||
|
return Math.round(d/60) + "m";
|
||||||
|
}
|
||||||
|
|
||||||
|
return d + "s";
|
||||||
|
}
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
jQuery(document).ready(function() {
|
||||||
|
|
||||||
if (Kanboard.Exists("analytic-task-repartition")) {
|
if (Kanboard.Exists("analytic-task-repartition")) {
|
||||||
@@ -182,6 +275,12 @@
|
|||||||
else if (Kanboard.Exists("budget-chart")) {
|
else if (Kanboard.Exists("budget-chart")) {
|
||||||
drawBudget();
|
drawBudget();
|
||||||
}
|
}
|
||||||
|
else if (Kanboard.Exists("analytic-avg-time-column")) {
|
||||||
|
drawAvgTimeColumn();
|
||||||
|
}
|
||||||
|
else if (Kanboard.Exists("analytic-task-time-column")) {
|
||||||
|
drawTaskTimeColumn();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user