Add new analytic page: Average time spent into each column

This commit is contained in:
Frederic Guillot 2015-07-05 21:22:31 -04:00
parent bb8b4c0e36
commit 663a1c20e6
11 changed files with 254 additions and 46 deletions

View File

@ -3,7 +3,7 @@
namespace Controller;
/**
* Project Anaytic controller
* Project Analytic controller
*
* @package controller
* @author Frederic Guillot
@ -26,6 +26,22 @@ class Analytic extends Base
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
*

View File

@ -101,7 +101,7 @@ class Task extends Base
'task' => $task,
'lead_time' => $this->taskAnalytic->getLeadTime($task),
'cycle_time' => $this->taskAnalytic->getCycleTime($task),
'column_averages' => $this->taskAnalytic->getAverageTimeByColumn($task),
'time_spent_columns' => $this->taskAnalytic->getTimeSpentByColumn($task),
)));
}

View File

@ -21,6 +21,10 @@ class Dt extends \Core\Base
*/
public function duration($seconds)
{
if ($seconds == 0) {
return 0;
}
$dtF = new DateTime("@0");
$dtT = new DateTime("@$seconds");
return $dtF->diff($dtT)->format('%a days, %h hours, %i minutes and %s seconds');

View File

@ -49,7 +49,7 @@ class ProjectAnalytic extends Base
* Get users repartition
*
* @access public
* @param integer $project_id Project id
* @param integer $project_id
* @return array
*/
public function getUserRepartition($project_id)
@ -87,4 +87,59 @@ class ProjectAnalytic extends Base
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;
}
}

View File

@ -45,21 +45,18 @@ class TaskAnalytic extends Base
* @param array $task
* @return array
*/
public function getAverageTimeByColumn(array $task)
public function getTimeSpentByColumn(array $task)
{
$result = array();
$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) {
$time_spent = 0;
$time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0;
if (empty($averages) && $task['column_id'] == $column_id) {
$time_spent = time() - $task['date_creation'];
}
else {
$time_spent = isset($averages[$column_id]) ? $averages[$column_id] : 0;
if ($task['column_id'] == $column_id) {
$time_spent += ($task['date_completed'] ?: time()) - $task['date_moved'];
}
$result[] = array(

View File

@ -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
* @param integer $task_id
* @return array
*/
public function getAverageTimeSpentByTask($task_id)
public function getTimeSpentByTask($task_id)
{
return $this->db
->hashtable(self::TABLE)

View 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 ?>

View File

@ -13,5 +13,8 @@
<li>
<?= $this->url->link(t('Burndown chart'), 'analytic', 'burndown', array('project_id' => $project['id'])) ?>
</li>
<li>
<?= $this->url->link(t('Average time into each column'), 'analytic', 'averageTimeByColumn', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>

View File

@ -9,13 +9,14 @@
</ul>
</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">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Average time spent') ?></th>
<th><?= t('Time spent') ?></th>
</tr>
<?php foreach ($column_averages as $column): ?>
<?php foreach ($time_spent_columns as $column): ?>
<tr>
<td><?= $this->e($column['title']) ?></td>
<td><?= $this->dt->duration($column['time_spent']) ?></td>
@ -25,8 +26,11 @@
<div class="alert alert-info">
<ul>
<li><?= t('The lead time is the time 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('If the task is not closed the current time is used.') ?></li>
<li><?= t('The lead time is the duration between the task creation 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 instead of the completion date.') ?></li>
</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

View File

@ -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() {
if (Kanboard.Exists("analytic-task-repartition")) {
@ -182,6 +275,12 @@
else if (Kanboard.Exists("budget-chart")) {
drawBudget();
}
else if (Kanboard.Exists("analytic-avg-time-column")) {
drawAvgTimeColumn();
}
else if (Kanboard.Exists("analytic-task-time-column")) {
drawTaskTimeColumn();
}
});
})();