* finish task #6769,6768.
This commit is contained in:
@@ -122,6 +122,7 @@ $lang->menu->my = '<span>Dashboard</span>|my|index';
|
||||
$lang->menu->product = $lang->productCommon . '|product|index|locate=no';
|
||||
$lang->menu->project = $lang->projectCommon . '|project|index|locate=no';
|
||||
$lang->menu->qa = 'QA|qa|index';
|
||||
$lang->menu->repo = 'Code|repo|log';
|
||||
$lang->menu->doc = 'Doc|doc|index';
|
||||
$lang->menu->report = 'Report|report|index';
|
||||
$lang->menu->company = 'Company|company|index';
|
||||
@@ -330,6 +331,12 @@ $lang->caselib->menu->testsuite = array('link' => 'Suite|testsuite|browse|');
|
||||
$lang->caselib->menu->report = array('link' => 'Report|testreport|browse|');
|
||||
$lang->caselib->menu->caselib = array('link' => 'Case Library|caselib|browse|libID=%s', 'alias' => 'create,createcase,view,edit,batchcreatecase,showimport', 'subModule' => 'tree,testcase');
|
||||
|
||||
$lang->repo = new stdclass();
|
||||
$lang->repo->menu = new stdclass();
|
||||
$lang->repo->menu->browse = array('link' =>'Browse|repo|log|repoID=%s&entry=', 'alias' => 'diff,view,revision,showsynccomment');
|
||||
$lang->repo->menu->settings = 'Settings|repo|settings|repoID=%s';
|
||||
$lang->repo->menu->delete = array('link' => 'Delete|repo|delete|repoID=%s', 'target' => 'hiddenwin');
|
||||
|
||||
/* Doc menu settings. */
|
||||
$lang->doc = new stdclass();
|
||||
$lang->doc->menu = new stdclass();
|
||||
@@ -874,6 +881,7 @@ if(isset($config->global->flow) and $config->global->flow == 'onlyTest')
|
||||
/* Adjust menu group. */
|
||||
$lang->menugroup->bug = 'bug';
|
||||
$lang->menugroup->testcase = 'testcase';
|
||||
$lang->menugroup->case = 'testcase';
|
||||
$lang->menugroup->testtask = 'testtask';
|
||||
$lang->menugroup->testsuite = 'testsuite';
|
||||
$lang->menugroup->caselib = 'caselib';
|
||||
|
||||
@@ -94,6 +94,7 @@ $lang->report->menuOrder[5] = 'product';
|
||||
$lang->report->menuOrder[10] = 'prj';
|
||||
$lang->report->menuOrder[15] = 'test';
|
||||
$lang->report->menuOrder[20] = 'staff';
|
||||
$lang->report->menuOrder[25] = 'annual';
|
||||
|
||||
/* company menu order. */
|
||||
$lang->company->menuOrder[5] = 'browseUser';
|
||||
|
||||
@@ -101,7 +101,6 @@ $lang->loading = '稍候...';
|
||||
$lang->notFound = '抱歉,您访问的对象并不存在!';
|
||||
$lang->notPage = '抱歉,您访问的功能正在开发中!';
|
||||
$lang->showAll = '[[全部显示]]';
|
||||
$lang->selectedItems = '已选择 <strong>{0}</strong> 项';
|
||||
|
||||
$lang->future = '未来';
|
||||
$lang->year = '年';
|
||||
@@ -354,6 +353,7 @@ $lang->report->menu->product = array('link' => $lang->productCommon . '|report|p
|
||||
$lang->report->menu->prj = array('link' => $lang->projectCommon . '|report|projectdeviation');
|
||||
$lang->report->menu->test = array('link' => '测试|report|bugcreate', 'alias' => 'bugassign');
|
||||
$lang->report->menu->staff = array('link' => '组织|report|workload');
|
||||
$lang->report->menu->annual = array('link' => '年度总结|report|annualData', 'target' => '_blank');
|
||||
|
||||
$lang->report->notice = new stdclass();
|
||||
$lang->report->notice->help = '注:统计报表的数据来源于列表页面的检索结果,生成统计报表前请先在列表页面进行检索。比如列表页面我们检索的是%tab%,那么报表就是基于之前检索的%tab%的结果集进行统计。';
|
||||
|
||||
@@ -6,3 +6,21 @@ $config->report->dailyreminder->bug = true;
|
||||
$config->report->dailyreminder->task = true;
|
||||
$config->report->dailyreminder->todo = true;
|
||||
$config->report->dailyreminder->testTask = true;
|
||||
|
||||
$config->report->annualData['po']['block1'] = array('title' => 'baseInfo', 'data' => array('logins', 'involvedProducts', 'createdPlans', 'createdStories'));
|
||||
$config->report->annualData['po']['block2'] = array('title' => 'productOverview', 'data' => array());
|
||||
$config->report->annualData['po']['block3'] = array('title' => '', 'data' => array('products'));
|
||||
$config->report->annualData['po']['block4'] = array('title' => 'poData', 'data' => array('storyPri', 'storyStage'));
|
||||
$config->report->annualData['po']['block5'] = array('title' => 'poStatistics', 'data' => array('storyMonth'));
|
||||
|
||||
$config->report->annualData['dev']['block1'] = array('title' => 'baseInfo', 'data' => array('logins', 'actions', 'efforts', 'consumed'));
|
||||
$config->report->annualData['dev']['block2'] = array('title' => 'projectOverview', 'data' => array('doneProject', 'doingProject', 'suspendProject'));
|
||||
$config->report->annualData['dev']['block3'] = array('title' => '', 'data' => array('projects'));
|
||||
$config->report->annualData['dev']['block4'] = array('title' => 'devData', 'data' => array('finishedTaskPri', 'resolvedBugPri'));
|
||||
$config->report->annualData['dev']['block5'] = array('title' => 'devStatistics', 'data' => array('effortMonth'));
|
||||
|
||||
$config->report->annualData['qa']['block1'] = array('title' => 'baseInfo', 'data' => array('logins', 'actions', 'foundBugs', 'createdCases'));
|
||||
$config->report->annualData['qa']['block2'] = array('title' => 'projectOverview', 'data' => array('doneProject', 'doingProject', 'suspendProject'));
|
||||
$config->report->annualData['qa']['block3'] = array('title' => '', 'data' => array('projects'));
|
||||
$config->report->annualData['qa']['block4'] = array('title' => 'qaData', 'data' => array('bugPri', 'casePri'));
|
||||
$config->report->annualData['qa']['block5'] = array('title' => 'qaStatistics', 'data' => array('bugMonth', 'caseMonth'));
|
||||
|
||||
+101
-1
@@ -244,8 +244,108 @@ class report extends control
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function annualData()
|
||||
public function annualData($year = '')
|
||||
{
|
||||
if(empty($year)) $year = date('Y');
|
||||
$account = $this->app->user->account;
|
||||
|
||||
$role = 'po';
|
||||
if($this->app->user->role == 'dev' or $this->app->user->role == 'td' or $this->app->user->role == 'pm') $role = 'dev';
|
||||
if($this->app->user->role == 'qd' or $this->app->user->role == 'qa') $role = 'qa';
|
||||
$role = 'qa';
|
||||
|
||||
$data = array();
|
||||
$data['logins'] = $this->report->getUserYearLogins($account, $year);
|
||||
if($role == 'po')
|
||||
{
|
||||
$products = $this->report->getUserYearProducts($account, $year);
|
||||
$data['involvedProducts'] = count($products);
|
||||
|
||||
$planGroups = $this->report->getPlansByProducts($products, $year);
|
||||
$planCount = 0;
|
||||
foreach($planGroups as $plans) $planCount += $plans;
|
||||
$data['createdPlans'] = $planCount;
|
||||
$data['productStat'] = $this->report->getStatByProducts($products, $account, $year);
|
||||
|
||||
$storyInfo = $this->report->getUserYearStory($account, $year);
|
||||
$data['createdStories'] = $storyInfo['count'];
|
||||
$data['storyPri'] = $storyInfo['pri'];
|
||||
$data['storyStage'] = $storyInfo['stage'];
|
||||
$data['storyMonth'] = $storyInfo['month'];
|
||||
|
||||
$storyGroups = $this->report->getStoriesByProducts($products, $year);
|
||||
foreach($products as $productID => $product)
|
||||
{
|
||||
$product->plans = zget($planGroups, $productID, 0);
|
||||
$product->stories = zget($storyGroups, $productID, 0);
|
||||
}
|
||||
$data['products'] = $products;
|
||||
}
|
||||
elseif($role == 'dev')
|
||||
{
|
||||
$data['actions'] = $this->report->getUserYearActions($account, $year);
|
||||
|
||||
$efforts = $this->report->getUserYearEfforts($account, $year);
|
||||
$data['efforts'] = $efforts->count;
|
||||
$data['consumed'] = $efforts->consumed;
|
||||
|
||||
$projects = $this->report->getUserYearProjects($account, $year, $role);
|
||||
$projectStat = $this->report->getStatByProjects($projects);
|
||||
|
||||
$tasks = $this->report->getUserYearFinishedTasks($account, $year);
|
||||
$bugs = $this->report->getUserYearResolvedBugs($account, $year);
|
||||
$data['finishedTaskPri'] = $tasks['pri'];
|
||||
$data['resolvedBugPri'] = $bugs['pri'];
|
||||
$data['effortMonth'] = $this->report->getEffort4Month($account, $year);;
|
||||
|
||||
$stories = $this->report->getFinishedStoryByProjects($projects, $year);
|
||||
$tasks = $this->report->getFinishedTaskByProjects($projects, $account, $year);
|
||||
$bugs = $this->report->getResolvedBugByProjects($projects, $account, $year);
|
||||
foreach($projects as $projectID => $project)
|
||||
{
|
||||
$project->stories = zget($stories, $projectID, 0);
|
||||
$project->tasks = zget($tasks, $projectID, 0);
|
||||
$project->bugs = zget($bugs, $projectID, 0);
|
||||
}
|
||||
|
||||
$data['projects'] = $projects;
|
||||
$data['projectStat'] = $projectStat;
|
||||
}
|
||||
elseif($role == 'qa')
|
||||
{
|
||||
$data['actions'] = $this->report->getUserYearActions($account, $year);
|
||||
$bugInfo = $this->report->getUserYearCreatedBugs($account, $year);
|
||||
$data['foundBugs'] = $bugInfo['count'];
|
||||
$data['bugPri'] = $bugInfo['pri'];
|
||||
$data['bugMonth'] = $bugInfo['month'];
|
||||
|
||||
$caseInfo = $this->report->getUserYearCreatedCases($account, $year);
|
||||
$data['createdCases'] = $caseInfo['count'];
|
||||
$data['casePri'] = $caseInfo['pri'];
|
||||
$data['caseMonth'] = $caseInfo['month'];
|
||||
|
||||
$projects = $this->report->getUserYearProjects($account, $year, $role);
|
||||
$projectStat = $this->report->getStatByProjects($projects);
|
||||
|
||||
$bugs = $this->report->getCreatedBugByProjects($projects, $year);
|
||||
foreach($projects as $projectID => $project) $project->bugs = zget($bugs, $projectID, 0);
|
||||
|
||||
$data['projects'] = $projects;
|
||||
$data['projectStat'] = $projectStat;
|
||||
}
|
||||
|
||||
$firstAction = $this->dao->select('*')->from(TABLE_ACTION)->orderBy('id')->limit(1)->fetch();
|
||||
$firstYear = substr($firstAction->date, 0, 4);
|
||||
$currentYear = date('Y');
|
||||
|
||||
$years = array();
|
||||
for($thisYear = $firstYear; $thisYear <= $currentYear; $thisYear ++) $years[$thisYear] = $thisYear;
|
||||
|
||||
$this->view->title = sprintf($this->lang->report->annualData->title, $year, $this->app->user->realname);
|
||||
$this->view->data = $data;
|
||||
$this->view->role = $role;
|
||||
$this->view->year = $year;
|
||||
$this->view->years = $years;
|
||||
die($this->display());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ body {height: 100vh; padding: 0;}
|
||||
|
||||
#header > h1 {width: 500px; margin: 40px auto 0; height: 58px; font-weight: normal; text-align: center; font-size: 20px; display: block; line-height: 58px; position: relative; left: -30px;}
|
||||
#toolbar {position: absolute; right: 33px; top: 80px; text-align: center;}
|
||||
#toolbar #year{width:70px; display:inline-block; margin-right:5px; padding-top:2px; border-color: #1a77a5; background-color: transparent; color:#fff;}
|
||||
#toolbar #year option{background: #141414;}
|
||||
.exporting #toolbar {display: none;}
|
||||
#toolbar .btn-primary {border-color: #1a77a5; background-color: transparent; padding: 6px 7px;}
|
||||
#toolbar .btn-primary:hover,
|
||||
|
||||
@@ -194,4 +194,9 @@ $(function()
|
||||
{
|
||||
exportAnnualImage();
|
||||
});
|
||||
|
||||
$('#toolbar #year').change(function()
|
||||
{
|
||||
location.href = createLink('report', 'annualData', 'year=' + $(this).val());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,3 +100,49 @@ $lang->report->mailTitle->testTask = " 测试版本(%s),";
|
||||
|
||||
$lang->report->proVersion = '<a href="https://api.zentao.net/goto.php?item=proversion&from=reportpage" target="_blank">更多精彩,尽在专业版!</a>';
|
||||
$lang->report->proVersionEn = '<a href="http://api.zentao.pm/goto.php?item=proversion&from=reportpage" target="_blank">Try ZenTao Pro for more!</a>';
|
||||
|
||||
$lang->report->annualData = new stdclass();
|
||||
$lang->report->annualData->title = "%s年工作内容统计一览表 ——%s";
|
||||
$lang->report->annualData->baseInfo = "基本数据信息";
|
||||
$lang->report->annualData->logins = "累计登录次数";
|
||||
$lang->report->annualData->actions = "累计动态数";
|
||||
$lang->report->annualData->efforts = "累计日志数";
|
||||
$lang->report->annualData->consumed = "累计工时数";
|
||||
$lang->report->annualData->foundBugs = "累计发现Bug数";
|
||||
$lang->report->annualData->createdCases = "累计创建用例数";
|
||||
$lang->report->annualData->involvedProducts = "累计参与产品数";
|
||||
$lang->report->annualData->createdPlans = "累计创建计划数";
|
||||
$lang->report->annualData->createdStories = "累计创建需求数";
|
||||
|
||||
$lang->report->annualData->productOverview = "产品创建的需求数及占比";
|
||||
$lang->report->annualData->projectOverview = "参与项目概览";
|
||||
$lang->report->annualData->doneProject = "已完成的项目";
|
||||
$lang->report->annualData->doingProject = "正在进行的项目";
|
||||
$lang->report->annualData->suspendProject = "已挂起的项目";
|
||||
|
||||
$lang->report->annualData->projectName = '项目名称';
|
||||
$lang->report->annualData->finishedStory = '完成需求数';
|
||||
$lang->report->annualData->finishedTask = '完成任务数';
|
||||
$lang->report->annualData->foundBug = '发现Bug数';
|
||||
$lang->report->annualData->resolvedBug = '解决Bug数';
|
||||
$lang->report->annualData->productName = '产品名称';
|
||||
$lang->report->annualData->planCount = '计划数';
|
||||
$lang->report->annualData->storyCount = '需求数';
|
||||
|
||||
$lang->report->annualData->qaData = "完成任务与解决Bug数据";
|
||||
$lang->report->annualData->totalCreatedBug = '累计发现的Bug数';
|
||||
$lang->report->annualData->totalCreatedCase = '累计创建的用例数';
|
||||
|
||||
$lang->report->annualData->devData = "完成的任务数和解决的Bug数";
|
||||
$lang->report->annualData->totalFinishedTask = '完成的任务数';
|
||||
$lang->report->annualData->totalResolvedBug = '解决的Bug数';
|
||||
|
||||
$lang->report->annualData->poData = "所创建的需求数对应的优先级及状态";
|
||||
$lang->report->annualData->totalStoryPri = "创建需求优先级分布";
|
||||
$lang->report->annualData->totalStoryStage = "创建需求阶段分布";
|
||||
|
||||
$lang->report->annualData->qaStatistics = "完成任务与解决bug工时统计";
|
||||
$lang->report->annualData->poStatistics = "月创建需求数";
|
||||
$lang->report->annualData->devStatistics = "月完成任务累计工时";
|
||||
|
||||
$lang->report->annualData->unit = "个";
|
||||
|
||||
@@ -503,6 +503,294 @@ class reportModel extends model
|
||||
->andWhere("(t1.status='wait' OR t1.status='doing')")
|
||||
->fetchGroup('user');
|
||||
}
|
||||
|
||||
public function getUserYearLogins($account, $year)
|
||||
{
|
||||
return $this->dao->select('count(*) as count')->from(TABLE_ACTION)->where('actor')->eq($account)->andWhere('LEFT(date, 4)')->eq($year)->andWhere('action')->eq('login')->fetch('count');
|
||||
}
|
||||
|
||||
public function getUserYearActions($account, $year)
|
||||
{
|
||||
return $this->dao->select('count(*) as count')->from(TABLE_ACTION)->where('actor')->eq($account)->andWhere('LEFT(date, 4)')->eq($year)->fetch('count');
|
||||
}
|
||||
|
||||
public function getUserYearEfforts($account, $year)
|
||||
{
|
||||
return $this->dao->select('count(*) as count, sum(consumed) as consumed')->from(TABLE_TASKESTIMATE)->where('account')->eq($account)->andWhere('LEFT(date, 4)')->eq($year)->fetch();
|
||||
}
|
||||
|
||||
public function getUserYearStory($account, $year)
|
||||
{
|
||||
$stories = $this->dao->select('*')->from(TABLE_STORY)->where('openedBy')->eq($account)->andWhere('LEFT(openedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
|
||||
$storyInfo = array();
|
||||
$storyInfo['count'] = 0;
|
||||
$storyInfo['pri'] = array();
|
||||
$storyInfo['stage'] = array('wait' => 0, 'projected' => 0, 'developing' => 0, 'developed' => 0, 'released' => 0, 'closed' => 0);
|
||||
$storyInfo['month'] = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
foreach($stories as $story)
|
||||
{
|
||||
$storyInfo['count'] ++;
|
||||
|
||||
if(!isset($storyInfo['pri'][$story->pri])) $storyInfo['pri'][$story->pri] = 0;
|
||||
$storyInfo['pri'][$story->pri] ++;
|
||||
|
||||
if(isset($storyInfo['stage'][$story->stage])) $storyInfo['stage'][$story->stage] ++;
|
||||
|
||||
$month = (int)substr($story->openedDate, 5, 2) - 1;
|
||||
$storyInfo['month'][$month] ++;
|
||||
}
|
||||
|
||||
return $storyInfo;
|
||||
}
|
||||
|
||||
public function getUserYearCreatedBugs($account, $year)
|
||||
{
|
||||
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where('openedBy')->eq($account)->andWhere('LEFT(openedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
|
||||
$bugInfo = array();
|
||||
$bugInfo['count'] = 0;
|
||||
$bugInfo['pri'] = array();
|
||||
$bugInfo['month'] = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
foreach($bugs as $bug)
|
||||
{
|
||||
$bugInfo['count'] ++;
|
||||
|
||||
if(!isset($bugInfo['pri'][$bug->pri])) $bugInfo['pri'][$bug->pri] = 0;
|
||||
$bugInfo['pri'][$bug->pri] ++;
|
||||
|
||||
$month = (int)substr($bug->openedDate, 5, 2) - 1;
|
||||
$bugInfo['month'][$month] ++;
|
||||
}
|
||||
return $bugInfo;
|
||||
}
|
||||
|
||||
public function getUserYearResolvedBugs($account, $year)
|
||||
{
|
||||
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where('resolvedBy')->eq($account)->andWhere('LEFT(resolvedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
|
||||
$bugInfo = array();
|
||||
$bugInfo['count'] = 0;
|
||||
$bugInfo['pri'] = array();
|
||||
foreach($bugs as $bug)
|
||||
{
|
||||
$bugInfo['count'] ++;
|
||||
|
||||
if(!isset($bugInfo['pri'][$bug->pri])) $bugInfo['pri'][$bug->pri] = 0;
|
||||
$bugInfo['pri'][$bug->pri] ++;
|
||||
}
|
||||
return $bugInfo;
|
||||
}
|
||||
|
||||
public function getUserYearFinishedTasks($account, $year)
|
||||
{
|
||||
$tasks = $this->dao->select('*')->from(TABLE_TASK)->where('finishedBy')->eq($account)->andWhere('LEFT(finishedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
|
||||
$taskInfo = array();
|
||||
$taskInfo['count'] = 0;
|
||||
$taskInfo['pri'] = array();
|
||||
foreach($tasks as $task)
|
||||
{
|
||||
$taskInfo['count'] ++;
|
||||
|
||||
if(!isset($taskInfo['pri'][$task->pri])) $taskInfo['pri'][$task->pri] = 0;
|
||||
$taskInfo['pri'][$task->pri] ++;
|
||||
}
|
||||
return $taskInfo;
|
||||
}
|
||||
|
||||
public function getUserYearCreatedCases($account, $year)
|
||||
{
|
||||
$cases = $this->dao->select('*')->from(TABLE_CASE)->where('openedBy')->eq($account)->andWhere('LEFT(openedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchAll();
|
||||
|
||||
$caseInfo = array();
|
||||
$caseInfo['count'] = 0;
|
||||
$caseInfo['pri'] = array();
|
||||
$caseInfo['month'] = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
foreach($cases as $case)
|
||||
{
|
||||
$caseInfo['count'] ++;
|
||||
|
||||
if(!isset($caseInfo['pri'][$case->pri])) $caseInfo['pri'][$case->pri] = 0;
|
||||
$caseInfo['pri'][$case->pri] ++;
|
||||
|
||||
$month = (int)substr($case->openedDate, 5, 2) - 1;
|
||||
$caseInfo['month'][$month] ++;
|
||||
}
|
||||
return $caseInfo;
|
||||
}
|
||||
|
||||
public function getUserYearProducts($account, $year)
|
||||
{
|
||||
$products = $this->dao->select('id,name,status')->from(TABLE_PRODUCT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('LEFT(createdDate, 4)')->eq($year)
|
||||
->andWhere('createdBy', true)->eq($account)
|
||||
->orWhere('PO')->eq($account)
|
||||
->orWhere('QD')->eq($account)
|
||||
->orWhere('RD')->eq($account)
|
||||
->markRight(1)
|
||||
->fetchAll('id');
|
||||
|
||||
$storyProducts = $this->dao->select('DISTINCT product')->from(TABLE_STORY)->where('openedBy')->eq($account)->andWhere('LEFT(openedDate, 4)')->eq($year)->fetchPairs('product', 'product');
|
||||
$planProducts = $this->dao->select('DISTINCT t1.product')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_ACTION)->alias('t2')->on("t1.id=t2.objectID and t2.objectType='productplan'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('LEFT(t2.date, 4)')->eq($year)
|
||||
->andWhere('t2.action')->eq('opened')
|
||||
->groupBy('t1.product')
|
||||
->fetchPairs('product', 'product');
|
||||
$products += $this->dao->select('id,name,status')->from(TABLE_PRODUCT)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('id')->in($storyProducts + $planProducts)
|
||||
->fetchAll('id');
|
||||
return $products;
|
||||
}
|
||||
|
||||
public function getPlansByProducts($products, $year)
|
||||
{
|
||||
return $this->dao->select('t1.product, count(DISTINCT t1.id) as plans')->from(TABLE_PRODUCTPLAN)->alias('t1')
|
||||
->leftJoin(TABLE_ACTION)->alias('t2')->on("t1.id=t2.objectID and t2.objectType='productplan'")
|
||||
->where('t1.deleted')->eq(0)
|
||||
->andWhere('t1.product')->in(array_keys($products))
|
||||
->andWhere('LEFT(t2.date, 4)')->eq($year)
|
||||
->andWhere('t2.action')->eq('opened')
|
||||
->groupBy('t1.product')
|
||||
->fetchPairs('product', 'plans');
|
||||
}
|
||||
|
||||
public function getStoriesByProducts($products, $year)
|
||||
{
|
||||
return $this->dao->select('product, count(*) as stories')->from(TABLE_STORY)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('product')->in(array_keys($products))
|
||||
->andWhere('LEFT(openedDate, 4)')->eq($year)
|
||||
->groupBy('product')
|
||||
->fetchPairs('product', 'stories');
|
||||
}
|
||||
|
||||
public function getUserYearProjects($account, $year, $role = 'dev')
|
||||
{
|
||||
$projects = $this->dao->select('id,name,status')->from(TABLE_PROJECT)->where('deleted')->eq(0)
|
||||
->andWhere('LEFT(begin, 4)', true)->eq($year)
|
||||
->orWhere('LEFT(end, 4)')->le($year)
|
||||
->markRight(1)
|
||||
->andWhere('openedBy', true)->eq($account)
|
||||
->orWhere('PO')->eq($account)
|
||||
->orWhere('PM')->eq($account)
|
||||
->orWhere('QD')->eq($account)
|
||||
->orWhere('RD')->eq($account)
|
||||
->markRight(1)
|
||||
->fetchAll('id');
|
||||
$teamProjects = $this->dao->select('*')->from(TABLE_TEAM)->where('type')->eq('project')->andWhere('account')->eq($account)->andWhere('LEFT(`join`, 4)')->eq($year)->fetchPairs('root', 'root');
|
||||
$taskProjects = array();
|
||||
if($role == 'dev') $taskProjects = $this->dao->select('project')->from(TABLE_TASK)->where('finishedBy')->eq($account)->andWhere('LEFT(finishedDate, 4)')->eq($year)->andWhere('deleted')->eq(0)->fetchPairs('project', 'project');
|
||||
$projects += $this->dao->select('id,name,status')->from(TABLE_PROJECT)->where('deleted')->eq(0)
|
||||
->andWhere('id')->in($teamProjects + $taskProjects)
|
||||
->fetchAll('id');
|
||||
return $projects;
|
||||
}
|
||||
|
||||
public function getCreatedBugByProjects($projects, $year)
|
||||
{
|
||||
return $this->dao->select('project, count(*) as bugs')->from(TABLE_BUG)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('project')->in(array_keys($projects))
|
||||
->andWhere('LEFT(openedDate, 4)')->eq($year)
|
||||
->groupBy('project')
|
||||
->fetchPairs('project', 'bugs');
|
||||
}
|
||||
|
||||
public function getFinishedStoryByProjects($projects, $year)
|
||||
{
|
||||
return $this->dao->select('t1.project, count(DISTINCT t1.story) as stories')->from(TABLE_PROJECTSTORY)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story=t2.id')
|
||||
->where('t2.deleted')->eq(0)
|
||||
->andWhere('t1.project')->in(array_keys($projects))
|
||||
->andWhere('LEFT(t2.closedDate, 4)')->eq($year)
|
||||
->groupBy('t1.project')
|
||||
->fetchPairs('project', 'stories');
|
||||
}
|
||||
|
||||
public function getFinishedTaskByProjects($projects, $account, $year)
|
||||
{
|
||||
return $this->dao->select('project, count(*) as tasks')->from(TABLE_TASK)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('project')->in(array_keys($projects))
|
||||
->andWhere('LEFT(finishedDate, 4)')->eq($year)
|
||||
->andWhere('finishedBy')->eq($account)
|
||||
->groupBy('project')
|
||||
->fetchPairs('project', 'tasks');
|
||||
}
|
||||
|
||||
public function getResolvedBugByProjects($projects, $account, $year)
|
||||
{
|
||||
return $this->dao->select('project, count(*) as bugs')->from(TABLE_BUG)
|
||||
->where('deleted')->eq(0)
|
||||
->andWhere('project')->in(array_keys($projects))
|
||||
->andWhere('LEFT(resolvedDate, 4)')->eq($year)
|
||||
->andWhere('resolvedBy')->eq($account)
|
||||
->groupBy('project')
|
||||
->fetchPairs('project', 'bugs');
|
||||
}
|
||||
|
||||
public function getStatByProjects($projects)
|
||||
{
|
||||
$projectStat = array();
|
||||
$projectStat['count'] = 0;
|
||||
$projectStat['doing'] = 0;
|
||||
$projectStat['done'] = 0;
|
||||
$projectStat['suspended'] = 0;
|
||||
foreach($projects as $project)
|
||||
{
|
||||
$projectStat['count']++;
|
||||
if($project->status == 'closed' or $project->status == 'done') $projectStat['done']++;
|
||||
if($project->status == 'wait' or $project->status == 'doing') $projectStat['doing']++;
|
||||
if($project->status == 'suspended') $projectStat['suspended']++;
|
||||
}
|
||||
return $projectStat;
|
||||
}
|
||||
|
||||
public function getStatByProducts($products, $account, $year)
|
||||
{
|
||||
$allStories = $this->dao->select('product, count(*) as count')->from(TABLE_STORY)->where('deleted')->eq(0)
|
||||
->andWhere('product')->in(array_keys($products))
|
||||
->andWhere('LEFT(openedDate, 4)')->eq($year)
|
||||
->groupBy('product')
|
||||
->fetchPairs('product', 'count');
|
||||
$mineStories = $this->dao->select('product, count(*) as count')->from(TABLE_STORY)->where('deleted')->eq(0)
|
||||
->andWhere('product')->in(array_keys($products))
|
||||
->andWhere('openedBy')->eq($account)
|
||||
->andWhere('LEFT(openedDate, 4)')->eq($year)
|
||||
->groupBy('product')
|
||||
->fetchPairs('product', 'count');
|
||||
|
||||
$productStat = array();
|
||||
foreach($products as $productID => $product)
|
||||
{
|
||||
$productStat[$productID]['name'] = $product->name;
|
||||
$productStat[$productID]['count'] = zget($allStories, $productID, 0);
|
||||
$productStat[$productID]['mine'] = zget($mineStories, $productID, 0);
|
||||
}
|
||||
|
||||
return $productStat;
|
||||
}
|
||||
|
||||
public function getEffort4Month($account, $year)
|
||||
{
|
||||
$efforts = $this->dao->select('*')->from(TABLE_TASKESTIMATE)->where('account')->eq($account)
|
||||
->andWhere('LEFT(date, 4)')->eq($year)
|
||||
->orderBy('date')
|
||||
->fetchAll();
|
||||
|
||||
$months = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
foreach($efforts as $effort)
|
||||
{
|
||||
$month = (int)substr($effort->date, 5, 2) - 1;
|
||||
$months[$month] += $effort->consumed;
|
||||
}
|
||||
return $months;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
<canvas id='block5Chart' width='520' height='240'></canvas>
|
||||
</section>
|
||||
<div id='toolbar'>
|
||||
<?php echo html::select('year', $years, $year, "class='form-control'");?>
|
||||
<button type='button' class='btn btn-primary' id='exportBtn'><i class='icon icon-export'></i></button>
|
||||
<a id='imageDownloadBtn' class='hidden' download='annual_data.png'></a>
|
||||
</div>
|
||||
@@ -75,115 +76,161 @@
|
||||
<div id='loadIndicator' class='load-indicator'></div>
|
||||
</div>
|
||||
<script>
|
||||
// 年度数据示例
|
||||
var annualData =
|
||||
{
|
||||
// 大标题
|
||||
title: '2019年工作内容统计一览表 ── XXX',
|
||||
title: '<?php echo $title;?>',
|
||||
|
||||
// 区块1
|
||||
block1:
|
||||
<?php foreach($config->report->annualData[$role] as $blockKey => $blockConfig):?>
|
||||
<?php echo $blockKey;?>:
|
||||
{
|
||||
title: '基本数据信息',
|
||||
data:
|
||||
[
|
||||
{title: '累计登录次数', value: 344},
|
||||
{title: '累计动态数', value: 1543},
|
||||
{title: '累计日志数', value: 629},
|
||||
{title: '累计工时数', value: 772}
|
||||
]
|
||||
},
|
||||
|
||||
// 区块2
|
||||
block2:
|
||||
{
|
||||
title: '参与项目概览',
|
||||
data:
|
||||
[
|
||||
{title: '已完成的项目', value: 20, percent: '71.4%'},
|
||||
{title: '正在进行的项目', value: 7, percent: '25%'},
|
||||
{title: '已挂起的项目', value: 1, percent: '3.5%'}
|
||||
],
|
||||
unit: '个'
|
||||
},
|
||||
|
||||
// 区块3
|
||||
block3:
|
||||
{
|
||||
cols:
|
||||
[
|
||||
{title: '项目名称', width: 'auto', align: 'left'},
|
||||
{title: '完成需求数', width: 80, align: 'center'},
|
||||
{title: '完成任务数', width: 80, align: 'center'},
|
||||
{title: '解决bug数', width: 72, align: 'center'}
|
||||
],
|
||||
rows:
|
||||
[
|
||||
['项目名称一', 22, 2, '0%'],
|
||||
['项目名称二', 22, 2, '0%'],
|
||||
['项目名称三', 22, 2, '0%'],
|
||||
['项目名称四', 22, 2, '0%'],
|
||||
['项目名称五', 22, 2, '0%'],
|
||||
['项目名称六', 22, 2, '0%'],
|
||||
['项目名称七', 22, 2, '0%'],
|
||||
['项目名称八', 22, 2, '0%'],
|
||||
['项目名称九', 22, 2, '0%'],
|
||||
['项目名称十', 22, 2, '0%'],
|
||||
['项目名称十一', 22, 2, '0%'],
|
||||
['项目名称十二', 22, 2, '0%']
|
||||
]
|
||||
},
|
||||
|
||||
// 区块4
|
||||
block4:
|
||||
{
|
||||
title: '完成任务与解决bug数据',
|
||||
chart1:
|
||||
<?php if(!empty($blockConfig['title'])):?>
|
||||
title: '<?php echo $lang->report->annualData->{$blockConfig['title']};?>',
|
||||
<?php endif;?>
|
||||
<?php
|
||||
$blockData = array();
|
||||
if($blockKey == 'block1')
|
||||
{
|
||||
title: '累计完成任务数',
|
||||
unit: '个',
|
||||
data:
|
||||
[
|
||||
// legend 属性可选,如果留空,则不再下方的图例上显示标题
|
||||
{value: 42, title: '优先级1', legend: 'P1'},
|
||||
{value: 172, title: '优先级2', legend: 'P2'},
|
||||
{value: 212, title: '优先级3', legend: 'P3'},
|
||||
{value: 86, title: '优先级4', legend: 'P4'}
|
||||
],
|
||||
},
|
||||
chart2:
|
||||
{
|
||||
title: '累计修复bug数',
|
||||
unit: '个',
|
||||
data:
|
||||
[
|
||||
{value: 42, title: '优先级1'},
|
||||
{value: 32, title: '优先级2'},
|
||||
{value: 20, title: '优先级3'},
|
||||
{value: 34, title: '优先级4'}
|
||||
],
|
||||
foreach($blockConfig['data'] as $name) $blockData[] = array('title' => $lang->report->annualData->{$name}, 'value' => $data[$name]);
|
||||
echo "data :" . json_encode($blockData);
|
||||
}
|
||||
},
|
||||
|
||||
block5:
|
||||
{
|
||||
title: '完成任务与解决bug工时统计',
|
||||
labels: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
||||
datasets:
|
||||
[
|
||||
elseif($blockKey == 'block2')
|
||||
{
|
||||
echo "unit: '" . $lang->report->annualData->unit . "',";
|
||||
if($role == 'qa' or $role == 'dev')
|
||||
{
|
||||
label: '完成任务累计工时',
|
||||
data: [110, 154, 184, 220, 212, 250, 130, 146, 201, 89, 140, 59],
|
||||
},
|
||||
{
|
||||
label: '解决bug累计工时',
|
||||
data: [10, 5, 11, 20, 30, 14, 3, 20, 50, 27, 13, 3],
|
||||
$count = $data['projectStat']['count'];
|
||||
$blockData[] = array('title' => $lang->report->annualData->doneProject, 'value' => $data['projectStat']['done'], 'percent' => (empty($count) ? 0 : round($data['projectStat']['done'] / $count, 4) * 100 . '%'));
|
||||
$blockData[] = array('title' => $lang->report->annualData->doingProject, 'value' => $data['projectStat']['doing'], 'percent' => (empty($count) ? 0 : round($data['projectStat']['doing'] / $count, 4) * 100 . '%'));
|
||||
$blockData[] = array('title' => $lang->report->annualData->suspendProject, 'value' => $data['projectStat']['suspended'], 'percent' => (empty($count) ? 0 : round($data['projectStat']['suspended'] / $count, 4) * 100 . '%'));
|
||||
echo "data: " . json_encode($blockData);
|
||||
}
|
||||
]
|
||||
elseif($role == 'po')
|
||||
{
|
||||
foreach($data['productStat'] as $productStat)
|
||||
{
|
||||
$count = $productStat['count'];
|
||||
$blockData[] = array('title' => $productStat['name'], 'value' => (int)$productStat['mine'], 'percent' => (empty($count) ? '0' : round($productStat['mine'] / $count, 3) * 100 . '%'));
|
||||
}
|
||||
echo "data: " . json_encode($blockData);
|
||||
}
|
||||
}
|
||||
elseif($blockKey == 'block3')
|
||||
{
|
||||
$cols = array();
|
||||
$rows = array();
|
||||
if($role == 'po')
|
||||
{
|
||||
$cols[] = array('title' => $lang->report->annualData->productName, 'width' => 'auto', 'align' => 'left');
|
||||
$cols[] = array('title' => $lang->report->annualData->planCount, 'width' => '80', 'align' => 'center');
|
||||
$cols[] = array('title' => $lang->report->annualData->storyCount, 'width' => '80', 'align' => 'center');
|
||||
|
||||
foreach($data['products'] as $product) $rows[] = array($product->name, $product->plans, $product->stories);
|
||||
}
|
||||
elseif($role == 'dev')
|
||||
{
|
||||
$cols[] = array('title' => $lang->report->annualData->projectName, 'width' => 'auto', 'align' => 'left');
|
||||
$cols[] = array('title' => $lang->report->annualData->finishedStory, 'width' => '80', 'align' => 'center');
|
||||
$cols[] = array('title' => $lang->report->annualData->finishedTask, 'width' => '80', 'align' => 'center');
|
||||
$cols[] = array('title' => $lang->report->annualData->resolvedBug, 'width' => '80', 'align' => 'center');
|
||||
|
||||
foreach($data['projects'] as $project) $rows[] = array($project->name, $project->stories, $project->tasks, $project->bugs);
|
||||
}
|
||||
elseif($role == 'qa')
|
||||
{
|
||||
$cols[] = array('title' => $lang->report->annualData->projectName, 'width' => 'auto', 'align' => 'left');
|
||||
$cols[] = array('title' => $lang->report->annualData->foundBug, 'width' => '80', 'align' => 'center');
|
||||
|
||||
foreach($data['projects'] as $project) $rows[] = array($project->name, $project->bugs);
|
||||
}
|
||||
|
||||
echo "cols: " . json_encode($cols) . ',';
|
||||
echo "rows: " . json_encode($rows);
|
||||
}
|
||||
elseif($blockKey == 'block4')
|
||||
{
|
||||
if($role == 'qa')
|
||||
{
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
$totalData = array();
|
||||
foreach($data[$name] as $pri => $value) $totalData[] = array('title' => $pri, 'value' => $value, 'legend' => 'P' . $pri);
|
||||
|
||||
if($name == 'bugPri') $title = $lang->report->annualData->totalCreatedBug;
|
||||
if($name == 'casePri') $title = $lang->report->annualData->totalCreatedCase;
|
||||
$chart = array('title' => $title, 'unit' => $lang->report->annualData->unit, 'data' => $totalData);
|
||||
echo "chart" . ($i + 1) . ': ' . json_encode($chart) . ',';
|
||||
}
|
||||
}
|
||||
elseif($role == 'po')
|
||||
{
|
||||
$this->app->loadLang('story');
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
$totalData = array();
|
||||
foreach($data[$name] as $key => $value)
|
||||
{
|
||||
$totalData[] = array('title' => $key, 'value' => $value, 'legend' => ($name == 'storyPri' ? ('P' . $key) : $lang->story->stageList[$key]));
|
||||
}
|
||||
|
||||
if($name == 'storyPri') $title = $lang->report->annualData->totalStoryPri;
|
||||
if($name == 'storyStage') $title = $lang->report->annualData->totalStoryStage;
|
||||
$chart = array('title' => $title, 'unit' => $lang->report->annualData->unit, 'data' => $totalData);
|
||||
echo "chart" . ($i + 1) . ': ' . json_encode($chart) . ',';
|
||||
}
|
||||
}
|
||||
elseif($role == 'dev')
|
||||
{
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
$totalData = array();
|
||||
foreach($data[$name] as $key => $value) $totalData[] = array('title' => $key, 'value' => $value, 'legend' => 'P' . $key);
|
||||
|
||||
if($name == 'finishedTaskPri') $title = $lang->report->annualData->totalFinishedTask;
|
||||
if($name == 'resolvedBugPri') $title = $lang->report->annualData->totalResolvedBug;
|
||||
$chart = array('title' => $title, 'unit' => $lang->report->annualData->unit, 'data' => $totalData);
|
||||
echo "chart" . ($i + 1) . ': ' . json_encode($chart) . ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif($blockKey == 'block5')
|
||||
{
|
||||
echo 'labels:' . json_encode($lang->datepicker->monthNames) . ',';
|
||||
|
||||
if($role == 'qa')
|
||||
{
|
||||
$datasets = array();
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
if($name == 'bugMonth') $label = $lang->report->annualData->totalCreatedBug;
|
||||
if($name == 'caseMonth') $label = $lang->report->annualData->totalCreatedCase;
|
||||
$datasets[] = array('label' => $label, 'data' => $data[$name]);
|
||||
}
|
||||
}
|
||||
elseif($role == 'po')
|
||||
{
|
||||
$datasets = array();
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
if($name == 'storyMonth') $label = $lang->report->annualData->poStatistics;
|
||||
$datasets[] = array('label' => $label, 'data' => $data[$name]);
|
||||
}
|
||||
}
|
||||
elseif($role == 'dev')
|
||||
{
|
||||
$datasets = array();
|
||||
foreach($blockConfig['data'] as $i => $name)
|
||||
{
|
||||
if($name == 'effortMonth') $label = $lang->report->annualData->devStatistics;
|
||||
$datasets[] = array('label' => $label, 'data' => $data[$name]);
|
||||
}
|
||||
}
|
||||
echo "datasets: " . json_encode($datasets);
|
||||
}
|
||||
?>
|
||||
},
|
||||
<?php endforeach;?>
|
||||
};
|
||||
|
||||
// 显示年度数据
|
||||
$(function()
|
||||
{
|
||||
showAnnualData(annualData);
|
||||
|
||||
Reference in New Issue
Block a user