Add task analytics
This commit is contained in:
parent
67b9a56469
commit
bb8b4c0e36
|
|
@ -26,4 +26,20 @@ class Activity extends Base
|
|||
'title' => t('%s\'s activity', $project['name'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display task activities
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function task()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('activity/task', array(
|
||||
'title' => $task['title'],
|
||||
'task' => $task,
|
||||
'events' => $this->projectActivity->getTask($task['id']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,19 +88,20 @@ class Task extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Display task activities
|
||||
* Display task analytics
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function activites()
|
||||
public function analytics()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('task/activity', array(
|
||||
$this->response->html($this->taskLayout('task/analytics', array(
|
||||
'title' => $task['title'],
|
||||
'task' => $task,
|
||||
'ajax' => $this->request->isAjax(),
|
||||
'events' => $this->projectActivity->getTask($task['id']),
|
||||
'lead_time' => $this->taskAnalytic->getLeadTime($task),
|
||||
'cycle_time' => $this->taskAnalytic->getCycleTime($task),
|
||||
'column_averages' => $this->taskAnalytic->getAverageTimeByColumn($task),
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use Pimple\Container;
|
|||
*
|
||||
* @property \Helper\App $app
|
||||
* @property \Helper\Asset $asset
|
||||
* @property \Helper\Datetime $datetime
|
||||
* @property \Helper\Dt $dt
|
||||
* @property \Helper\File $file
|
||||
* @property \Helper\Form $form
|
||||
* @property \Helper\Subtask $subtask
|
||||
|
|
|
|||
|
|
@ -2,14 +2,30 @@
|
|||
|
||||
namespace Helper;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* DateTime helpers
|
||||
*
|
||||
* @package helper
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Datetime extends \Core\Base
|
||||
class Dt extends \Core\Base
|
||||
{
|
||||
/**
|
||||
* Get duration in seconds into human format
|
||||
*
|
||||
* @access public
|
||||
* @param integer $seconds
|
||||
* @return string
|
||||
*/
|
||||
public function duration($seconds)
|
||||
{
|
||||
$dtF = new DateTime("@0");
|
||||
$dtT = new DateTime("@$seconds");
|
||||
return $dtF->diff($dtT)->format('%a days, %h hours, %i minutes and %s seconds');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the age of an item in quasi human readable format.
|
||||
* It's in this format: <1h , NNh, NNd
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Analytic
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Get the time between date_creation and date_completed or now if empty
|
||||
*
|
||||
* @access public
|
||||
* @param array $task
|
||||
* @return integer
|
||||
*/
|
||||
public function getLeadTime(array $task)
|
||||
{
|
||||
return ($task['date_completed'] ?: time()) - $task['date_creation'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time between date_started and date_completed or now if empty
|
||||
*
|
||||
* @access public
|
||||
* @param array $task
|
||||
* @return integer
|
||||
*/
|
||||
public function getCycleTime(array $task)
|
||||
{
|
||||
if (empty($task['date_started'])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($task['date_completed'] ?: time()) - $task['date_started'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the average time spent in each column
|
||||
*
|
||||
* @access public
|
||||
* @param array $task
|
||||
* @return array
|
||||
*/
|
||||
public function getAverageTimeByColumn(array $task)
|
||||
{
|
||||
$result = array();
|
||||
$columns = $this->board->getColumnsList($task['project_id']);
|
||||
$averages = $this->transition->getAverageTimeSpentByTask($task['id']);
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
|
||||
$time_spent = 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;
|
||||
}
|
||||
|
||||
$result[] = array(
|
||||
'id' => $column_id,
|
||||
'title' => $column_title,
|
||||
'time_spent' => $time_spent,
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,22 @@ class Transition extends Base
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get average time spent by task for each column
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id
|
||||
* @return array
|
||||
*/
|
||||
public function getAverageTimeSpentByTask($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(self::TABLE)
|
||||
->groupBy('src_column_id')
|
||||
->eq('task_id', $task_id)
|
||||
->getAll('src_column_id', 'SUM(time_spent) AS time_spent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all transitions by task
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class ClassProvider implements ServiceProviderInterface
|
|||
'SubtaskTimeTracking',
|
||||
'Swimlane',
|
||||
'Task',
|
||||
'TaskAnalytic',
|
||||
'TaskCreation',
|
||||
'TaskDuplication',
|
||||
'TaskExport',
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@
|
|||
|
||||
<?php if ($task['is_active'] == 1): ?>
|
||||
<div class="task-board-days">
|
||||
<span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->datetime->age($task['date_creation']) ?></span>
|
||||
<span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->datetime->age($task['date_moved']) ?></span>
|
||||
<span title="<?= t('Task age in days')?>" class="task-days-age"><?= $this->dt->age($task['date_creation']) ?></span>
|
||||
<span title="<?= t('Days in this column')?>" class="task-days-incolumn"><?= $this->dt->age($task['date_moved']) ?></span>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="task-board-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></div>
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
<?php if ($subtask['is_timer_started']): ?>
|
||||
<i class="fa fa-pause"></i>
|
||||
<?= $this->url->link(t('Stop timer'), 'timer', 'subtask', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?>
|
||||
(<?= $this->datetime->age($subtask['timer_start_date']) ?>)
|
||||
(<?= $this->dt->age($subtask['timer_start_date']) ?>)
|
||||
<?php else: ?>
|
||||
<i class="fa fa-play-circle-o"></i>
|
||||
<?= $this->url->link(t('Start timer'), 'timer', 'subtask', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'])) ?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<div class="page-header">
|
||||
<h2><?= t('Analytics') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="listing">
|
||||
<ul>
|
||||
<li><?= t('Lead time: ').'<strong>'.$this->dt->duration($lead_time) ?></strong></li>
|
||||
<li><?= t('Cycle time: ').'<strong>'.$this->dt->duration($cycle_time) ?></strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3><?= t('Average time spent for each column') ?></h3>
|
||||
<table class="table-stripped">
|
||||
<tr>
|
||||
<th><?= t('Column') ?></th>
|
||||
<th><?= t('Average time spent') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($column_averages as $column): ?>
|
||||
<tr>
|
||||
<td><?= $this->e($column['title']) ?></td>
|
||||
<td><?= $this->dt->duration($column['time_spent']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -5,11 +5,14 @@
|
|||
<?= $this->url->link(t('Summary'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->url->link(t('Activity stream'), 'task', 'activites', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
<?= $this->url->link(t('Activity stream'), 'activity', 'task', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->url->link(t('Transitions'), 'task', 'transitions', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->url->link(t('Analytics'), 'task', 'analytics', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</li>
|
||||
<?php if ($task['time_estimated'] > 0 || $task['time_spent'] > 0): ?>
|
||||
<li>
|
||||
<?= $this->url->link(t('Time tracking'), 'task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<td><?= $this->e($transition['src_column']) ?></td>
|
||||
<td><?= $this->e($transition['dst_column']) ?></td>
|
||||
<td><?= $this->url->link($this->e($transition['name'] ?: $transition['username']), 'user', 'show', array('user_id' => $transition['user_id'])) ?></td>
|
||||
<td><?= n(round($transition['time_spent'] / 3600, 2)).' '.t('hours') ?></td>
|
||||
<td><?= $this->dt->duration($transition['time_spent']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@
|
|||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@
|
|||
<?= $this->form->checkbox('all_day', t('All day'), 1) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Comment'), 'comment') ?>
|
||||
<?= $this->form->text('comment', $values, $errors) ?>
|
||||
|
|
|
|||
|
|
@ -42,10 +42,10 @@
|
|||
<?= $this->form->checkbox('all_day', t('All day'), 1) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Comment'), 'comment') ?>
|
||||
<?= $this->form->text('comment', $values, $errors) ?>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
</tr>
|
||||
<?php foreach ($timetable as $slot): ?>
|
||||
<tr>
|
||||
<td><?= $this->datetime->getWeekDay($slot['day']) ?></td>
|
||||
<td><?= $this->dt->getWeekDay($slot['day']) ?></td>
|
||||
<td><?= $slot['start'] ?></td>
|
||||
<td><?= $slot['end'] ?></td>
|
||||
<td>
|
||||
|
|
@ -32,13 +32,13 @@
|
|||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?= $this->form->label(t('Day'), 'day') ?>
|
||||
<?= $this->form->select('day', $this->datetime->getWeekDays(), $values, $errors) ?>
|
||||
<?= $this->form->select('day', $this->dt->getWeekDays(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('Start time'), 'start') ?>
|
||||
<?= $this->form->select('start', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('start', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<?= $this->form->label(t('End time'), 'end') ?>
|
||||
<?= $this->form->select('end', $this->datetime->getDayHours(), $values, $errors) ?>
|
||||
<?= $this->form->select('end', $this->dt->getDayHours(), $values, $errors) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
require_once __DIR__.'/Base.php';
|
||||
|
||||
use Helper\Datetime;
|
||||
use Helper\Dt;
|
||||
|
||||
class DatetimeHelperTest extends Base
|
||||
{
|
||||
public function testAge()
|
||||
{
|
||||
$h = new Datetime($this->container);
|
||||
$h = new Dt($this->container);
|
||||
|
||||
$this->assertEquals('<15m', $h->age(0, 30));
|
||||
$this->assertEquals('<30m', $h->age(0, 1000));
|
||||
|
|
@ -20,7 +20,7 @@ class DatetimeHelperTest extends Base
|
|||
|
||||
public function testGetDayHours()
|
||||
{
|
||||
$h = new Datetime($this->container);
|
||||
$h = new Dt($this->container);
|
||||
|
||||
$slots = $h->getDayHours();
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ class DatetimeHelperTest extends Base
|
|||
|
||||
public function testGetWeekDays()
|
||||
{
|
||||
$h = new Datetime($this->container);
|
||||
$h = new Dt($this->container);
|
||||
|
||||
$slots = $h->getWeekDays();
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ class DatetimeHelperTest extends Base
|
|||
|
||||
public function testGetWeekDay()
|
||||
{
|
||||
$h = new Datetime($this->container);
|
||||
$h = new Dt($this->container);
|
||||
|
||||
$this->assertEquals('Monday', $h->getWeekDay(1));
|
||||
$this->assertEquals('Sunday', $h->getWeekDay(7));
|
||||
|
|
|
|||
Loading…
Reference in New Issue