* Refactor program::refreshStats method.
This commit is contained in:
+9
-115
@@ -549,7 +549,7 @@ class programModel extends model
|
||||
/* Merge project team. */
|
||||
$project->teamCount = 0;
|
||||
$project->teamMembers = array();
|
||||
$project->leftTasks = isset($data['leftTasks'][$project->id]) ? $data['leftTasks'][$project->id]->tasks : '—';
|
||||
$project->leftTasks = isset($leftTasks[$project->id]) ? $leftTasks[$project->id]->tasks : '—';
|
||||
if(!empty($teams))
|
||||
{
|
||||
$project->teamCount = isset($teams[$project->id]) ? count($teams[$project->id]) : 0;
|
||||
@@ -1397,13 +1397,14 @@ class programModel extends model
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新项目集的统计数据。
|
||||
* Refresh stats fields(estimate,consumed,left,progress) of program, project, execution.
|
||||
*
|
||||
* @param bool $refreshAll
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function refreshStats($refreshAll = false)
|
||||
public function refreshStats($refreshAll = false): void
|
||||
{
|
||||
$updateTime = zget($this->app->config->global, 'projectStatsTime', '');
|
||||
$now = helper::now();
|
||||
@@ -1426,124 +1427,17 @@ class programModel extends model
|
||||
if(empty($projects)) return;
|
||||
}
|
||||
|
||||
/* 1. Get summary and members of executions to be refreshed. */
|
||||
$tasks = $this->dao->select('t1.id, execution, t1.estimate, t1.consumed, t1.`left`, t1.status')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->ge(0) // Ignore parent task.
|
||||
->beginIF(!empty($projects))->andWhere('t1.project')->in(array_keys($projects))->fi()
|
||||
->fetchAll('id');
|
||||
/* 1. Refresh stats to db. */
|
||||
$this->programTao->updateStats(array_keys($projects));
|
||||
|
||||
$summary = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(empty($task->execution)) continue;
|
||||
if(!isset($summary[$task->execution]))
|
||||
{
|
||||
$summary[$task->execution] = new stdclass();
|
||||
$summary[$task->execution]->totalEstimate = 0;
|
||||
$summary[$task->execution]->totalConsumed = 0;
|
||||
$summary[$task->execution]->totalLeft = 0;
|
||||
}
|
||||
/* 2. Update programStatsTime. */
|
||||
$this->programTao->updateProcess();
|
||||
|
||||
$summary[$task->execution]->execution = $task->execution;
|
||||
$summary[$task->execution]->totalEstimate += $task->estimate;
|
||||
$summary[$task->execution]->totalConsumed += $task->consumed;
|
||||
$summary[$task->execution]->totalLeft += ($task->status == 'closed' or $task->status == 'cancel') ? 0 : $task->left;
|
||||
}
|
||||
|
||||
$teamMembers = $this->dao->select('t1.root, COUNT(1) AS members')->from(TABLE_TEAM)->alias('t1')
|
||||
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
|
||||
->where('t1.type')->eq('project')
|
||||
->beginIF(!empty($projects))->andWhere('t1.root')->in(array_keys($projects))->fi()
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->groupBy('t1.root')
|
||||
->fetchPairs('root');
|
||||
|
||||
$projectsPairs = $this->dao->select('id,deleted')->from(TABLE_PROJECT)->fetchPairs();
|
||||
|
||||
/* 2. Get all parents to be refreshed. */
|
||||
$executions = array();
|
||||
foreach($summary as $execution) $executions[$execution->execution] = $execution->execution;
|
||||
$paths = $this->dao->select('id,path')->from(TABLE_PROJECT)->where('id')->in($executions)->fetchAll();
|
||||
$executionPaths = array();
|
||||
foreach($paths as $path) $executionPaths[$path->id] = explode(',', trim($path->path, ','));
|
||||
|
||||
/* 3. Compute stats of execution and parents. */
|
||||
$stats = array();
|
||||
foreach($summary as $execution)
|
||||
{
|
||||
$executionID = $execution->execution;
|
||||
foreach($executionPaths[$executionID] as $nodeID)
|
||||
{
|
||||
if(!isset($stats[$nodeID])) $stats[$nodeID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalLeftNotDel' => 0, 'totalConsumedNotDel' => 0);
|
||||
$stats[$nodeID]['totalEstimate'] += $execution->totalEstimate;
|
||||
$stats[$nodeID]['totalConsumed'] += $execution->totalConsumed;
|
||||
$stats[$nodeID]['totalLeft'] += $execution->totalLeft;
|
||||
|
||||
// Check $execution->execution and $nodeID(path) is not deleted.
|
||||
if(empty($projectsPairs[$execution->execution]) && empty($projectsPairs[$nodeID]))
|
||||
{
|
||||
$stats[$nodeID]['totalConsumedNotDel'] += $execution->totalConsumed;
|
||||
$stats[$nodeID]['totalLeftNotDel'] += $execution->totalLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($teamMembers as $projectID => $teamCount)
|
||||
{
|
||||
if(!isset($stats[$projectID])) $stats[$projectID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalConsumedNotDel' => 0, 'totalLeftNotDel' => 0);
|
||||
$stats[$projectID]['teamCount'] = $teamCount;
|
||||
}
|
||||
|
||||
/* 4. Refresh stats to db. */
|
||||
foreach($stats as $projectID => $project)
|
||||
{
|
||||
$totalRealNotDel = $project['totalConsumedNotDel'] + $project['totalLeftNotDel'];
|
||||
$progress = $totalRealNotDel ? floor($project['totalConsumedNotDel'] / $totalRealNotDel * 1000) / 1000 * 100 : 0;
|
||||
$this->dao->update(TABLE_PROJECT)
|
||||
->set('progress')->eq($progress)
|
||||
->set('teamCount')->eq($project['teamCount'])
|
||||
->set('estimate')->eq($project['totalEstimate'])
|
||||
->set('consumed')->eq($project['totalConsumedNotDel'])
|
||||
->set('left')->eq($project['totalLeftNotDel'])
|
||||
->where('id')->eq($projectID)
|
||||
->exec();
|
||||
}
|
||||
|
||||
/* 5. Update programStatsTime. */
|
||||
$projectList = $this->dao->select('id,progress,path,consumed,`left`')->from(TABLE_PROJECT)
|
||||
->where('type')->eq('project')
|
||||
->andWhere('parent')->ne(0)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchAll('id');
|
||||
$programProgress = array();
|
||||
foreach($projectList as $projectID => $project)
|
||||
{
|
||||
$path = explode(',', trim($project->path, ','));
|
||||
|
||||
foreach($path as $programID)
|
||||
{
|
||||
if($programID == $projectID) continue;
|
||||
|
||||
if(!isset($programProgress[$programID])) $programProgress[$programID] = array('consumed' => 0, 'left' => 0);
|
||||
$programProgress[$programID]['consumed'] += $project->consumed;
|
||||
$programProgress[$programID]['left'] += $project->left;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($programProgress as $programID => $hours)
|
||||
{
|
||||
$progress = ($hours['consumed'] + $hours['left']) ? floor($hours['consumed'] / ($hours['consumed'] + $hours['left']) * 1000) / 1000 * 100 : 0;
|
||||
|
||||
$this->dao->update(TABLE_PROJECT)->set('progress')->eq($progress)->where('id')->eq($programID)->exec();
|
||||
}
|
||||
|
||||
/* 6. Update projectStatsTime in config. */
|
||||
/* 3. Update projectStatsTime in config. */
|
||||
$this->loadModel('setting')->setItem('system.common.global.projectStatsTime', $now);
|
||||
$this->app->config->global->projectStatsTime = $now;
|
||||
|
||||
/* 7. Clear actions older than 30 days. */
|
||||
/* 4. Clear actions older than 30 days. */
|
||||
$this->loadModel('action')->cleanActions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,151 @@ class programTao extends programModel
|
||||
->fetchPairs();
|
||||
if($shadowProducts) $this->dao->update(TABLE_PRODUCT)->set('program')->eq($newTopProgram)->where('id')->in($shadowProducts)->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新项目集的统计数据。
|
||||
* Update stats of program.
|
||||
*
|
||||
* @param array $projectIdList
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateStats(array $projectIdList): bool
|
||||
{
|
||||
/* Get summary and members of executions to be refreshed. */
|
||||
$stats = $this->getTaskStats($projectIdList);
|
||||
$teamMembers = $this->dao->select('t1.root, COUNT(1) AS members')->from(TABLE_TEAM)->alias('t1')
|
||||
->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
|
||||
->where('t1.type')->eq('project')
|
||||
->beginIF(!empty($projectIdList))->andWhere('t1.root')->in($projectIdList)->fi()
|
||||
->andWhere('t2.deleted')->eq(0)
|
||||
->groupBy('t1.root')
|
||||
->fetchPairs('root');
|
||||
|
||||
foreach($teamMembers as $projectID => $teamCount)
|
||||
{
|
||||
if(!isset($stats[$projectID])) $stats[$projectID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalConsumedNotDel' => 0, 'totalLeftNotDel' => 0);
|
||||
$stats[$projectID]['teamCount'] = $teamCount;
|
||||
}
|
||||
|
||||
foreach($stats as $projectID => $project)
|
||||
{
|
||||
$totalRealNotDel = $project['totalConsumedNotDel'] + $project['totalLeftNotDel'];
|
||||
$progress = $totalRealNotDel ? floor($project['totalConsumedNotDel'] / $totalRealNotDel * 1000) / 1000 * 100 : 0;
|
||||
$this->dao->update(TABLE_PROJECT)
|
||||
->set('progress')->eq($progress)
|
||||
->set('teamCount')->eq($project['teamCount'])
|
||||
->set('estimate')->eq($project['totalEstimate'])
|
||||
->set('consumed')->eq($project['totalConsumedNotDel'])
|
||||
->set('left')->eq($project['totalLeftNotDel'])
|
||||
->where('id')->eq($projectID)
|
||||
->exec();
|
||||
}
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务的统计数据。
|
||||
* Get task stats.
|
||||
*
|
||||
* @param array $projectIdList
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
protected function getTaskStats(array $projectIdList): array
|
||||
{
|
||||
/* 1. Get summary of executions to be refreshed. */
|
||||
$tasks = $this->dao->select('t1.id, execution, t1.estimate, t1.consumed, t1.`left`, t1.status')->from(TABLE_TASK)->alias('t1')
|
||||
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t1.parent')->ge(0) // Ignore parent task.
|
||||
->beginIF(!empty($projectIdList))->andWhere('t1.project')->in($projectIdList)->fi()
|
||||
->fetchAll('id');
|
||||
|
||||
$summary = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
if(empty($task->execution)) continue;
|
||||
if(!isset($summary[$task->execution]))
|
||||
{
|
||||
$summary[$task->execution] = new stdclass();
|
||||
$summary[$task->execution]->totalEstimate = 0;
|
||||
$summary[$task->execution]->totalConsumed = 0;
|
||||
$summary[$task->execution]->totalLeft = 0;
|
||||
}
|
||||
$summary[$task->execution]->execution = $task->execution;
|
||||
$summary[$task->execution]->totalEstimate += $task->estimate;
|
||||
$summary[$task->execution]->totalConsumed += $task->consumed;
|
||||
$summary[$task->execution]->totalLeft += ($task->status == 'closed' or $task->status == 'cancel') ? 0 : $task->left;
|
||||
}
|
||||
|
||||
/* 2. Get all parents to be refreshed. */
|
||||
$executions = array();
|
||||
foreach($summary as $execution) $executions[$execution->execution] = $execution->execution;
|
||||
$paths = $this->dao->select('id,path')->from(TABLE_PROJECT)->where('id')->in($executions)->fetchAll();
|
||||
$executionPaths = array();
|
||||
foreach($paths as $path) $executionPaths[$path->id] = explode(',', trim($path->path, ','));
|
||||
|
||||
/* 3. Compute stats of execution and parents. */
|
||||
$stats = array();
|
||||
$projectsPairs = $this->dao->select('id,deleted')->from(TABLE_PROJECT)->fetchPairs();
|
||||
foreach($summary as $execution)
|
||||
{
|
||||
$executionID = $execution->execution;
|
||||
foreach($executionPaths[$executionID] as $nodeID)
|
||||
{
|
||||
if(!isset($stats[$nodeID])) $stats[$nodeID] = array('totalEstimate' => 0, 'totalConsumed' => 0, 'totalLeft' => 0, 'teamCount' => 0, 'totalLeftNotDel' => 0, 'totalConsumedNotDel' => 0);
|
||||
$stats[$nodeID]['totalEstimate'] += $execution->totalEstimate;
|
||||
$stats[$nodeID]['totalConsumed'] += $execution->totalConsumed;
|
||||
$stats[$nodeID]['totalLeft'] += $execution->totalLeft;
|
||||
|
||||
/* Check $execution->execution and $nodeID(path) is not deleted. */
|
||||
if(empty($projectsPairs[$execution->execution]) && empty($projectsPairs[$nodeID]))
|
||||
{
|
||||
$stats[$nodeID]['totalConsumedNotDel'] += $execution->totalConsumed;
|
||||
$stats[$nodeID]['totalLeftNotDel'] += $execution->totalLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新项目集的进度。
|
||||
* Update program progress.
|
||||
*
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
protected function updateProcess(): bool
|
||||
{
|
||||
$projectList = $this->dao->select('id,progress,path,consumed,`left`')->from(TABLE_PROJECT)
|
||||
->where('type')->eq('project')
|
||||
->andWhere('parent')->ne(0)
|
||||
->andWhere('deleted')->eq(0)
|
||||
->fetchAll('id');
|
||||
|
||||
$programProgress = array();
|
||||
foreach($projectList as $projectID => $project)
|
||||
{
|
||||
$path = explode(',', trim($project->path, ','));
|
||||
foreach($path as $programID)
|
||||
{
|
||||
if($programID == $projectID) continue;
|
||||
if(!isset($programProgress[$programID])) $programProgress[$programID] = array('consumed' => 0, 'left' => 0);
|
||||
|
||||
$programProgress[$programID]['consumed'] += $project->consumed;
|
||||
$programProgress[$programID]['left'] += $project->left;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($programProgress as $programID => $hours)
|
||||
{
|
||||
$progress = ($hours['consumed'] + $hours['left']) ? floor($hours['consumed'] / ($hours['consumed'] + $hours['left']) * 1000) / 1000 * 100 : 0;
|
||||
$this->dao->update(TABLE_PROJECT)->set('progress')->eq($progress)->where('id')->eq($programID)->exec();
|
||||
}
|
||||
|
||||
return !dao::isError();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user