* Finish task #59701.

This commit is contained in:
孙广明
2022-07-05 15:03:15 +08:00
parent f1a7448b71
commit 25028997fa
13 changed files with 361 additions and 11 deletions
+58
View File
@@ -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.
*
+11
View File
@@ -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;}
+7
View File
@@ -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';
+7
View File
@@ -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';
+7
View File
@@ -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';
+7
View File
@@ -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 = '团队成员';
+132
View File
@@ -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.
*
+96
View File
@@ -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';?>