added working template of compare hours
This commit is contained in:
parent
78fd4d3ee9
commit
583e6bf064
2
Makefile
2
Makefile
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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 ?>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in New Issue