added working template of compare hours

This commit is contained in:
Matthew Cillo 2015-12-07 22:40:55 -05:00
parent 78fd4d3ee9
commit 583e6bf064
8 changed files with 171 additions and 2 deletions

View File

@ -4,7 +4,7 @@ CSS_APP = $(addprefix assets/css/src/, $(addsuffix .css, base links title table
CSS_PRINT = $(addprefix assets/css/src/, $(addsuffix .css, print links table board task comment subtask markdown))
CSS_VENDOR = $(addprefix assets/css/vendor/, $(addsuffix .css, jquery-ui.min jquery-ui-timepicker-addon.min chosen.min fullcalendar.min font-awesome.min c3.min))
JS_APP = $(addprefix assets/js/src/, $(addsuffix .js, Popover Dropdown Tooltip Markdown Sidebar Search App Screenshot Calendar Board Swimlane Gantt Task Project TaskRepartitionChart UserRepartitionChart CumulativeFlowDiagram BurndownChart AvgTimeColumnChart TaskTimeColumnChart LeadCycleTimeChart Router))
JS_APP = $(addprefix assets/js/src/, $(addsuffix .js, Popover Dropdown Tooltip Markdown Sidebar Search App Screenshot Calendar Board Swimlane Gantt Task Project TaskRepartitionChart UserRepartitionChart CumulativeFlowDiagram BurndownChart AvgTimeColumnChart TaskTimeColumnChart LeadCycleTimeChart CompareHoursColumnChart Router))
JS_VENDOR = $(addprefix assets/js/vendor/, $(addsuffix .js, jquery-1.11.3.min jquery-ui.min jquery-ui-timepicker-addon.min jquery.ui.touch-punch.min chosen.jquery.min moment.min fullcalendar.min mousetrap.min mousetrap-global-bind.min))
JS_LANG = $(addprefix assets/js/vendor/lang/, $(addsuffix .js, cs da de es fi fr hu id it ja nl nb pl pt pt-br ru sv sr th tr zh-cn))

View File

@ -1,6 +1,7 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Model\Task as TaskModel;
/**
* Project Analytic controller
@ -166,4 +167,27 @@ class Analytic extends Base
'title' => t($title, $project['name']),
)));
}
public function compareHours()
{
$project = $this->getProject();
$params = $this->getProjectFilters('analytic', 'compareHours');
$query = $this->taskFilter->search('status:all')->filterByProject($params['project']['id'])->getQuery();
$paginator = $this->paginator
->setUrl('analytics', 'compare_hours')
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setQuery($query)
->calculate();
$stats = $this->projectAnalytic->getHoursByStatus($project['id']);
$this->response->html($this->layout('analytic/compare_hours', array(
'project' => $project,
'paginator' => $paginator,
'metrics' => $stats,
)));
}
}

View File

@ -179,4 +179,44 @@ class ProjectAnalytic extends Base
return $stats;
}
public function getHoursByStatus($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', 'time_estimated', 'time_spent', 'is_active')
->eq('project_id', $project_id)
->desc('id')
->limit(1000)
->findAll();
// Init values
$stats['closed'] = array(
'time_spent' => 0,
'time_estimated' => 0,
);
$stats['open'] = array(
'time_spent' => 0,
'time_estimated' => 0,
);
// Get time spent foreach task/column and take into account the last move
foreach ($tasks as &$task) {
if ($task['is_active']) {
$stats['open']['time_estimated'] += $task['time_estimated'];
$stats['open']['time_spent'] += $task['time_spent'];
} else {
$stats['closed']['time_estimated'] += $task['time_estimated'];
$stats['closed']['time_spent'] += $task['time_spent'];
}
}
return $stats;
}
}

View File

@ -122,6 +122,7 @@ class TaskFinder extends Base
'tasks.recurrence_parent',
'tasks.recurrence_child',
'tasks.time_estimated',
'tasks.time_spent',
User::TABLE.'.username AS assignee_username',
User::TABLE.'.name AS assignee_name',
Category::TABLE.'.name AS category_name',

View File

@ -0,0 +1,57 @@
<div class="page-header">
<h2><?= t('Compare Estimated Time vs Actual Time') ?></h2>
</div>
<div class="listing">
<ul>
<li><?= t('Estimated hours: ').'<strong>'.$this->e($metrics['open']['time_estimated']+$metrics['open']['time_estimated']) ?></strong></li>
<li><?= t('Actual hours: ').'<strong>'.$this->e($metrics['open']['time_spent']+$metrics['closed']['time_spent']) ?></strong></li>
</ul>
</div>
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
<section id="analytic-compare-hours">
<div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS)?>' data-label-spent="<?= t('Hours Spent') ?>" data-label-estimated="<?= t('Hours Estimated') ?>"></div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
<table class="table-fixed table-small">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
<th class="column-10"><?= $paginator->order(t('Estimated Time'), 'tasks.time_estimated') ?></th>
<th class="column-10"><?= $paginator->order(t('Actual Time'), 'tasks.time_spent') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
<td class="task-table color-<?= $task['color_id'] ?>">
<?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?= $this->url->link($this->e($task['title']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
<?php if ($task['is_active'] == \Kanboard\Model\Task::STATUS_OPEN): ?>
<?= t('Open') ?>
<?php else: ?>
<?= t('Closed') ?>
<?php endif ?>
</td>
<td>
<?= $this->e($task['time_estimated']) ?>
</td>
<td>
<?= $this->e($task['time_spent']) ?>
</td>
</tr>
<?php endforeach ?>
</table>
<?= $paginator ?>
<?php endif ?>
</section>
<?php endif ?>

View File

@ -19,7 +19,10 @@
<li <?= $this->app->getRouterAction() === 'leadandcycletime' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Lead and cycle time'), 'analytic', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->getRouterAction() === 'comparehours' ? 'class="active"' : '' ?>>
<?= $this->url->link(t('Compare hours'), 'analytic', 'compareHours', array('project_id' => $project['id'])) ?>
</li>
</ul>
<div class="sidebar-collapse"><a href="#" title="<?= t('Hide sidebar') ?>"><i class="fa fa-chevron-left"></i></a></div>
<div class="sidebar-expand" style="display: none"><a href="#" title="<?= t('Expand sidebar') ?>"><i class="fa fa-chevron-right"></i></a></div>
</div>
</div>

View File

@ -0,0 +1,43 @@
function CompareHoursColumnChart(app) {
this.app = app;
}
CompareHoursColumnChart.prototype.execute = function() {
var metrics = $("#chart").data("metrics");
var spent = [$("#chart").data("label-spent")];
var estimated = [$("#chart").data("label-estimated")];
var categories = [];
for (var status in metrics) {
spent.push(parseInt(metrics[status].time_spent));
estimated.push(parseInt(metrics[status].time_estimated));
categories.push(status);
}
console.log(spent);
c3.generate({
data: {
columns: [spent, estimated],
type: 'bar'
},
bar: {
width: {
ratio: 0.2
}
},
axis: {
x: {
type: 'category',
categories: categories
},
y: {
tick: {
format: this.app.formatDuration
}
}
},
legend: {
show: true
}
});
};

View File

@ -30,6 +30,7 @@ jQuery(document).ready(function() {
router.addRoute('analytic-avg-time-column', AvgTimeColumnChart);
router.addRoute('analytic-task-time-column', TaskTimeColumnChart);
router.addRoute('analytic-lead-cycle-time', LeadCycleTimeChart);
router.addRoute('analytic-compare-hours', CompareHoursColumnChart);
router.addRoute('gantt-chart', Gantt);
router.dispatch(app);
app.listen();