reports.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. /**
  3. * The reports entry point of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Chunsheng Wang <chunsheng@cnezsoft.com>
  8. * @package entries
  9. * @version 1
  10. * @link https://www.zentao.net
  11. */
  12. class reportsEntry extends entry
  13. {
  14. /**
  15. * GET method.
  16. *
  17. * @access public
  18. * @return string
  19. */
  20. public function get()
  21. {
  22. $fields = $this->param('fields', '');
  23. $dept = $this->param('dept', 0);
  24. $account = $this->param('account', '');
  25. $year = $this->param('year', date('Y'));
  26. if(empty($fields)) return $this->send(400, 'Need fields param for report.');
  27. $accounts = array();
  28. if($account) $accounts = array($account => $account);
  29. if(empty($accounts) and $dept) $accounts = array_keys($this->loadModel('dept')->getDeptUserPairs($dept));
  30. if(empty($accounts) and empty($dept)) $accounts = array_keys($this->loadModel('user')->getPairs('noclosed'));
  31. $fields = explode(',', strtolower($fields));
  32. $report = array();
  33. foreach($fields as $field)
  34. {
  35. $field = trim($field);
  36. if(empty($field)) continue;
  37. if($field == 'projectoverview')
  38. {
  39. $report['projectOverview'] = $this->projectOverview($accounts);
  40. }
  41. elseif($field == 'radar')
  42. {
  43. $report['radar'] = $this->radar($accounts, $year);
  44. }
  45. elseif($field == 'projectprogress')
  46. {
  47. $report['projectProgress'] = $this->projectProgress();
  48. }
  49. elseif($field == 'executionprogress')
  50. {
  51. $report['executionProgress'] = $this->executionProgress();
  52. }
  53. elseif($field == 'productprogress')
  54. {
  55. $report['productProgress'] = $this->productProgress();
  56. }
  57. elseif($field == 'bugprogress')
  58. {
  59. $report['bugProgress'] = $this->bugProgress();
  60. }
  61. elseif($field == 'bugprogress')
  62. {
  63. $report['bugProgress'] = $this->bugProgress();
  64. }
  65. elseif($field == 'output')
  66. {
  67. $report['output'] = $this->loadModel('report')->getOutput4API($accounts, $year);
  68. }
  69. }
  70. return $this->send(200, $report);
  71. }
  72. /**
  73. * Get project overview by status.
  74. *
  75. * @param array $accounts
  76. * @access public
  77. * @return array
  78. */
  79. public function projectOverview($accounts)
  80. {
  81. $statusOverview = $this->loadModel('report')->getProjectStatusOverview($accounts);
  82. $this->app->loadLang('project');
  83. $total = 0;
  84. $overview = array();
  85. foreach($statusOverview as $status => $count)
  86. {
  87. $total += $count;
  88. $statusName = zget($this->lang->project->statusList, $status);
  89. $overview[$status] = array();
  90. $overview[$status]['code'] = $status;
  91. $overview[$status]['name'] = $statusName;
  92. $overview[$status]['total'] = $count;
  93. }
  94. $projectOverview = array();
  95. $projectOverview['total'] = $total;
  96. $projectOverview['overview'] = array_values($overview);
  97. return $projectOverview;
  98. }
  99. /**
  100. * Get radar data. include product, execution, qa, devel and other.
  101. *
  102. * @param array $accounts
  103. * @param string $year
  104. * @access public
  105. * @return array
  106. */
  107. public function radar($accounts, $year)
  108. {
  109. $contributions = $this->loadModel('report')->getUserYearContributions($accounts, $year);
  110. $annualDataConfig = $this->config->report->annualData;
  111. $radarData = array('product' => 0, 'execution' => 0, 'devel' => 0, 'qa' => 0, 'other' => 0);
  112. foreach($contributions as $objectType => $objectContributions)
  113. {
  114. foreach($objectContributions as $actionName => $count)
  115. {
  116. $radarTypes = isset($annualDataConfig['radar'][$objectType][$actionName]) ? $annualDataConfig['radar'][$objectType][$actionName] : array('other');
  117. foreach($radarTypes as $radarType) $radarData[$radarType] += $count;
  118. }
  119. }
  120. $radar = array();
  121. foreach($radarData as $radarType => $total)
  122. {
  123. $radar[$radarType]['code'] = $radarType;
  124. $radar[$radarType]['name'] = $this->lang->report->annualData->radarItems[$radarType];
  125. $radar[$radarType]['total'] = $total;
  126. }
  127. return array_values($radar);
  128. }
  129. /**
  130. * Get project progress.
  131. *
  132. * @access public
  133. * @return array
  134. */
  135. public function projectProgress()
  136. {
  137. $projects = $this->loadModel('program')->getProjectStats(0, 'all');
  138. $this->app->loadLang('project');
  139. $processedProjects = array();
  140. $statusList['all']['total'] = 0;
  141. $statusList['doing']['total'] = 0;
  142. $statusList['wait']['total'] = 0;
  143. $statusList['closed']['total'] = 0;
  144. foreach($projects as $project)
  145. {
  146. $newProject = new stdclass();
  147. $newProject->id = $project->id;
  148. $newProject->name = $project->name;
  149. $newProject->status = $project->status;
  150. $newProject->progress = round($project->progress, 1);
  151. $newProject->totalConsumed = round($project->totalConsumed, 1);
  152. $newProject->totalLeft = round($project->totalLeft, 1);
  153. if(isset($project->delay)) $newProject->delay = $project->delay;
  154. $statusList['all']['total'] += 1;
  155. if(isset($statusList[$project->status])) $statusList[$project->status]['total'] += 1;
  156. $processedProjects[$project->id] = $newProject;
  157. }
  158. foreach(array_keys($statusList) as $status)
  159. {
  160. $statusName = zget($this->lang->project->statusList, $status);
  161. if($status == 'all') $statusName = $this->lang->project->featureBar['browse']['all'];
  162. $statusList[$status]['code'] = $status;
  163. $statusList[$status]['name'] = $statusName;
  164. }
  165. return array('statusList' => $statusList, 'projects' => array_values($processedProjects));
  166. }
  167. /**
  168. * Get execution progress.
  169. *
  170. * @access public
  171. * @return array
  172. */
  173. public function executionProgress()
  174. {
  175. $executions = $this->loadModel('execution')->getStatData(0, 'all', 0, 0, false, '', 'id_desc');
  176. $processedExecutions = array();
  177. $statusList['all']['total'] = 0;
  178. $statusList['doing']['total'] = 0;
  179. $statusList['wait']['total'] = 0;
  180. $statusList['closed']['total'] = 0;
  181. foreach($executions as $execution)
  182. {
  183. $newExecution = new stdclass();
  184. $newExecution->id = $execution->id;
  185. $newExecution->name = $execution->name;
  186. $newExecution->status = $execution->status;
  187. $newExecution->progress = round($execution->progress, 1);
  188. $newExecution->totalConsumed = round($execution->totalConsumed, 1);
  189. $newExecution->totalLeft = round($execution->totalLeft, 1);
  190. if(isset($execution->delay)) $newExecution->delay = $execution->delay;
  191. $statusList['all']['total'] += 1;
  192. if(isset($statusList[$execution->status])) $statusList[$execution->status]['total'] += 1;
  193. $processedExecutions[$execution->id] = $newExecution;
  194. }
  195. foreach(array_keys($statusList) as $status)
  196. {
  197. $statusName = zget($this->lang->execution->statusList, $status);
  198. if($status == 'all') $statusName = $this->lang->execution->allTasks;
  199. $statusList[$status]['code'] = $status;
  200. $statusList[$status]['name'] = $statusName;
  201. }
  202. return array('statusList' => $statusList, 'executions' => array_values($processedExecutions));
  203. }
  204. /**
  205. * Get product progress with story.
  206. *
  207. * @access public
  208. * @return array
  209. */
  210. public function productProgress()
  211. {
  212. $this->app->loadLang('product');
  213. $this->app->loadLang('story');
  214. $storyStatusStat = $this->dao->select('t1.product, t2.name, t2.status, t1.status AS storyStatus, COUNT(1) AS storyCount')->from(TABLE_STORY)->alias('t1')
  215. ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
  216. ->where('t2.deleted')->eq(0)
  217. ->andWhere('t1.deleted')->eq(0)
  218. ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi()
  219. ->groupBy('t1.product,t1.status')
  220. ->orderBy('t1.product_desc,t1.status')
  221. ->fetchAll();
  222. $productStatusList['all']['total'] = 0;
  223. $productStatusList['normal']['total'] = 0;
  224. $productStatusList['closed']['total'] = 0;
  225. $processedProducts = array();
  226. $productStoryStat = array();
  227. foreach($storyStatusStat as $product)
  228. {
  229. $productStoryStat[$product->product][$product->storyStatus] = $product->storyCount;
  230. if(isset($processedProducts[$product->product])) continue;
  231. $newProduct = new stdclass();
  232. $newProduct->id = $product->product;
  233. $newProduct->name = $product->name;
  234. $newProduct->status = $product->status;
  235. $processedProducts[$product->product] = $newProduct;
  236. $productStatusList['all']['total'] += 1;
  237. if(isset($productStatusList[$product->status])) $productStatusList[$product->status]['total'] += 1;
  238. }
  239. /* Set story status statistics integrate into product. */
  240. $storyStatusList = array('draft' => array(), 'reviewing' => array(), 'active' => array(), 'changing' => array(), 'closed' => array());
  241. foreach($processedProducts as $productID => $product)
  242. {
  243. $product->storyStat = array();
  244. foreach(array_keys($storyStatusList) as $storyStatus) $product->storyStat[$storyStatus] = isset($productStoryStat[$productID][$storyStatus]) ? $productStoryStat[$productID][$storyStatus] : 0;
  245. $product->progress = $product->storyStat['closed'] == 0 ? 0 : round($product->storyStat['closed'] / array_sum($product->storyStat) * 100, 1);
  246. }
  247. foreach(array_keys($productStatusList) as $status)
  248. {
  249. $statusName = zget($this->lang->product->statusList, $status);
  250. if($status == 'all') $statusName = $this->lang->product->allStory;
  251. if($status == 'normal') $statusName = $this->lang->product->unclosed;
  252. $productStatusList[$status]['code'] = $status;
  253. $productStatusList[$status]['name'] = $statusName;
  254. }
  255. foreach(array_keys($storyStatusList) as $status)
  256. {
  257. $statusName = zget($this->lang->story->statusList, $status);
  258. $storyStatusList[$status]['code'] = $status;
  259. $storyStatusList[$status]['name'] = $statusName;
  260. }
  261. return array('productStatusList' => $productStatusList, 'products' => array_values($processedProducts), 'storyStatusList' => $storyStatusList);
  262. }
  263. /**
  264. * Get bug progress by product.
  265. *
  266. * @access public
  267. * @return array
  268. */
  269. public function bugProgress()
  270. {
  271. $this->app->loadLang('product');
  272. $this->app->loadLang('bug');
  273. $bugStatusStat = $this->dao->select('t1.product, t2.name, t2.status, t1.status AS bugStatus, COUNT(1) AS bugCount')->from(TABLE_BUG)->alias('t1')
  274. ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id')
  275. ->where('t2.deleted')->eq(0)
  276. ->andWhere('t1.deleted')->eq(0)
  277. ->beginIF(!$this->app->user->admin)->andWhere('t2.id')->in($this->app->user->view->products)->fi()
  278. ->groupBy('t1.product,t1.status')
  279. ->orderBy('t1.product_desc,t1.status')
  280. ->fetchAll();
  281. $productStatusList['all']['total'] = 0;
  282. $productStatusList['normal']['total'] = 0;
  283. $productStatusList['closed']['total'] = 0;
  284. $processedProducts = array();
  285. $productBugStat = array();
  286. foreach($bugStatusStat as $product)
  287. {
  288. $productBugStat[$product->product][$product->bugStatus] = $product->bugCount;
  289. if(isset($processedProducts[$product->product])) continue;
  290. $newProduct = new stdclass();
  291. $newProduct->id = $product->product;
  292. $newProduct->name = $product->name;
  293. $newProduct->status = $product->status;
  294. $processedProducts[$product->product] = $newProduct;
  295. $productStatusList['all']['total'] += 1;
  296. if(isset($productStatusList[$product->status])) $productStatusList[$product->status]['total'] += 1;
  297. }
  298. /* Set bug status statistics integrate into product. */
  299. $bugStatusList = array('active' => array(), 'resolved' => array(), 'closed' => array());
  300. foreach($processedProducts as $productID => $product)
  301. {
  302. $product->bugStat = array();
  303. foreach(array_keys($bugStatusList) as $bugStatus) $product->bugStat[$bugStatus] = isset($productBugStat[$productID][$bugStatus]) ? $productBugStat[$productID][$bugStatus] : 0;
  304. }
  305. foreach(array_keys($productStatusList) as $status)
  306. {
  307. $statusName = zget($this->lang->product->statusList, $status);
  308. if($status == 'all') $statusName = $this->lang->product->allStory;
  309. if($status == 'normal') $statusName = $this->lang->product->unclosed;
  310. $productStatusList[$status]['code'] = $status;
  311. $productStatusList[$status]['name'] = $statusName;
  312. }
  313. foreach(array_keys($bugStatusList) as $status)
  314. {
  315. $statusName = zget($this->lang->bug->statusList, $status);
  316. $bugStatusList[$status]['code'] = $status;
  317. $bugStatusList[$status]['name'] = $statusName;
  318. }
  319. return array('productStatusList' => $productStatusList, 'bugs' => array_values($processedProducts), 'bugStatusList' => $bugStatusList);
  320. }
  321. }