* Finish task #59701.
This commit is contained in:
@@ -1283,6 +1283,64 @@ class execution extends control
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kanban CFD.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $type
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function cfd($executionID = 0, $type = 'story')
|
||||
{
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
|
||||
$this->loadModel('kanban');
|
||||
$this->app->loadClass('date');
|
||||
|
||||
$end = helper::today() > $execution->end ? $execution->end : helper::today();
|
||||
if(helper::today() > $execution->end)
|
||||
{
|
||||
if(date($execution->end, strtotime('-14 days')) > $execution->begin)
|
||||
{
|
||||
$begin = date($execution->end, strtotime('-14 days'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$begin = $execution->begin;
|
||||
}
|
||||
}
|
||||
elseif(($execution->begin < helper::today()) and (helper::today() < $execution->end))
|
||||
{
|
||||
$begin = (date('Y-m-d', strtotime('-14 days')) < $execution->begin) ? $execution->begin : date('Y-m-d', strtotime('-14 days'));
|
||||
}
|
||||
|
||||
$dateList = date::getDateList($begin, $end, 'Y-m-d', '');
|
||||
|
||||
$this->view->title = $this->lang->execution->CFD;
|
||||
$this->view->type = $type;
|
||||
$this->view->execution = $execution;
|
||||
$this->view->executionName = $execution->name;
|
||||
$this->view->executionID = $executionID;
|
||||
$this->view->chartData = $this->execution->buildCFDData($executionID, $dateList, $type);
|
||||
$this->display();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute cfd datas.
|
||||
*
|
||||
* @param string $reload
|
||||
* @param int $executionID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function computeCFD($reload = 'no', $executionID = 0)
|
||||
{
|
||||
$this->execution->computeCFD($executionID);
|
||||
if($reload == 'yes') return print(js::reload('parent'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix burn for first date.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.main-content > h2 {font-size: 16px;}
|
||||
#cfdWrapper {max-width: 1400px; margin: 0 auto; padding: 25px 100px 10px; position: relative;}
|
||||
#cfdYUnit {position: absolute; color: #CBD0DB; top: 0; left: 90px;}
|
||||
#cfdXUnit {position: absolute; color: #CBD0DB; bottom: 20px; right: 60px;}
|
||||
#cfdLegend {position: absolute; right: 0; width: 80px; height: 60px; top: 50%; margin-top: -30px; line-height: 30px; color: #838A9D; font-size: 12px;}
|
||||
#cfdLegend > div {position: relative; padding-left: 30px;}
|
||||
#cfdLegend > div > .barline {position: absolute; width: 20px; left: 0; top: 13px; height: 3px; background: #EEEEEE;}
|
||||
#cfdLegend .line-real .bg-primary {background: #1183fb;}
|
||||
#cfdLegend .line-real .bg-delay {background: red;}
|
||||
|
||||
.pull-left .input-control {margin-right: 10px;}
|
||||
@@ -118,6 +118,7 @@ $lang->execution->relatedMember = 'Teammitglieder';
|
||||
$lang->execution->watermark = 'Exported by ZenTao';
|
||||
$lang->execution->burnXUnit = '(Date)';
|
||||
$lang->execution->burnYUnit = '(Hours)';
|
||||
$lang->execution->count = '(Count)';
|
||||
$lang->execution->waitTasks = 'Waiting Tasks';
|
||||
$lang->execution->viewByUser = 'By User';
|
||||
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
|
||||
@@ -165,6 +166,10 @@ $lang->execution->lifeTimeList['short'] = "Short-Term";
|
||||
$lang->execution->lifeTimeList['long'] = "Long-Term";
|
||||
$lang->execution->lifeTimeList['ops'] = "DevOps";
|
||||
|
||||
$lang->execution->cfdTypeList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->cfdTypeList['task'] = "Task";
|
||||
$lang->execution->cfdTypeList['bug'] = "Bug";
|
||||
|
||||
$lang->team = new stdclass();
|
||||
$lang->team->account = 'Konto';
|
||||
$lang->team->role = 'Rolle';
|
||||
@@ -221,6 +226,8 @@ $lang->execution->build = 'Builds';
|
||||
$lang->execution->testtask = 'Testaufgaben';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Aktualisieren';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Daten';
|
||||
$lang->execution->fixFirst = 'Bearbeite Mannstunden des ersten Tags';
|
||||
$lang->execution->team = 'Teammitglieder';
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->execution->relatedMember = 'Team';
|
||||
$lang->execution->watermark = 'Exported by ZenTao';
|
||||
$lang->execution->burnXUnit = '(Date)';
|
||||
$lang->execution->burnYUnit = '(Hours)';
|
||||
$lang->execution->count = '(Count)';
|
||||
$lang->execution->waitTasks = 'Waiting Tasks';
|
||||
$lang->execution->viewByUser = 'By User';
|
||||
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
|
||||
@@ -165,6 +166,10 @@ $lang->execution->lifeTimeList['short'] = "Short-Term";
|
||||
$lang->execution->lifeTimeList['long'] = "Long-Term";
|
||||
$lang->execution->lifeTimeList['ops'] = "DevOps";
|
||||
|
||||
$lang->execution->cfdTypeList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->cfdTypeList['task'] = "Task";
|
||||
$lang->execution->cfdTypeList['bug'] = "Bug";
|
||||
|
||||
$lang->team = new stdclass();
|
||||
$lang->team->account = 'User';
|
||||
$lang->team->role = 'Role';
|
||||
@@ -221,6 +226,8 @@ $lang->execution->build = 'Build List';
|
||||
$lang->execution->testtask = 'Request';
|
||||
$lang->execution->burn = 'Burndown';
|
||||
$lang->execution->computeBurn = 'Update';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = 'Burndown Data';
|
||||
$lang->execution->fixFirst = 'Edit 1st-Day Estimates';
|
||||
$lang->execution->team = 'Members';
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->execution->relatedMember = 'Equipe';
|
||||
$lang->execution->watermark = 'Exporté par ZenTao';
|
||||
$lang->execution->burnXUnit = '(Date)';
|
||||
$lang->execution->burnYUnit = '(Hours)';
|
||||
$lang->execution->count = '(Count)';
|
||||
$lang->execution->waitTasks = 'Waiting Tasks';
|
||||
$lang->execution->viewByUser = 'Par Utilisateur';
|
||||
$lang->execution->oneProduct = "Only one stage can be linked {$lang->productCommon}";
|
||||
@@ -165,6 +166,10 @@ $lang->execution->lifeTimeList['short'] = "Short-Term";
|
||||
$lang->execution->lifeTimeList['long'] = "Long-Term";
|
||||
$lang->execution->lifeTimeList['ops'] = "DevOps";
|
||||
|
||||
$lang->execution->cfdTypeList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->cfdTypeList['task'] = "Task";
|
||||
$lang->execution->cfdTypeList['bug'] = "Bug";
|
||||
|
||||
$lang->team = new stdclass();
|
||||
$lang->team->account = 'Utilisateur';
|
||||
$lang->team->role = 'Rôle';
|
||||
@@ -221,6 +226,8 @@ $lang->execution->build = 'Liste Builds';
|
||||
$lang->execution->testtask = 'Recette';
|
||||
$lang->execution->burn = ' Atterrissage';
|
||||
$lang->execution->computeBurn = 'Calculer';
|
||||
$lang->execution->CFD = 'Cumulative Flow diagrams';
|
||||
$lang->execution->computeCFD = 'Compute Cumulative Flow diagrams';
|
||||
$lang->execution->burnData = "Données d'atterrissage";
|
||||
$lang->execution->fixFirst = 'Fixer 1er-Jour Estimation';
|
||||
$lang->execution->team = 'Membres';
|
||||
|
||||
@@ -118,6 +118,7 @@ $lang->execution->relatedMember = '相关成员';
|
||||
$lang->execution->watermark = '由禅道导出';
|
||||
$lang->execution->burnXUnit = '(日期)';
|
||||
$lang->execution->burnYUnit = '(工时)';
|
||||
$lang->execution->count = '(数量)';
|
||||
$lang->execution->waitTasks = '待处理';
|
||||
$lang->execution->viewByUser = '按用户查看';
|
||||
$lang->execution->oneProduct = "阶段只能关联一个{$lang->productCommon}";
|
||||
@@ -165,6 +166,10 @@ $lang->execution->lifeTimeList['short'] = "短期";
|
||||
$lang->execution->lifeTimeList['long'] = "长期";
|
||||
$lang->execution->lifeTimeList['ops'] = "运维";
|
||||
|
||||
$lang->execution->cfdTypeList['story'] = "{$lang->SRCommon}";
|
||||
$lang->execution->cfdTypeList['task'] = "任务";
|
||||
$lang->execution->cfdTypeList['bug'] = "Bug";
|
||||
|
||||
$lang->team = new stdclass();
|
||||
$lang->team->account = '用户';
|
||||
$lang->team->role = '角色';
|
||||
@@ -221,6 +226,8 @@ $lang->execution->build = '所有版本';
|
||||
$lang->execution->testtask = '测试单';
|
||||
$lang->execution->burn = '燃尽图';
|
||||
$lang->execution->computeBurn = '更新燃尽图';
|
||||
$lang->execution->CFD = '累积流图';
|
||||
$lang->execution->computeCFD = '更新累积流图';
|
||||
$lang->execution->burnData = '燃尽图数据';
|
||||
$lang->execution->fixFirst = '修改首天工时';
|
||||
$lang->execution->team = '团队成员';
|
||||
|
||||
@@ -68,6 +68,7 @@ class executionModel extends model
|
||||
{
|
||||
$this->lang->execution->menu = new stdclass();
|
||||
$this->lang->execution->menu->kanban = array('link' => "{$this->lang->kanban->common}|execution|kanban|executionID=%s");
|
||||
$this->lang->execution->menu->CFD = array('link' => "{$this->lang->execution->CFD}|execution|cfd|executionID=%s");
|
||||
$this->lang->execution->menu->build = array('link' => "{$this->lang->build->common}|execution|build|executionID=%s");
|
||||
$this->lang->execution->dividerMenu = '';
|
||||
$this->lang->execution->accessDenied = str_replace($this->lang->executionCommon, $this->lang->execution->kanban, $this->lang->execution->accessDenied);
|
||||
@@ -2997,6 +2998,75 @@ class executionModel extends model
|
||||
return $burns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute cfd of a execution.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function computeCFD($executionID = 0)
|
||||
{
|
||||
$today = helper::today();
|
||||
$executions = $this->dao->select('id, code')->from(TABLE_EXECUTION)
|
||||
->where('type')->eq('kanban')
|
||||
->andWhere('status')->notin('done,closed,suspended')
|
||||
->beginIF($executionID)->andWhere('id')->in($executionID)->fi()
|
||||
->fetchPairs();
|
||||
if(!$executions) return array();
|
||||
|
||||
/* Update today's data of cfd. */
|
||||
$cells = $this->dao->select("t1.id, t1.kanban as execution, t1.`column`, t1.type, t1.cards, t1.lane, t2.name, t2.parent")
|
||||
->from(TABLE_KANBANCELL)->alias('t1')
|
||||
->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column = t2.id')
|
||||
->where('t1.kanban')->in(array_keys($executions))
|
||||
->andWhere('t2.deleted')->eq('0')
|
||||
->andWhere('t1.type')->in('story,bug,task')
|
||||
->orderBy('t2.id asc')
|
||||
->fetchAll('id');
|
||||
|
||||
/* Group by type/name/execution/colID. */
|
||||
$columnGroup = array();
|
||||
$parentNames = array();
|
||||
foreach($cells as $id => $column)
|
||||
{
|
||||
if($column->parent == '-1')
|
||||
{
|
||||
$parentNames[$column->column] = $column->name;
|
||||
continue;
|
||||
}
|
||||
|
||||
$column->name = isset($parentNames[$column->parent]) ? $parentNames[$column->parent] . "($column->name)" : $column->name;
|
||||
$columnGroup[$column->execution][$column->type][$column->name][$column->lane][$column->column] = $column;
|
||||
}
|
||||
|
||||
foreach($columnGroup as $executionID => $executionGroup)
|
||||
{
|
||||
foreach($executionGroup as $type => $columns)
|
||||
{
|
||||
foreach($columns as $colName => $laneGroup)
|
||||
{
|
||||
$cfd = new stdclass();
|
||||
$cfd->count = 0;
|
||||
$cfd->date = $today;
|
||||
$cfd->type = $type;
|
||||
foreach($laneGroup as $laneID => $columnGroup)
|
||||
{
|
||||
foreach($columnGroup as $colID => $columnCard)
|
||||
{
|
||||
$cards = trim($columnCard->cards, ',');
|
||||
$cfd->count += $cards ? count(explode(',', $cards)) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
$cfd->name = $colName;
|
||||
$cfd->execution = $executionID;
|
||||
$this->dao->replace(TABLE_CFD)->data($cfd)->exec();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix burn for first day.
|
||||
*
|
||||
@@ -3134,6 +3204,68 @@ class executionModel extends model
|
||||
if($todayTag > $counts) return array_slice($sets, $todayTag - $counts, $counts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CFD data.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $type
|
||||
* @param array $dateList
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function buildCFDData($executionID, $dateList, $type)
|
||||
{
|
||||
$this->loadModel('report');
|
||||
$setGroup = $this->getCFDData($executionID, $dateList, $type);
|
||||
|
||||
if(empty($setGroup)) return array();
|
||||
|
||||
$chartData['labels'] = $this->report->convertFormat($dateList, DT_DATE5);
|
||||
//$chartData['line'] = $this->report->createSingleJSON($sets, $dateList);
|
||||
|
||||
|
||||
$chartData['line'] = array();
|
||||
foreach($setGroup as $name => $sets)
|
||||
{
|
||||
$chartData['line'][$name] = $this->report->createSingleJSON($sets, $dateList);
|
||||
}
|
||||
|
||||
return $chartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CFD data to display.
|
||||
*
|
||||
* @param int $executionID
|
||||
* @param string $type
|
||||
* @param array $dateList
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function getCFDData($executionID = 0, $dateList = array(), $type = 'story')
|
||||
{
|
||||
$execution = $this->getById($executionID);
|
||||
|
||||
$setGroup = $this->dao->select("date, `count` AS value, `name`")->from(TABLE_CFD)
|
||||
->where('execution')->eq((int)$executionID)
|
||||
->andWhere('type')->eq($type)
|
||||
->andWhere('date')->in($dateList)
|
||||
->orderBy('date DESC, id asc')->fetchGroup('name', 'date');
|
||||
|
||||
$data = array();
|
||||
foreach($setGroup as $name => $sets)
|
||||
{
|
||||
foreach($sets as $date => $set)
|
||||
{
|
||||
if($date < $execution->begin) continue;
|
||||
|
||||
$data[$name][$date] = $set;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get taskes by search.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* The burn view file of execution module of ZenTaoPMS.
|
||||
*
|
||||
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
|
||||
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
|
||||
* @package execution
|
||||
* @version $Id: cfd.html.php 4164 2013-01-20 08:27:55Z wwccss $
|
||||
* @link http://www.zentao.net
|
||||
*/
|
||||
?>
|
||||
<?php include '../../common/view/header.html.php';?>
|
||||
<?php include '../../common/view/chart.html.php';?>
|
||||
<?php js::set('executionID', $executionID);?>
|
||||
<?php js::set('executionName', $execution->name);?>
|
||||
<?php js::set('watermark', $lang->execution->watermark);?>
|
||||
<?php js::set('cfdXUnit', $lang->execution->burnXUnit);?>
|
||||
<?php js::set('cfdYUnit', $lang->execution->burnYUnit);?>
|
||||
<?php js::set('type', $type);?>
|
||||
<div id='mainMenu' class='clearfix'>
|
||||
<div class='btn-toolbar pull-left'>
|
||||
<?php
|
||||
if(strpos('wait,doing', $execution->status) !== false)
|
||||
{
|
||||
common::printLink('execution', 'computeCFD', "reload=yes&executionID=$executionID", '<i class="icon icon-refresh"></i> ' . $lang->execution->computeCFD, 'hiddenwin', "title='{$lang->execution->computeCFD}' class='btn btn-primary' id='computeCFD'");
|
||||
echo '<div class="space"></div>';
|
||||
}
|
||||
?>
|
||||
<div class='input-control w-100px'>
|
||||
<?php echo html::select('type', $lang->execution->cfdTypeList, $type, "class='form-control chosen'");?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id='mainContent' class='main-content'>
|
||||
<h2 class='text-center'><?php echo $executionName . ' - ' . zget($lang->execution->cfdTypeList, $type) . $lang->execution->CFD;?></h2>
|
||||
<div id="cfdWrapper">
|
||||
<div id="cfdChart">
|
||||
<canvas id="cfdCanvas"></canvas>
|
||||
</div>
|
||||
<div id="cfdYUnit"><?php echo $lang->execution->count;?></div>
|
||||
<div id="cfdXUnit"><?php echo $lang->execution->burnXUnit;?></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var sets = [];
|
||||
var colors = ['#33B4DB', '#7ECF69', '#FFC73A', '#FF5A61', '#50C8D0', '#AF5AFF', '#4EA3FF', '#6C73FF', '#FF8C5A'];
|
||||
var i = 0;
|
||||
<?php foreach($chartData['line'] as $label => $set):?>
|
||||
sets.push({
|
||||
label: '<?php echo $label;?>',
|
||||
color: colors[i],
|
||||
pointStrokeColor: colors[i],
|
||||
pointHighlightStroke: colors[i],
|
||||
pointColor: colors[i],
|
||||
fillColor: colors[i] + '12',
|
||||
pointHighlightFill: colors[i],
|
||||
data: <?php echo $set;?>
|
||||
})
|
||||
i ++;
|
||||
<?php endforeach;?>
|
||||
|
||||
$('#type').change(function()
|
||||
{
|
||||
location.href = createLink('execution', 'cfd', 'executionID=' + executionID + '&type=' + $(this).val());
|
||||
});
|
||||
|
||||
$(function(){ initBurnChar();})
|
||||
|
||||
function initBurnChar()
|
||||
{
|
||||
var data =
|
||||
{
|
||||
labels: <?php echo json_encode($chartData['labels'])?>,
|
||||
datasets: sets
|
||||
};
|
||||
|
||||
var cfdChart = $("#cfdCanvas").lineChart(data,
|
||||
{
|
||||
pointDotStrokeWidth: 2,
|
||||
pointDotRadius: 3,
|
||||
datasetStrokeWidth: 3,
|
||||
datasetFill: true,
|
||||
datasetStroke: true,
|
||||
scaleShowBeyondLine: false,
|
||||
responsive: true,
|
||||
bezierCurve: false,
|
||||
scaleFontColor: '#838A9D',
|
||||
tooltipXPadding: 10,
|
||||
tooltipYPadding: 10,
|
||||
multiTooltipTitleTemplate: '<%= label %> <?php echo $lang->execution->count;?>',
|
||||
multiTooltipTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%><%= value %>",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<?php include '../../common/view/footer.html.php';?>
|
||||
Reference in New Issue
Block a user