zen.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. <?php
  2. /**
  3. * The zen file of metric module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author zhouxin <zhouxin@easysoft.ltd>
  8. * @package metric
  9. * @link https://www.zentao.net
  10. */
  11. class metricZen extends metric
  12. {
  13. private $validObjects;
  14. /**
  15. * 构建创建度量的数据。
  16. * Build metric data for create.
  17. *
  18. * @access protected
  19. * @return object
  20. */
  21. protected function buildMetricForCreate()
  22. {
  23. return form::data($this->config->metric->form->create)
  24. ->setDefault('createdBy', $this->app->user->account)
  25. ->setDefault('createdDate', helper::now())
  26. ->get();
  27. }
  28. /**
  29. * 构建编辑度量的数据。
  30. * Build metric data for edit.
  31. *
  32. * @access protected
  33. * @return object
  34. */
  35. protected function buildMetricForEdit()
  36. {
  37. return form::data($this->config->metric->form->create)
  38. ->setDefault('editedBy', $this->app->user->account)
  39. ->setDefault('editedDate', helper::now())
  40. ->get();
  41. }
  42. /**
  43. * 成功插入度量数据后,其他的额外操作。
  44. * Process after create metric.
  45. *
  46. * @param int $metricID
  47. * @param string $afterCreate
  48. * @param string $from metric|metriclib
  49. * @param string $location
  50. * @access protected
  51. * @return array
  52. */
  53. protected function responseAfterCreate($metricID, $afterCreate, $from, $location)
  54. {
  55. if($afterCreate == 'back' && $location) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $location);
  56. $location = $this->createLink('metric', 'implement', "metricID=$metricID&from=$from");
  57. $callback = array('name' => 'loadImplement', 'params' => $location);
  58. return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => $callback);
  59. }
  60. /**
  61. * 成功编辑度量数据后,其他的额外操作。
  62. * Process after edit metric.
  63. *
  64. * @access protected
  65. * @return array
  66. */
  67. protected function responseAfterEdit($metricID, $afterEdit, $location)
  68. {
  69. if($afterEdit == 'back' && $location) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'load' => $location);
  70. $location = $this->createLink('metric', 'implement', "metricID=$metricID");
  71. $callback = array('name' => 'loadImplement', 'params' => $location);
  72. return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => $callback);
  73. }
  74. /**
  75. * 根据分类后的度量项,准备数据源句柄。
  76. * Prepare the data source handle based on the classified measures.
  77. *
  78. * @param object $calcGroup
  79. * @access protected
  80. * @return array
  81. */
  82. protected function prepareDataset($calcGroup)
  83. {
  84. $dao = $this->metric->getDAO();
  85. $dataSource = $calcGroup->dataset;
  86. $calcList = $calcGroup->calcList;
  87. foreach($calcList as $calc)
  88. {
  89. $calc->setDAO($dao);
  90. if($calc->useSCM && ($this->config->inQuickon || $this->config->inCompose))
  91. {
  92. $scm = $this->app->loadClass('scm');
  93. $calc->setSCM($scm);
  94. $calc->setGitFoxRepos($this->loadModel('repo')->getGitFoxRepos());
  95. }
  96. }
  97. if(empty($dataSource))
  98. {
  99. $calc = current($calcList);
  100. $calc->setDAO($dao);
  101. return $calc->getStatement();
  102. }
  103. foreach($calcList as $calc)
  104. {
  105. $calc->setHolidays($this->loadModel('holiday')->getList());
  106. $calc->setWeekend(isset($this->config->project->weekend) ? $this->config->project->weekend : 2);
  107. }
  108. $dataset = $this->metric->getDataset($dao);
  109. $fieldList = $this->metric->uniteFieldList($calcList);
  110. return $dataset->$dataSource($fieldList);
  111. }
  112. /**
  113. * 构建模块树数据。
  114. * Prepare module tree data.
  115. *
  116. * @param string $scope
  117. * @param string $stage
  118. * @param array $modules
  119. * @access protected
  120. * @return void
  121. */
  122. protected function prepareTree($scope, $stage, $modules)
  123. {
  124. $sortedModules = array();
  125. $groupModules = array();
  126. foreach($modules as $module)
  127. {
  128. $groupModules[$module->object][] = $module;
  129. }
  130. foreach(array_keys($this->lang->metric->objectList) as $object)
  131. {
  132. if(isset($groupModules[$object]) and !empty($groupModules[$object]))
  133. {
  134. $sortedModules = array_merge($sortedModules, $groupModules[$object]);
  135. }
  136. }
  137. $moduleTree = array();
  138. foreach($sortedModules as $module)
  139. {
  140. $object = $module->object;
  141. $purpose = $module->purpose;
  142. $moduleTree[$object] = (object)array
  143. (
  144. 'id' => $object,
  145. 'parent' => '0',
  146. 'name' => $this->lang->metric->objectList[$object],
  147. 'url' => $this->inlink('browse', "scope=$scope&stage=$stage&param=$object&type=byTree")
  148. );
  149. $moduleTree["{$object}_{$purpose}"] = (object)array
  150. (
  151. 'id' => "{$object}_{$purpose}",
  152. 'parent' => $object,
  153. 'name' => $this->lang->metric->purposeList[$purpose],
  154. 'url' => $this->inlink('browse', "scope=$scope&stage=$stage&param={$object}_{$purpose}&type=byTree")
  155. );
  156. }
  157. return $moduleTree;
  158. }
  159. /**
  160. * 构建范围下拉数据。
  161. * Prepare scope picker data.
  162. *
  163. * @access protected
  164. * @return void
  165. */
  166. protected function prepareScopeList()
  167. {
  168. $scopeList = array();
  169. foreach($this->lang->metric->scopeList as $scope => $scopeText) $scopeList[] = array('key' => $scope, 'text' => $scopeText);
  170. return $scopeList;
  171. }
  172. /**
  173. * 开始计时。
  174. *
  175. * @access private
  176. * @return float
  177. */
  178. private function startTime()
  179. {
  180. return microtime(true);
  181. }
  182. /**
  183. * 结束计时。
  184. *
  185. * @param float $beginTime
  186. * @access private
  187. * @return string
  188. */
  189. private function endTime($beginTime)
  190. {
  191. $time = microtime(true) - $beginTime;
  192. return number_format((float)$time, 5, '.', '0');
  193. }
  194. /**
  195. * 获取有效的对象。
  196. * Get valid objects.
  197. *
  198. * @access private
  199. * @return array
  200. */
  201. private function getValidObjects()
  202. {
  203. if($this->validObjects !== null) return $this->validObjects;
  204. /* 保证逻辑集中,这里直接使用sql查询获取数据,保证查询性能。*/
  205. /* To ensure logical concentration, here we directly use sql to get data to ensure query performance. */
  206. $productList = $this->dao->select('id,closedDate')->from(TABLE_PRODUCT)
  207. ->where('deleted')->eq(0)
  208. ->andWhere('status')->ne('closed')
  209. ->andWhere('shadow')->eq(0)
  210. ->fetchPairs('id', 'closedDate');
  211. $projectList = $this->dao->select('id,closedDate')->from(TABLE_PROJECT)
  212. ->where('deleted')->eq(0)
  213. ->andWhere('status')->notIN('closed,done')
  214. ->andWhere('type')->eq('project')
  215. ->fetchPairs('id', 'closedDate');
  216. $executionList = $this->dao->select('id,closedDate')->from(TABLE_EXECUTION)
  217. ->where('deleted')->eq(0)
  218. ->andWhere('status')->notIN('closed,done')
  219. ->andWhere('type')->in('sprint,stage,kanban')
  220. ->fetchPairs('id', 'closedDate');
  221. $this->validObjects = array('product' => $productList, 'project' => $projectList, 'execution' => $executionList);
  222. return $this->validObjects;
  223. }
  224. /**
  225. * 计算度量数据。
  226. * Calculate metric data.
  227. *
  228. * @param array $classifiedCalcGroup
  229. * @access protected
  230. * @return void
  231. */
  232. protected function calculateMetric($classifiedCalcGroup)
  233. {
  234. set_time_limit(0);
  235. $calcBeginTime = $this->startTime();
  236. foreach($classifiedCalcGroup as $calcGroup)
  237. {
  238. try
  239. {
  240. /* 开始计算度量数据。*/
  241. /* Start calculating metric data. */
  242. $beginTime = $this->startTime();
  243. $statement = $this->prepareDataset($calcGroup);
  244. $this->calcMetric($statement, $calcGroup->calcList);
  245. $recordWithCode = $this->prepareMetricRecord($calcGroup->calcList);
  246. $calcTime = $this->endTime($beginTime);
  247. /* 开始插入度量数据。*/
  248. /* Start inserting metric data. */
  249. $beginTime = $this->startTime();
  250. $this->metric->insertMetricLib($recordWithCode);
  251. $executeTime = $this->endTime($beginTime);
  252. /* 记录度量数据计算和插入的时间以及sql语句。*/
  253. /* Record the time of metric data calculation and insertion and sql statement. */
  254. $total = 0;
  255. $codes = '';
  256. $sql = $statement ? $statement->get() : '';
  257. foreach($recordWithCode as $code => $records)
  258. {
  259. $count = count($records);
  260. $codes .= "$code($count), ";
  261. $total += $count;
  262. }
  263. $this->metric->saveLogs("Calculate consumed time(seconds): calc: $calcTime, insert: $executeTime, total: $total, sql: $sql, $codes");
  264. }
  265. catch(Exception $e)
  266. {
  267. a($this->formatException($e));
  268. }
  269. catch(Error $e)
  270. {
  271. a($this->formatException($e));
  272. }
  273. }
  274. /* 记录度量数据计算的总时间。*/
  275. /* Record the total time of metric data calculation. */
  276. $executeTime = $this->endTime($calcBeginTime);
  277. $this->metric->saveLogs("Calculate all consumed time: {$executeTime} seconds");
  278. /* 开始去重。*/
  279. /* Start deduplication. */
  280. $beginTime = $this->startTime();
  281. $metrics = $this->metric->getExecutableMetric();
  282. foreach($metrics as $code)
  283. {
  284. $deduplicationBeginTime = $this->startTime();
  285. $this->metric->deduplication($code);
  286. $executeTime = $this->endTime($deduplicationBeginTime);
  287. $this->metric->saveLogs("Deduplication consumed time: {$executeTime} seconds, the code: $code");
  288. }
  289. /* 重建主键。*/
  290. /* Rebuild the primary key. */
  291. $this->metric->rebuildPrimaryKey();
  292. /* 记录去重的时间。*/
  293. /* Record the time of deduplication. */
  294. $executeTime = $this->endTime($beginTime);
  295. $this->metric->saveLogs("Deduplicate all consumed time: {$executeTime} seconds");
  296. }
  297. /**
  298. * 根据度量项计算的结果,构建可插入表的度量数据。
  299. * Build measurements that can be inserted into tables based on the results of the measurements computed.
  300. *
  301. * @param array $calcList
  302. * @access protected
  303. * @return array
  304. */
  305. protected function prepareMetricRecord($calcList)
  306. {
  307. /* 获取今天和昨天的日期。*/
  308. /* Get the date of today and yesterday. */
  309. $yesterday = date('j', strtotime('-1 day', strtotime('today')));
  310. $today = date('j');
  311. $options = array('year' => date('Y'), 'month' => date('n'), 'week' => substr(date('oW'), -2), 'day' => "$today,$yesterday");
  312. /* 获取未关闭的对象id。*/
  313. /* Get the id of the unclosed objects. */
  314. $this->getValidObjects();
  315. $validObjects = $this->validObjects;
  316. $records = array();
  317. $yesterday = strtotime('-1 day');
  318. foreach($calcList as $code => $calc)
  319. {
  320. $metric = $this->metric->getByCode($code);
  321. /* 如果度量项是复用的,则计算复用的度量数据。*/
  322. /* If the metric is reused, calculate the reused metric data. */
  323. if($calc->reuse) $this->prepareReuseMetricResult($calc, $options);
  324. /* 判断是否统计关闭时的对象的度量数据。*/
  325. $endWithClosing = preg_match('/_when_closing$/', $code);
  326. $results = $calc->getResult($options);
  327. $records[$code] = array();
  328. if(is_array($results))
  329. {
  330. $scope = $metric->scope;
  331. foreach($results as $record)
  332. {
  333. $record = (object)$record;
  334. if(!is_numeric($record->value) || empty($record->value)) continue;
  335. /* 如果度量项是产品、项目、执行且不统计关闭时的对象的度量数据,则过滤掉已关闭的数据。*/
  336. /* If the metric is product, project, execution, filter out the closed data. */
  337. $closedDate = isset($validObjects[$scope]) && !helper::isZeroDate(zget($validObjects[$scope], $record->$scope, '')) ? $validObjects[$scope][$record->$scope] : null;
  338. $isClosedEarlierThanYesterday = $closedDate ? strtotime($closedDate) < $yesterday : false;
  339. if(!$endWithClosing && $closedDate !== null && $isClosedEarlierThanYesterday) continue;
  340. $record->metricID = $calc->id;
  341. $record->metricCode = $code;
  342. $record->date = helper::now();
  343. $record->system = $metric->scope == 'system' ? 1 : 0;
  344. $records[$code][] = $record;
  345. }
  346. }
  347. }
  348. return $records;
  349. }
  350. /**
  351. * 获取复用的度量数据。
  352. * Prepare metric result for reuse metric.
  353. *
  354. * @param object $calc
  355. * @param array $options
  356. * @access protected
  357. * @return array
  358. */
  359. protected function prepareReuseMetricResult($calc, $options)
  360. {
  361. $reuseMetrics = array();
  362. foreach($calc->reuseMetrics as $key => $reuseMetric) $reuseMetrics[$key] = $this->metric->getResultByCode($reuseMetric, $options);
  363. $calc->calculate($reuseMetrics);
  364. }
  365. /**
  366. * 获取某度量项某日期的度量数据。
  367. * Get metric record by code and date.
  368. *
  369. * @param string $code
  370. * @param object $calc
  371. * @param string $date
  372. * @param string $type
  373. * @access protected
  374. * @return array
  375. */
  376. protected function getRecordByCodeAndDate($code, $calc, $date, $type = 'single')
  377. {
  378. $now = helper::now();
  379. $metric = $this->metric->getByCode($code);
  380. $dateType = $this->metric->getDateTypeByCode($code);
  381. if($dateType == 'nodate') return array();
  382. if($type == 'all' && $this->metric->checkHasInferenceOfDate($code, $dateType, $date)) return array();
  383. $records = array();
  384. $dateConfig = $this->metric->parseDateStr($date, $dateType);
  385. $results = $calc->getResult($dateConfig);
  386. if(is_array($results))
  387. {
  388. foreach($results as $record)
  389. {
  390. $record = (object)$record;
  391. if(empty($record->value)) continue;
  392. $record->metricID = $metric->id;
  393. $record->metricCode = $code;
  394. $record->date = $now;
  395. $record->system = $metric->scope == 'system' ? 1 : 0;
  396. $records[] = $record;
  397. }
  398. }
  399. return $records;
  400. }
  401. /**
  402. * 根据度量项编码,初始化度量数据。
  403. * Initialize metric data based on metric code.
  404. *
  405. * @param string $code
  406. * @param string $dateType
  407. * @param string $date
  408. * @access protected
  409. * @return array
  410. */
  411. protected function initMetricRecords($recordCommon, $scope, $date = 'now')
  412. {
  413. $records = array();
  414. if($scope == 'system')
  415. {
  416. $record = clone $recordCommon;
  417. //$record->system = 1;
  418. $uniqueKey = $this->getUniqueKeyByRecord($record);
  419. $records[$uniqueKey] = $record;
  420. }
  421. else
  422. {
  423. if($date == 'now') $date = helper::now();
  424. $scopeList = $this->metric->getPairsByScopeAndDate($scope, $date);
  425. foreach($scopeList as $key => $value)
  426. {
  427. $record = clone $recordCommon;
  428. $record->$scope = $key;
  429. $uniqueKey = $this->getUniqueKeyByRecord($record);
  430. $records[$uniqueKey] = $record;
  431. }
  432. }
  433. return $records;
  434. }
  435. /**
  436. * 根据度量项范围,构建度量数据的通用字段。
  437. * Build common fields of metric data based on metric scope.
  438. *
  439. * @param int $metricID
  440. * @param string $code
  441. * @param string $date
  442. * @param array $dateValues
  443. * @access protected
  444. * @return array
  445. */
  446. protected function buildRecordCommonFields($metricID, $code, $date, $dateValues)
  447. {
  448. $record = new stdclass();
  449. $record->value = 0;
  450. $record->date = $date;
  451. $record->calcType = 'cron';
  452. $record->calculatedBy = 'system';
  453. $record = (object)array_merge($dateValues, (array)$record);
  454. return $record;
  455. }
  456. /**
  457. * 补全缺失的度量数据。
  458. * Complete missing metric data.
  459. *
  460. * @param array $records
  461. * @param array $metric
  462. * @access protected
  463. * @return array
  464. */
  465. protected function completeMissingRecords($records, $metric)
  466. {
  467. $now = helper::now();
  468. $dateValues = $this->metric->parseDateStr($now);
  469. $dateType = $metric->dateType;
  470. $recordCommon = $this->buildRecordCommonFields($metric->id, $metric->code, $now, $dateValues->$dateType);
  471. $initRecords = $this->initMetricRecords($recordCommon, $metric->scope);
  472. foreach($records as $record)
  473. {
  474. $uniqueKey = $this->getUniqueKeyByRecord($record, $metric->scope);
  475. if(!isset($initRecords[$uniqueKey]))
  476. {
  477. $initRecords[$uniqueKey] = $record;
  478. continue;
  479. }
  480. $initRecords[$uniqueKey]->value = $record->value;
  481. }
  482. return array_values($initRecords);
  483. }
  484. /**
  485. * 根据度量数据,获取度量数据的唯一键。
  486. * Get the unique key of metric data based on metric data.
  487. *
  488. * @param object $record
  489. * @param string $scope
  490. * @access protected
  491. * @return string
  492. */
  493. protected function getUniqueKeyByRecord($record, $scope = '')
  494. {
  495. $record = (array)$record;
  496. $uniqueKeys = array();
  497. $ignoreFields = array('value', 'metricID', 'metricCode', 'calcType', 'calculatedBy', 'date');
  498. if($scope == 'system') $ignoreFields[] = 'id';
  499. foreach($record as $field => $value)
  500. {
  501. if(in_array($field, $ignoreFields) || empty($value)) continue;
  502. $uniqueKeys[] = $field . $value;
  503. }
  504. return implode('_', $uniqueKeys);
  505. }
  506. /**
  507. * 遍历数据,对每个度量项计算每一行数据。
  508. * Calculate metric for every row.
  509. *
  510. * @param array $rows
  511. * @param array $calcList
  512. * @access protected
  513. * @return void
  514. */
  515. protected function calcMetric($statement, $calcList)
  516. {
  517. if(empty($statement)) return;
  518. $dbType = $this->config->metricDB->type;
  519. if($dbType == 'duckdb')
  520. {
  521. $this->loadModel('bi');
  522. $sql = $statement->get();
  523. $dbh = $this->app->loadDriver('duckdb');
  524. $rows = $dbh->query($sql)->fetchAll();
  525. foreach($rows as $row)
  526. {
  527. foreach($calcList as $calc)
  528. {
  529. if(!$calc->reuse)
  530. {
  531. $record = $this->getCalcFields($calc, $row);
  532. $calc->calculate($record);
  533. }
  534. }
  535. }
  536. return;
  537. }
  538. $statement = $statement->query();
  539. while($row = $statement->fetch())
  540. {
  541. foreach($calcList as $code => $calc)
  542. {
  543. if(!$calc->reuse)
  544. {
  545. $record = $this->getCalcFields($calc, $row);
  546. $calc->calculate($record);
  547. }
  548. }
  549. }
  550. }
  551. protected function getCalcFields($calc, $row)
  552. {
  553. if(!isset($calc->dataset) || empty($calc->dataset)) return (object)$row;
  554. $pureRow = new stdclass();
  555. foreach($calc->fieldList as $field)
  556. {
  557. if(strpos(strtoupper($field), ' AS ') !== false)
  558. {
  559. $pos = strpos(strtoupper($field), ' AS ');
  560. $tag = substr($field, $pos, 4);
  561. $extractField = explode($tag, $field);
  562. $pureField = end($extractField);
  563. $aliasField = $pureField;
  564. }
  565. else
  566. {
  567. $extractField = explode('.', $field);
  568. $pureField = end($extractField);
  569. $aliasField = str_replace('.', '_', $field);
  570. }
  571. $pureRow->$pureField = $row->$aliasField;
  572. }
  573. if(isset($row->defaultHours)) $pureRow->defaultHours = $row->defaultHours;
  574. return $pureRow;
  575. }
  576. /**
  577. * 获取度量项的基本信息。
  578. * Get the basic information of the metric.
  579. *
  580. * @param object $view
  581. * @param string $fields
  582. * @access protected
  583. * @return array
  584. */
  585. protected function getBasicInfo($view, $fields = 'scope,object,purpose,dateType,name,alias,code,unit,stage')
  586. {
  587. extract((array)$view);
  588. $isOldMetric = $this->metric->isOldMetric($metric);
  589. $unit = $isOldMetric ? $metric->oldUnit : zget($this->lang->metric->unitList, $metric->unit);
  590. $legendBasic = array();
  591. if(strpos($fields, 'scope') !== false) $legendBasic['scope'] = array('name' => $this->lang->metric->scope, 'text' => zget($this->lang->metric->scopeList, $metric->scope));
  592. if(strpos($fields, 'object') !== false) $legendBasic['object'] = array('name' => $this->lang->metric->object, 'text' => zget($this->lang->metric->objectList, $metric->object));
  593. if(strpos($fields, 'purpose') !== false) $legendBasic['purpose'] = array('name' => $this->lang->metric->purpose, 'text' => zget($this->lang->metric->purposeList, $metric->purpose));
  594. if(strpos($fields, 'dateType') !== false) $legendBasic['dateType'] = array('name' => $this->lang->metric->dateType, 'text' => zget($this->lang->metric->dateTypeList, $metric->dateType));
  595. if(strpos($fields, 'name') !== false) $legendBasic['name'] = array('name' => $this->lang->metric->name, 'text' => $metric->name);
  596. if(strpos($fields, 'alias') !== false) $legendBasic['alias'] = array('name' => $this->lang->metric->alias, 'text' => $metric->alias);
  597. if(strpos($fields, 'code') !== false) $legendBasic['code'] = array('name' => $this->lang->metric->code, 'text' => $metric->code);
  598. if(strpos($fields, 'unit') !== false) $legendBasic['unit'] = array('name' => $this->lang->metric->unit, 'text' => $unit);
  599. if($isOldMetric)
  600. {
  601. $legendBasic['collectType'] = array('name' => $this->lang->metric->collectType, 'text' => zget($this->lang->metric->old->collectTypeList, $metric->collectType));
  602. $legendBasic['collectConf'] = array('name' => $this->lang->metric->collectConf, 'text' => $this->metric->getCollectConfText($metric));
  603. }
  604. if(strpos($fields, 'desc') !== false) $legendBasic['desc'] = array('name' => $this->lang->metric->desc, 'text' => $metric->desc);
  605. if(strpos($fields, 'definition') !== false) $legendBasic['definition'] = array('name' => $this->lang->metric->definition, 'text' => $metric->definition);
  606. if(strpos($fields, 'stage') !== false) $legendBasic['stage'] = array('name' => $this->lang->metric->stage, 'text' => zget($this->lang->metric->stageList, $metric->stage));
  607. return $legendBasic;
  608. }
  609. /**
  610. * 获取度量项的创建和编辑信息。
  611. * Get the create and edit information of the metric.
  612. *
  613. * @param object $view
  614. * @param string $fields
  615. * @access protected
  616. * @return array
  617. */
  618. protected function getCreateEditInfo($view, $fields = 'createdBy,implementedBy,offlineBy,lastEdited')
  619. {
  620. extract((array)$view);
  621. $users = $this->loadModel('user')->getPairs('noletter');
  622. $users['system'] = 'system';
  623. $createEditInfo = array();
  624. if(strpos($fields, 'createdBy') !== false) $createEditInfo['createdBy'] = array('name' => $this->lang->metric->createdBy, 'text' => zget($users, $metric->createdBy) . ($metric->createdBy ? $this->lang->at . $metric->createdDate : ''));
  625. if(strpos($fields, 'implementedBy') !== false) $createEditInfo['implementedBy'] = array('name' => $this->lang->metric->implementedBy, 'text' => zget($users, $metric->implementedBy) . ($metric->implementedBy ? $this->lang->at . $metric->implementedDate : ''));
  626. if(strpos($fields, 'offlineBy') !== false) $createEditInfo['offlineBy'] = array('name' => $this->lang->metric->offlineBy, 'text' => zget($users, $metric->delistedBy) . ($metric->delistedBy ? $this->lang->at . $metric->delistedDate : ''));
  627. if(strpos($fields, 'lastEdited') !== false) $createEditInfo['lastEdited'] = array('name' => $this->lang->metric->lastEdited, 'text' => zget($users, $metric->editedBy) . ($metric->editedBy ? $this->lang->at . $metric->editedDate : ''));
  628. return $createEditInfo;
  629. }
  630. /**
  631. * 获取旧版详情区块。
  632. * Get old metric info panel.
  633. *
  634. * @param int $oldMetricID
  635. * @access protected
  636. * @return array
  637. */
  638. protected function getOldMetricInfo($oldMetricID)
  639. {
  640. $oldMetric = $this->metric->getOldMetricByID($oldMetricID);
  641. $oldMetricInfo = array();
  642. $oldMetricInfo['scope'] = array('name' => $this->lang->metric->scope, 'text' => zget($this->lang->metric->old->scopeList, $oldMetric->scope));
  643. $oldMetricInfo['object'] = array('name' => $this->lang->metric->object, 'text' => zget($this->lang->metric->old->objectList, $oldMetric->object));
  644. $oldMetricInfo['purpose'] = array('name' => $this->lang->metric->purpose, 'text' => zget($this->lang->metric->old->purposeList, $oldMetric->purpose));
  645. $oldMetricInfo['code'] = array('name' => $this->lang->metric->code, 'text' => $oldMetric->code);
  646. $oldMetricInfo['unit'] = array('name' => $this->lang->metric->unit, 'text' => $oldMetric->unit);
  647. $oldMetricInfo['collectType'] = array('name' => $this->lang->metric->collectType, 'text' => zget($this->lang->metric->old->collectTypeList, $oldMetric->collectType));
  648. $oldMetricInfo['collectConf'] = array('name' => $this->lang->metric->collectConf, 'text' => $this->metric->getCollectConfText($oldMetric));
  649. $oldMetricInfo['definition'] = array('name' => $this->lang->metric->declaration, 'text' => $oldMetric->definition);
  650. $oldMetricInfo['sql'] = array('name' => $this->lang->metric->sqlStatement, 'text' => $oldMetric->configure);
  651. return $oldMetricInfo;
  652. }
  653. /**
  654. * 处理周定时配置。
  655. * Process week configuration.
  656. *
  657. * @param string $dateConf
  658. * @access protected
  659. * @return string
  660. */
  661. protected function processWeekConf($dateConf)
  662. {
  663. $days = explode(',', $dateConf);
  664. $dateConfNames = array();
  665. foreach($days as $day)
  666. {
  667. $dateConfNames[] = $this->lang->metric->oldMetric->dayNames[$day];
  668. }
  669. $dateConf = implode('、', $dateConfNames);
  670. return $dateConf;
  671. }
  672. /**
  673. * 获取度量数据表的宽度。
  674. * Get width of result table in view.
  675. *
  676. * @param array $headers
  677. * @access protected
  678. * @return int
  679. */
  680. protected function getViewTableWidth($headers)
  681. {
  682. $width = 0;
  683. foreach($headers as $header) $width += isset($header['width']) ? $header['width'] : 160;
  684. /* Add a little redundancy. */
  685. $width ++;
  686. return $width;
  687. }
  688. /**
  689. * Get pager extra.
  690. *
  691. * @param string $tableWidth
  692. * @access protected
  693. * @return string
  694. */
  695. public function getPagerExtra($tableWidth)
  696. {
  697. return ($tableWidth > 300) ? '' : 'shortPageSize';
  698. }
  699. /**
  700. * 格式化异常为字符串。
  701. * Format exception to string.
  702. *
  703. * @param Exception $e
  704. * @access protected
  705. * @return string
  706. */
  707. public function formatException($e)
  708. {
  709. $message = $e->getMessage();
  710. $line = $e->getLine();
  711. $file = $e->getFile();
  712. return "Error: $message in $file on line $line";
  713. }
  714. /**
  715. * 根据后台配置的估算单位对列表赋值。
  716. * Assign unitList['measure'] by custom hourPoint.
  717. *
  718. * @access protected
  719. * @return void
  720. */
  721. protected function processUnitList()
  722. {
  723. $this->app->loadLang('custom');
  724. $key = zget($this->config->custom, 'hourPoint', '0');
  725. $this->lang->metric->unitList['measure'] = $this->lang->custom->conceptOptions->hourPoint[$key];
  726. }
  727. /**
  728. * 构建操作权限。
  729. * Prepare action priv.
  730. *
  731. * @param array $metrics
  732. * @access protected
  733. * @return array
  734. */
  735. protected function prepareActionPriv($metrics)
  736. {
  737. $this->loadModel('screen');
  738. foreach($metrics as $metric)
  739. {
  740. $metric->canEdit = $metric->stage == 'wait';
  741. $metric->canImplement = ($metric->stage == 'wait' && !$this->metric->isOldMetric($metric) && $metric->builtin == '0');
  742. $metric->canDelist = $metric->stage == 'released' && $metric->builtin == '0';
  743. $metric->canRecalculate = $metric->stage == 'released' && !empty($metric->dateType) && $metric->dateType != 'nodate';
  744. $metric->isUsed = $this->screen->checkIFChartInUse($metric->id, 'metric');
  745. }
  746. return $metrics;
  747. }
  748. }