Add burndown chart
This commit is contained in:
@@ -75,6 +75,7 @@ Documentation
|
||||
- [Swimlanes](docs/swimlanes.markdown)
|
||||
- [Calendar](docs/calendar.markdown)
|
||||
- [Budget](docs/budget.markdown)
|
||||
- [Analytics](docs/analytics.markdown)
|
||||
|
||||
#### Working with tasks
|
||||
|
||||
|
||||
@@ -125,4 +125,46 @@ class Analytic extends Base
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show burndown chart
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function burndown()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
$from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
|
||||
$to = $this->request->getStringParam('to', date('Y-m-d'));
|
||||
|
||||
if (! empty($values)) {
|
||||
$from = $values['from'];
|
||||
$to = $values['to'];
|
||||
}
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->json(array(
|
||||
'metrics' => $this->projectDailySummary->getRawMetricsByDay($project['id'], $from, $to),
|
||||
'labels' => array(
|
||||
'day' => t('Date'),
|
||||
'score' => t('Complexity'),
|
||||
)
|
||||
));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->layout('analytic/burndown', array(
|
||||
'values' => array(
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
),
|
||||
'display_graph' => $this->projectDailySummary->countDays($project['id'], $from, $to) >= 2,
|
||||
'project' => $project,
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => t('Burndown chart for "%s"', $project['name']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -852,4 +852,7 @@ return array(
|
||||
'uploaded by: %s' => 'Télécharger par : %s',
|
||||
'uploaded on: %s' => 'Télécharger le : %s',
|
||||
'size: %s' => 'Taille : %s',
|
||||
'Burndown chart for "%s"' => 'Graphique d\'avancement pour « %s »',
|
||||
'Burndown chart' => 'Graphique d\'avancement',
|
||||
'This chart show the task complexity over the time (Work Remaining).' => 'Ce graphique représente la complexité des tâches en fonction du temps (travail restant).',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -850,4 +850,7 @@ return array(
|
||||
// 'uploaded by: %s' => '',
|
||||
// 'uploaded on: %s' => '',
|
||||
// 'size: %s' => '',
|
||||
// 'Burndown chart for "%s"' => '',
|
||||
// 'Burndown chart' => '',
|
||||
// 'This chart show the task complexity over the time (Work Remaining).' => '',
|
||||
);
|
||||
|
||||
@@ -83,6 +83,8 @@ class ProjectAnalytic extends Base
|
||||
$metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2);
|
||||
}
|
||||
|
||||
ksort($metrics);
|
||||
|
||||
return array_values($metrics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ class ProjectDailySummary extends Base
|
||||
/**
|
||||
* Update daily totals for the project
|
||||
*
|
||||
* "total" is the number open of tasks in the column
|
||||
* "score" is the sum of tasks score in the column
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $date Record date (YYYY-MM-DD)
|
||||
@@ -40,6 +43,7 @@ class ProjectDailySummary extends Base
|
||||
'project_id' => $project_id,
|
||||
'column_id' => $column_id,
|
||||
'total' => 0,
|
||||
'score' => 0,
|
||||
));
|
||||
|
||||
$db->table(ProjectDailySummary::TABLE)
|
||||
@@ -47,6 +51,11 @@ class ProjectDailySummary extends Base
|
||||
->eq('column_id', $column_id)
|
||||
->eq('day', $date)
|
||||
->update(array(
|
||||
'score' => $db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
->eq('is_active', Task::STATUS_OPEN)
|
||||
->sum('score'),
|
||||
'total' => $db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
@@ -92,12 +101,39 @@ class ProjectDailySummary extends Base
|
||||
ProjectDailySummary::TABLE.'.column_id',
|
||||
ProjectDailySummary::TABLE.'.day',
|
||||
ProjectDailySummary::TABLE.'.total',
|
||||
ProjectDailySummary::TABLE.'.score',
|
||||
Board::TABLE.'.title AS column_title'
|
||||
)
|
||||
->join(Board::TABLE, 'id', 'column_id')
|
||||
->eq(ProjectDailySummary::TABLE.'.project_id', $project_id)
|
||||
->gte('day', $from)
|
||||
->lte('day', $to)
|
||||
->asc(ProjectDailySummary::TABLE.'.day')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw metrics for the project within a data range grouped by day
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $from Start date (ISO format YYYY-MM-DD)
|
||||
* @param string $to End date
|
||||
* @return array
|
||||
*/
|
||||
public function getRawMetricsByDay($project_id, $from, $to)
|
||||
{
|
||||
return $this->db->table(ProjectDailySummary::TABLE)
|
||||
->columns(
|
||||
ProjectDailySummary::TABLE.'.day',
|
||||
'SUM('.ProjectDailySummary::TABLE.'.total) AS total',
|
||||
'SUM('.ProjectDailySummary::TABLE.'.score) AS score'
|
||||
)
|
||||
->eq(ProjectDailySummary::TABLE.'.project_id', $project_id)
|
||||
->gte('day', $from)
|
||||
->lte('day', $to)
|
||||
->asc(ProjectDailySummary::TABLE.'.day')
|
||||
->groupBy(ProjectDailySummary::TABLE.'.day')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,12 @@ use PDO;
|
||||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 63;
|
||||
const VERSION = 64;
|
||||
|
||||
function version_64($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INT NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_63($pdo)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,12 @@ use PDO;
|
||||
use Core\Security;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 44;
|
||||
const VERSION = 45;
|
||||
|
||||
function version_45($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_44($pdo)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,12 @@ use Core\Security;
|
||||
use PDO;
|
||||
use Model\Link;
|
||||
|
||||
const VERSION = 62;
|
||||
const VERSION = 63;
|
||||
|
||||
function version_63($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
|
||||
function version_62($pdo)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ class ProjectDailySummarySubscriber extends Base implements EventSubscriberInter
|
||||
{
|
||||
return array(
|
||||
Task::EVENT_CREATE => array('execute', 0),
|
||||
Task::EVENT_UPDATE => array('execute', 0),
|
||||
Task::EVENT_CLOSE => array('execute', 0),
|
||||
Task::EVENT_OPEN => array('execute', 0),
|
||||
Task::EVENT_MOVE_COLUMN => array('execute', 0),
|
||||
|
||||
34
app/Template/analytic/burndown.php
Normal file
34
app/Template/analytic/burndown.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<div class="page-header">
|
||||
<h2><?= t('Burndown chart') ?></h2>
|
||||
</div>
|
||||
|
||||
<?php if (! $display_graph): ?>
|
||||
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
|
||||
<?php else: ?>
|
||||
<section id="analytic-burndown">
|
||||
<div id="chart" data-url="<?= $this->u('analytic', 'burndown', array('project_id' => $project['id'], 'from' => $values['from'], 'to' => $values['to'])) ?>"></div>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<hr/>
|
||||
|
||||
<form method="post" class="form-inline" action="<?= $this->u('analytic', 'burndown', array('project_id' => $project['id'])) ?>" autocomplete="off">
|
||||
|
||||
<?= $this->formCsrf() ?>
|
||||
|
||||
<div class="form-inline-group">
|
||||
<?= $this->formLabel(t('Start Date'), 'from') ?>
|
||||
<?= $this->formText('from', $values, array(), array('required', 'placeholder="'.$this->inList($date_format, $date_formats).'"'), 'form-date') ?>
|
||||
</div>
|
||||
|
||||
<div class="form-inline-group">
|
||||
<?= $this->formLabel(t('End Date'), 'to') ?>
|
||||
<?= $this->formText('to', $values, array(), array('required', 'placeholder="'.$this->inList($date_format, $date_formats).'"'), 'form-date') ?>
|
||||
</div>
|
||||
|
||||
<div class="form-inline-group">
|
||||
<input type="submit" value="<?= t('Execute') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="alert alert-info"><?= t('This chart show the task complexity over the time (Work Remaining).') ?></p>
|
||||
@@ -10,5 +10,8 @@
|
||||
<li>
|
||||
<?= $this->a(t('Cumulative flow diagram'), 'analytic', 'cfd', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= $this->a(t('Burndown chart'), 'analytic', 'burndown', array('project_id' => $project['id'])) ?>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -160,7 +160,8 @@ Kanboard.Calendar=function(){function a(a){var b=$("#calendar").data("save-url")
|
||||
a.fullCalendar("addEventSource",b);a.fullCalendar("rerenderEvents")})}function b(a){var b=$("#calendar"),c=b.data("check-url"),d={start:b.fullCalendar("getView").start.format(),end:b.fullCalendar("getView").end.format()};jQuery.extend(d,a);for(var e in d)c+="&"+e+"="+d[e];$.getJSON(c,function(a){b.fullCalendar("removeEvents");b.fullCalendar("addEventSource",a);b.fullCalendar("rerenderEvents")})}function d(){var a=Kanboard.GetStorageItem(f);if(""!==a){var a=JSON.parse(a),c;for(c in a)$("select[name="+
|
||||
c+"]").val(a[c])}b(a||{});$(".calendar-filter").change(e)}function e(){var a={};$(".calendar-filter").each(function(){a[$(this).attr("name")]=$(this).val()});Kanboard.SetStorageItem(f,JSON.stringify(a));b(a)}var f="";jQuery(document).ready(function(){Kanboard.Exists("calendar")?(f="calendar_filters_"+$("#calendar").data("project-id"),$("#calendar").fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},
|
||||
viewRender:d,eventDrop:a}),d()):Kanboard.Exists("user-calendar")&&$("#user-calendar").fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,height:Kanboard.Exists("dashboard-calendar")?500:"auto",defaultView:"agendaWeek",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},viewRender:c,eventDrop:a})})}();
|
||||
Kanboard.Analytic=function(){jQuery(document).ready(function(){Kanboard.Exists("analytic-task-repartition")?Kanboard.Analytic.TaskRepartition.Init():Kanboard.Exists("analytic-user-repartition")?Kanboard.Analytic.UserRepartition.Init():Kanboard.Exists("analytic-cfd")&&Kanboard.Analytic.CFD.Init()});return{}}();
|
||||
Kanboard.Analytic=function(){jQuery(document).ready(function(){Kanboard.Exists("analytic-task-repartition")?Kanboard.Analytic.TaskRepartition.Init():Kanboard.Exists("analytic-user-repartition")?Kanboard.Analytic.UserRepartition.Init():Kanboard.Exists("analytic-cfd")?Kanboard.Analytic.CFD.Init():Kanboard.Exists("analytic-burndown")&&Kanboard.Analytic.Burndown.Init()});return{}}();
|
||||
Kanboard.Analytic.Burndown=function(){return{Init:function(){jQuery.getJSON($("#chart").attr("data-url"),function(a){var c=a.labels,b=a.metrics;a=[];for(var d=0;d<b.length;d++){var e={},f=parseInt(b[d].score);e[c.day]=b[d].day;e[c.score]=f;a.push(e)}b=dimple.newSvg("#chart","100%",380);a=new dimple.chart(b,a);a.addCategoryAxis("x",c.day).addOrderRule("Date");a.addMeasureAxis("y",c.score);a.addSeries(null,dimple.plot.line);a.draw()})}}}();
|
||||
Kanboard.Analytic.CFD=function(){return{Init:function(){jQuery.getJSON($("#chart").attr("data-url"),function(a){var c=a.labels,b=a.columns,d=a.metrics;a=[];for(var e=0;e<d.length;e++){var f={};f[c.column]=d[e].column_title;f[c.day]=d[e].day;f[c.total]=d[e].total;a.push(f)}d=dimple.newSvg("#chart","100%",380);a=new dimple.chart(d,a);a.addCategoryAxis("x",c.day).addOrderRule("Date");a.addMeasureAxis("y",c.total);a.addSeries(c.column,dimple.plot.area).addOrderRule(b.reverse());a.addLegend(10,10,500,
|
||||
30,"left");a.draw()})}}}();Kanboard.Analytic.TaskRepartition=function(){return{Init:function(){jQuery.getJSON($("#chart").attr("data-url"),function(a){var c=a.labels,b=a.metrics;a=[];for(var d=0;d<b.length;d++){var e={};e[c.nb_tasks]=b[d].nb_tasks;e[c.column_title]=b[d].column_title;a.push(e)}b=dimple.newSvg("#chart","100%",350);a=new dimple.chart(b,a);a.addMeasureAxis("p",c.nb_tasks);a.addSeries(c.column_title,dimple.plot.pie).innerRadius="50%";a.addLegend(0,0,100,"100%","left");a.draw()})}}}();
|
||||
Kanboard.Analytic.UserRepartition=function(){return{Init:function(){jQuery.getJSON($("#chart").attr("data-url"),function(a){var c=a.labels,b=a.metrics;a=[];for(var d=0;d<b.length;d++){var e={};e[c.nb_tasks]=b[d].nb_tasks;e[c.user]=b[d].user;a.push(e)}b=dimple.newSvg("#chart","100%",350);a=new dimple.chart(b,a);a.addMeasureAxis("p",c.nb_tasks);a.addSeries(c.user,dimple.plot.pie).innerRadius="50%";a.addLegend(0,0,100,"100%","left");a.draw()})}}}();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Kanboard.Analytic = (function() {
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
|
||||
if (Kanboard.Exists("analytic-task-repartition")) {
|
||||
Kanboard.Analytic.TaskRepartition.Init();
|
||||
}
|
||||
@@ -12,12 +12,62 @@ Kanboard.Analytic = (function() {
|
||||
else if (Kanboard.Exists("analytic-cfd")) {
|
||||
Kanboard.Analytic.CFD.Init();
|
||||
}
|
||||
else if (Kanboard.Exists("analytic-burndown")) {
|
||||
Kanboard.Analytic.Burndown.Init();
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
|
||||
})();
|
||||
|
||||
Kanboard.Analytic.Burndown = (function() {
|
||||
|
||||
function fetchData()
|
||||
{
|
||||
jQuery.getJSON($("#chart").attr("data-url"), function(data) {
|
||||
drawGraph(data.metrics, data.labels);
|
||||
});
|
||||
}
|
||||
|
||||
function drawGraph(metrics, labels)
|
||||
{
|
||||
var series = prepareSeries(metrics, labels);
|
||||
|
||||
var svg = dimple.newSvg("#chart", "100%", 380);
|
||||
var chart = new dimple.chart(svg, series);
|
||||
|
||||
var x = chart.addCategoryAxis("x", labels['day']);
|
||||
x.addOrderRule("Date");
|
||||
|
||||
chart.addMeasureAxis("y", labels['score']);
|
||||
chart.addSeries(null, dimple.plot.line);
|
||||
|
||||
chart.draw();
|
||||
}
|
||||
|
||||
function prepareSeries(metrics, labels)
|
||||
{
|
||||
var series = [];
|
||||
|
||||
for (var i = 0; i < metrics.length; i++) {
|
||||
|
||||
var row = {};
|
||||
var score = parseInt(metrics[i]['score']);
|
||||
row[labels['day']] = metrics[i]['day'];
|
||||
row[labels['score']] = score;
|
||||
series.push(row);
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
return {
|
||||
Init: fetchData
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
Kanboard.Analytic.CFD = (function() {
|
||||
|
||||
function fetchData()
|
||||
|
||||
43
composer.lock
generated
43
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "466ee3928d7d3b0bbee15de3b4c76676",
|
||||
"hash": "01ebe465ed3a59d8350670ebd4ef8793",
|
||||
"packages": [
|
||||
{
|
||||
"name": "christian-riesen/base32",
|
||||
@@ -190,12 +190,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoDb.git",
|
||||
"reference": "cd6a571d2de5c0b30d538d7cd6603dc16b25b844"
|
||||
"reference": "35c8d2d3f70b713f66e1dc14c1d6481abe5db3ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/cd6a571d2de5c0b30d538d7cd6603dc16b25b844",
|
||||
"reference": "cd6a571d2de5c0b30d538d7cd6603dc16b25b844",
|
||||
"url": "https://api.github.com/repos/fguillot/picoDb/zipball/35c8d2d3f70b713f66e1dc14c1d6481abe5db3ac",
|
||||
"reference": "35c8d2d3f70b713f66e1dc14c1d6481abe5db3ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -219,7 +219,7 @@
|
||||
],
|
||||
"description": "Minimalist database query builder",
|
||||
"homepage": "https://github.com/fguillot/picoDb",
|
||||
"time": "2015-03-27 02:21:18"
|
||||
"time": "2015-04-12 02:46:43"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/simple-validator",
|
||||
@@ -227,12 +227,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/simpleValidator.git",
|
||||
"reference": "5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7"
|
||||
"reference": "41655dc7b9224395f5bb3b5623f6e428fe6d64e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7",
|
||||
"reference": "5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7",
|
||||
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/41655dc7b9224395f5bb3b5623f6e428fe6d64e8",
|
||||
"reference": "41655dc7b9224395f5bb3b5623f6e428fe6d64e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -256,7 +256,7 @@
|
||||
],
|
||||
"description": "The most easy to use validator library for PHP :)",
|
||||
"homepage": "https://github.com/fguillot/simpleValidator",
|
||||
"time": "2015-02-14 21:04:14"
|
||||
"time": "2015-04-05 21:44:06"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/simpleLogger",
|
||||
@@ -495,17 +495,17 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v2.6.5",
|
||||
"version": "v2.6.6",
|
||||
"target-dir": "Symfony/Component/Console",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Console.git",
|
||||
"reference": "53f86497ccd01677e22435cfb7262599450a90d1"
|
||||
"reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Console/zipball/53f86497ccd01677e22435cfb7262599450a90d1",
|
||||
"reference": "53f86497ccd01677e22435cfb7262599450a90d1",
|
||||
"url": "https://api.github.com/repos/symfony/Console/zipball/5b91dc4ed5eb08553f57f6df04c4730a73992667",
|
||||
"reference": "5b91dc4ed5eb08553f57f6df04c4730a73992667",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -549,11 +549,11 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2015-03-13 17:37:22"
|
||||
"time": "2015-03-30 15:54:10"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v2.6.5",
|
||||
"version": "v2.6.6",
|
||||
"target-dir": "Symfony/Component/EventDispatcher",
|
||||
"source": {
|
||||
"type": "git",
|
||||
@@ -614,17 +614,17 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
"version": "v2.6.5",
|
||||
"version": "v2.6.6",
|
||||
"target-dir": "Symfony/Component/Stopwatch",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/Stopwatch.git",
|
||||
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5"
|
||||
"reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/ba4e774f71e2ce3e3f65cabac4031b9029972af5",
|
||||
"reference": "ba4e774f71e2ce3e3f65cabac4031b9029972af5",
|
||||
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/5f196e84b5640424a166d2ce9cca161ce1e9d912",
|
||||
"reference": "5f196e84b5640424a166d2ce9cca161ce1e9d912",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -660,7 +660,7 @@
|
||||
],
|
||||
"description": "Symfony Stopwatch Component",
|
||||
"homepage": "http://symfony.com",
|
||||
"time": "2015-02-24 11:52:21"
|
||||
"time": "2015-03-22 16:55:57"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
@@ -674,7 +674,8 @@
|
||||
"prefer-stable": false,
|
||||
"platform": {
|
||||
"php": ">=5.3",
|
||||
"ext-mbstring": "*"
|
||||
"ext-mbstring": "*",
|
||||
"ext-gd": "*"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
|
||||
33
docs/analytics.markdown
Normal file
33
docs/analytics.markdown
Normal file
@@ -0,0 +1,33 @@
|
||||
Analytics
|
||||
=========
|
||||
|
||||
User repartition
|
||||
----------------
|
||||
|
||||

|
||||
|
||||
This pie chart show the number of open tasks assigned per user.
|
||||
|
||||
Task distribution
|
||||
-----------------
|
||||
|
||||

|
||||
|
||||
This pie chart gives an overview of the number of open tasks per column.
|
||||
|
||||
Cumulative flow diagram
|
||||
-----------------------
|
||||
|
||||

|
||||
|
||||
This chart show the number of tasks cumulatively for each column over the time.
|
||||
|
||||
Burndown chart
|
||||
--------------
|
||||
|
||||

|
||||
|
||||
The [burn down chart](http://en.wikipedia.org/wiki/Burn_down_chart) is available for each project.
|
||||
This chart is a graphical representation of work left to do versus time.
|
||||
|
||||
Kanboard use the complexity or story point to generate this diagram.
|
||||
55
scripts/create-sample-burndown.php
Executable file
55
scripts/create-sample-burndown.php
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require __DIR__.'/../app/common.php';
|
||||
|
||||
use Model\ProjectDailySummary;
|
||||
use Model\TaskCreation;
|
||||
use Model\TaskStatus;
|
||||
|
||||
$pds = new ProjectDailySummary($container);
|
||||
$taskCreation = new TaskCreation($container);
|
||||
$taskStatus = new TaskStatus($container);
|
||||
|
||||
for ($i = 1; $i <= 15; $i++) {
|
||||
|
||||
$task = array(
|
||||
'title' => 'Task #'.$i,
|
||||
'project_id' => 1,
|
||||
'column_id' => rand(1, 4),
|
||||
'score' => rand(1, 21)
|
||||
);
|
||||
|
||||
$taskCreation->create($task);
|
||||
}
|
||||
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-7 days')));
|
||||
|
||||
$taskStatus->close(1);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-6 days')));
|
||||
|
||||
$taskStatus->close(2);
|
||||
$taskStatus->close(3);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-5 days')));
|
||||
|
||||
$taskStatus->close(4);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-4 days')));
|
||||
|
||||
$taskStatus->close(5);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-3 days')));
|
||||
|
||||
$taskStatus->close(6);
|
||||
$taskStatus->close(7);
|
||||
$taskStatus->close(8);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-2 days')));
|
||||
|
||||
$taskStatus->close(9);
|
||||
$taskStatus->close(10);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-2 days')));
|
||||
|
||||
$taskStatus->close(12);
|
||||
$taskStatus->close(13);
|
||||
$pds->updateTotals(1, date('Y-m-d', strtotime('-1 days')));
|
||||
|
||||
$taskStatus->close(1);
|
||||
$pds->updateTotals(1, date('Y-m-d'));
|
||||
Reference in New Issue
Block a user