diff --git a/app/Analytic/EstimatedActualColumnAnalytic.php b/app/Analytic/EstimatedActualColumnAnalytic.php new file mode 100644 index 000000000..a0bc1d12a --- /dev/null +++ b/app/Analytic/EstimatedActualColumnAnalytic.php @@ -0,0 +1,49 @@ +db->table(TaskModel::TABLE) + ->columns('SUM(time_estimated) AS hours_estimated', 'SUM(time_spent) AS hours_spent', 'column_id') + ->eq('project_id', $project_id) + ->groupBy('column_id') + ->findAll(); + + $columns = $this->columnModel->getList($project_id); + + $metrics = []; + foreach ($columns as $column_id => $column_title) { + $metrics[$column_id] = array( + 'hours_spent' => 0, + 'hours_estimated' => 0, + 'title' => $column_title, + ); + } + + foreach ($rows as $row) { + $metrics[$row['column_id']]['hours_spent'] = (float) $row['hours_spent']; + $metrics[$row['column_id']]['hours_estimated'] = (float) $row['hours_estimated']; + } + + return $metrics; + } +} diff --git a/app/Controller/AnalyticController.php b/app/Controller/AnalyticController.php index a30041b2c..27a7f25eb 100644 --- a/app/Controller/AnalyticController.php +++ b/app/Controller/AnalyticController.php @@ -130,6 +130,22 @@ class AnalyticController extends BaseController $this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart')); } + /** + * Estimated vs actual time per column + * + * @access public + */ + public function estimatedVsActualByColumn() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->analytic('analytic/estimated_actual_column', array( + 'project' => $project, + 'metrics' => $this->estimatedActualColumnAnalytic->build($project['id']), + 'title' => t('Estimated vs actual time per column'), + ))); + } + /** * Common method for CFD and Burdown chart * diff --git a/app/Core/Base.php b/app/Core/Base.php index 3535a339d..55bcc0e77 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -15,6 +15,7 @@ use Pimple\Container; * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic + * @property \Kanboard\Analytic\EstimatedActualColumnAnalytic $estimatedActualColumnAnalytic * @property \Kanboard\Core\Action\ActionManager $actionManager * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager * @property \Kanboard\Core\ExternalTask\ExternalTaskManager $externalTaskManager diff --git a/app/Helper/DateHelper.php b/app/Helper/DateHelper.php index 3ccd10334..14287db52 100644 --- a/app/Helper/DateHelper.php +++ b/app/Helper/DateHelper.php @@ -77,6 +77,18 @@ class DateHelper extends Base return $dtF->diff($dtT)->format($format); } + /** + * Get duration in hours into human format + * + * @access public + * @param float $hours + * @return string + */ + public function durationHours($hours) + { + return sprintf('%0.2f %s', round($hours, 2), t('hours')); + } + /** * Get the age of an item in quasi human readable format. * It's in this format: <1h , NNh, NNd diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index b012d80b5..c1d823902 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -24,6 +24,7 @@ class ClassProvider implements ServiceProviderInterface 'EstimatedTimeComparisonAnalytic', 'AverageLeadCycleTimeAnalytic', 'AverageTimeSpentColumnAnalytic', + 'EstimatedActualColumnAnalytic', ), 'Model' => array( 'ActionModel', diff --git a/app/Template/analytic/estimated_actual_column.php b/app/Template/analytic/estimated_actual_column.php new file mode 100644 index 000000000..c49944e41 --- /dev/null +++ b/app/Template/analytic/estimated_actual_column.php @@ -0,0 +1,30 @@ + +
= t('Not enough data to show the graph.') ?>
+ + = $this->app->component('chart-project-estimated-actual-column', array( + 'metrics' => $metrics, + 'labelSpent' => t('Hours Spent'), + 'labelEstimated' => t('Hours Estimated'), + )) ?> + +| = t('Column') ?> | += t('Hours Spent') ?> | += t('Hours Estimated') ?> | +
|---|---|---|
| = $this->text->e($column['title']) ?> | += $this->dt->durationHours($column['hours_spent']) ?> | += $this->dt->durationHours($column['hours_estimated']) ?> | +