| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945 |
- <?php
- use function zin\wg;
- /**
- * The model file of metric module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
- * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author zhouxin <zhouxin@easycorp.ltd>
- * @package metric
- * @version $Id: model.php 5145 2013-07-15 06:47:26Z zhouxin@easycorp.ltd $
- * @link http://www.zentao.net
- */
- class metricModel extends model
- {
- public $errorInfo = '';
- /**
- * 获取度量数据表的表头。
- * Get header of result table in view.
- *
- * @param object $metric
- * @access public
- * @return array
- */
- public function getViewTableHeader($metric)
- {
- $dataFields = $this->getMetricRecordDateField($metric);
- $dateType = $this->getDateTypeByCode($metric->code);
- $dataFields[] = 'id';
- $dataFields[] = 'value';
- $dataFields[] = 'date';
- $dataFieldStr = implode(',', $dataFields);
- $header = array();
- if($metric->scope != 'system')
- {
- $scope = $metric->scope;
- $header[] = array('name' => 'scope', 'title' => $this->lang->metric->tableHeader[$scope], 'width' => 159);
- }
- if($dateType != 'nodate') $header[] = array('name' => 'date', 'title' => $this->lang->metric->date, 'width' => 96);
- $header[] = array('name' => 'value', 'title' => $this->lang->metric->value, 'width' => 96);
- $header[] = array('name' => 'calcTime', 'title' => $this->lang->metric->calcTime, 'width' => 128);
- return $header;
- }
- /**
- * 获取度量数据表的数据。
- * Get data of result table.
- *
- * @param object $metric
- * @param array $result
- * @access public
- * @return array|false
- */
- public function getViewTableData($metric, $result)
- {
- if(empty($result)) return array();
- $scope = $metric->scope;
- $dateType = $this->getDateTypeByCode($metric->code);
- if($scope != 'system') $objectPairs = $this->getPairsByScope($scope);
- $tableData = array();
- foreach($result as $record)
- {
- $record = (array)$record;
- if(isset($record['date'])) $record['calcTime'] = date("Y-m-d H:i", strtotime($record['date']));
- $row = $this->buildDateCell($record, $dateType);
- if($scope != 'system')
- {
- $row->scope = isset($objectPairs[$record[$scope]]) ? $objectPairs[$record[$scope]] : $record[$scope];
- $row->scopeID = $record[$scope];
- }
- $row->value = is_numeric($record['value']) ? round((float)$record['value'], 2) : $record['value'];
- $row->calcType = zget($record, 'calcType', '');
- $row->calculatedBy = zget($record, 'calculatedBy', '');
- $tableData[] = $row;
- }
- return $tableData;
- }
- /**
- * 对 headers 进行分组满足表头合并单元格, 返回经过分组的 headers 和 data。
- * Return grouped headers and data for table headers merges cell.
- *
- * @param array $header
- * @param array $data
- * @param bool $withCalcTime
- * @access public
- * @return array
- */
- public function getGroupTable($header, $data, $dateType, $withCalcTime = true)
- {
- if(!$header or !$data) return array(array(), array());
- $headerLength = count($header);
- if($headerLength == 2)
- {
- return $this->getTimeTable($data, $dateType, $withCalcTime); // $dateType should eq nodate
- }
- elseif($headerLength == 3)
- {
- if($this->isObjectMetric($header))
- {
- return $this->getObjectTable($header, $data, $dateType, $withCalcTime); // $dateType should eq nodate
- }
- else
- {
- return $this->getTimeTable($data, $dateType, $withCalcTime);
- }
- }
- elseif($headerLength == 4)
- {
- return $this->getObjectTable($header, $data, $dateType, $withCalcTime);
- }
- return array($header, $data);
- }
- /**
- * 构建有时间属性的度量项的表格数据。
- * Build table data for metric with time.
- *
- * @param array $data
- * @param string $dateType
- * @param bool $withCalcTime
- * @access public
- * @return array
- */
- public function getTimeTable($data, $dateType = 'day', $withCalcTime = true)
- {
- usort($data, function($a, $b) use ($dateType)
- {
- if($dateType == 'week')
- {
- list($yearA, $weekA) = explode('-', $a->dateString);
- list($yearB, $weekB) = explode('-', $b->dateString);
- list($firstDayOfWeekA, $lastDayOfWeekA) = $this->getStartAndEndOfWeek($yearA, $weekA, 'date');
- list($firstDayOfWeekB, $lastDayOfWeekB) = $this->getStartAndEndOfWeek($yearB, $weekB, 'date');
- $dateA = strtotime($firstDayOfWeekA);
- $dateB = strtotime($firstDayOfWeekB);
- }
- else
- {
- $dateA = strtotime($a->dateString);
- $dateB = strtotime($b->dateString);
- }
- if ($dateA == $dateB) return 0;
- return ($dateA > $dateB) ? -1 : 1;
- });
- $groupHeader = array();
- $groupHeader[] = array('name' => 'date', 'title' => zget($this->lang->metric, $dateType, ''), 'align' => 'center', 'width' => 96);
- $groupHeader[] = array('name' => 'value', 'title' => $this->lang->metric->value, 'align' => 'center', 'width' => 68);
- $groupData = array();
- $users = $this->loadModel('user')->getPairs('noletter');
- foreach($data as $dataInfo)
- {
- $value = $withCalcTime ? array($dataInfo->value, $dataInfo->calcTime, $dataInfo->calcType, zget($users, $dataInfo->calculatedBy)) : $dataInfo->value;
- $date = isset($dataInfo->date) ? $dataInfo->date : $dataInfo->dateString;
- $dataSeries = array('date' => $date, 'value' => $value);
- $groupData[] = $dataSeries;
- }
- return array($groupHeader, $groupData);
- }
- /**
- * 构建有对象属性的度量项的表格数据。
- * Build table data for metric with object.
- *
- * @param array $data
- * @param string $dateType
- * @param bool $withCalcTime
- * @access public
- * @return array
- */
- public function getObjectTable($header, $data, $dateType = 'day', $withCalcTime = true)
- {
- $groupHeader = array();
- $groupData = array();
- $headerField = current($header)['name'];
- $headerTitle = current($header)['title'];
- $groupHeader[] = array('name' => $headerField, 'title' => $headerTitle, 'fixed' => 'left', 'width' => 128);
- $dateField = 'dateString';
- usort($data, function($a, $b) use($dateField)
- {
- $dateA = strtotime($a->$dateField);
- $dateB = strtotime($b->$dateField);
- if ($dateA == $dateB) {
- return 0;
- }
- return ($dateA > $dateB) ? -1 : 1;
- });
- $times = array();
- $objects = array();
- $users = $this->loadModel('user')->getPairs('noletter');
- foreach($data as $dataInfo)
- {
- $time = substr($dataInfo->$dateField, 0, 10);
- $calcTime = $dataInfo->calcTime;
- $object = $dataInfo->scope;
- $value = $dataInfo->value;
- $calcType = $dataInfo->calcType;
- $calculatedBy = zget($users, $dataInfo->calculatedBy);
- if(!isset($times[$time])) $times[$time] = $time;
- if(!isset($objects[$object])) $objects[$object] = array();
- $objects[$object][$time] = $withCalcTime ? array($value, $calcTime, $calcType, $calculatedBy) : $value;
- }
- /* e.g $times = array('2023-10-14', '2023-10-15'), $objects = array('object1' => array('2023-10-14' => 2, '2023-10-15 => 3)) */
- $numberHeaderWidth = 68;
- foreach($times as $time)
- {
- $header = array('name' => $time, 'align' => 'center', 'width' => $numberHeaderWidth);
- if($dateType == 'year')
- {
- $header['title'] = sprintf($this->lang->metric->yearFormat, $time);
- }
- elseif($dateType == 'month')
- {
- list($year, $month) = explode('-', $time);
- $header['title'] = $this->lang->datepicker->monthNames[(int)$month - 1];
- $header['headerGroup'] = sprintf($this->lang->metric->yearFormat, $year);
- }
- elseif($dateType == 'week')
- {
- list($year, $week) = explode('-', $time);
- $header['title'] = sprintf($this->lang->metric->weekFormat, $week);
- $header['headerGroup'] = sprintf($this->lang->metric->yearFormat, $year);
- }
- elseif($dateType == 'day' or $dateType == 'nodate')
- {
- list($year, $month, $day) = explode('-', $time);
- $header['title'] = sprintf($this->lang->metric->monthDayFormat, $month, $day);
- $header['headerGroup'] = sprintf($this->lang->metric->yearFormat, $year);
- }
- $groupHeader[] = $header;
- }
- foreach($objects as $object => $datas)
- {
- $objectData = array($headerField => $object);
- foreach($times as $time) $objectData[$time] = isset($datas[$time]) ? $datas[$time] : 0;
- $groupData[] = $objectData;
- }
- return array($groupHeader, $groupData);
- }
- /**
- * 获取所有范围的键值对。
- * Get scope pairs.
- *
- * @param bool $all
- * @access public
- * @return array
- */
- public function getScopePairs($all = true)
- {
- $metrics = $all ? array() : $this->metricTao->fetchMetricsByScope($this->config->metric->scopeList);
- $scopePairs = array();
- foreach($this->lang->metric->scopeList as $scope => $name)
- {
- if(!$all && !isset($metrics[$scope])) continue;
- $scopePair = new stdclass();
- $scopePair->value = $scope;
- $scopePair->label = $name;
- $scopePairs[] = $scopePair;
- }
- return $scopePairs;
- }
- /**
- * 获取度量项数据列表。
- * Get metric data list.
- *
- * @param string $scope
- * @param string $stage
- * @param int $param
- * @param string $type
- * @param int $queryID
- * @param string $sort
- * @param object $pager
- * @access public
- * @return array|false
- */
- public function getList($scope, $stage = 'all', $param = 0, $type = '', $queryID = 0, $sort = 'id_desc', $pager = null)
- {
- if($queryID)
- {
- $query = $this->loadModel('search')->getQuery($queryID);
- if($query)
- {
- $this->session->set('metricQuery', $query->sql);
- $this->session->set('metricForm', $query->form);
- }
- else
- {
- $this->session->set('metricQuery', ' 1 = 1');
- }
- }
- $object = null;
- $purpose = null;
- if($type == 'byTree')
- {
- $object_purpose = explode('_', $param);
- $object = $object_purpose[0];
- if(count($object_purpose) == 2) $purpose = $object_purpose[1];
- }
- $query = $type == 'bysearch' ? $this->session->metricQuery : '';
- $metrics = $this->metricTao->fetchMetrics($scope, $stage, $object, $purpose, $query, $sort, $pager);
- $metrics = $this->processOldMetrics($metrics);
- return $metrics;
- }
- /**
- * 以对象为维度分组度量项。
- * Group metrics by object.
- *
- * @param array $metrics
- * @access public
- * @return array
- */
- public function groupMetricByObject($metrics)
- {
- $groupMetrics = array_fill_keys(array_keys($this->lang->metric->objectList), array());
- foreach($metrics as $metric)
- {
- $group = isset($groupMetrics[$metric->object]) ? $metric->object : 'other';
- $groupMetrics[$group][] = $metric;
- }
- $purposes = array_keys($this->lang->metric->purposeList);
- foreach($groupMetrics as $key => $metrics)
- {
- if(empty($metrics))
- {
- unset($groupMetrics[$key]);
- continue;
- }
- usort($metrics, function($a, $b) use ($purposes) {
- $aIndex = array_search($a->purpose, $purposes);
- $bIndex = array_search($b->purpose, $purposes);
- if($aIndex === $bIndex) return 0;
- return ($aIndex < $bIndex) ? -1 : 1;
- });
- $groupMetrics[$key] = $metrics;
- }
- return $groupMetrics;
- }
- /**
- * 根据id列表获取度量项列表。
- * Get metrics by id list.
- *
- * @param array $metricIDList
- * @access public
- * @return array|false
- */
- public function getMetricsByIDList($metricIDList)
- {
- return $this->metricTao->fetchMetricsByIDList($metricIDList);
- }
- /**
- * 根据code列表获取度量项列表。
- * Get metrics by code list.
- *
- * @param array $codeList
- * @access public
- * @return array|false
- */
- public function getMetricsByCodeList($codeList)
- {
- return $this->metricTao->fetchMetricsByCodeList($codeList);
- }
- /**
- * 获取度量库数据的收集方式和采集人。
- * Get calculate type and calculate people by metric record id.
- *
- * @param int $recordID
- * @access public
- * @return object|false
- */
- public function getRecordCalcInfo($recordID)
- {
- return $this->dao->select('calcType, calculatedBy')->from(TABLE_METRICLIB)->where('id')->eq($recordID)->fetch();
- }
- /**
- * 获取旧度量项列表。
- * Get old metric list.
- *
- * @param string $orderBy
- * @access public
- * @return array
- */
- public function getOldMetricList($orderBy = 'id_desc')
- {
- return $this->dao->select('*')->from(TABLE_BASICMEAS)->where('deleted')->eq(0)->orderby($orderBy)->fetchAll('id');
- }
- /**
- * 通过筛选器筛选度量项。
- * Get metric list by filters.
- *
- * @param array $filters
- * @param string $stage
- * @access public
- * @return array|false
- */
- public function getListByFilter($filters, $stage)
- {
- return $this->metricTao->fetchMetricsWithFilter($filters, $stage);
- }
- /**
- * 通过我的收藏筛选度量项。
- * Get metric list by collect.
- *
- * @param string $stage
- * @access public
- * @return array|false
- */
- public function getListByCollect($stage)
- {
- return $this->metricTao->fetchMetricsByCollect($stage);
- }
- /**
- * 获取模块树数据。
- * Get module tree data.
- *
- * @param string $scope
- * @access public
- * @return void
- */
- public function getModuleTreeList($scope)
- {
- return $this->metricTao->fetchModules($scope);
- }
- /**
- * 根据代号获取度量项信息。
- * Get metric info by code.
- *
- * @param string $code
- * @param string|array $fieldList
- * @access public
- * @return object|false
- */
- public function getByCode($code, $fieldList = '*')
- {
- static $metricsWithCode;
- if(is_array($fieldList)) $fieldList = implode(',', $fieldList);
- if(!isset($metricsWithCode[$code . $fieldList])) $metricsWithCode[$code . $fieldList] = $this->fetchMetricByCode($code, $fieldList);
- return $metricsWithCode[$code . $fieldList];
- }
- /**
- * 根据ID获取度量项信息。
- * Get metric info by id.
- *
- * @param int $metricID
- * @param string|array $fieldList
- * @access public
- * @return object|false
- */
- public function getByID($metricID, $fieldList = '*')
- {
- if($metricID === 0) return false;
- if(is_array($fieldList)) $fieldList = implode(',', $fieldList);
- $metric = $this->dao->select($fieldList)->from(TABLE_METRIC)->where('id')->eq($metricID)->fetch();
- if(!$metric) return false;
- if($this->isOldMetric($metric))
- {
- $oldMetric = $this->getOldMetricByID($metric->fromID);
- $metric->sql = $oldMetric->configure;
- $metric->params = $oldMetric->params;
- $metric->oldUnit = $oldMetric->unit;
- $metric->collectType = $oldMetric->collectType;
- $metric->collectConf = $oldMetric->collectConf;
- $metric->execTime = $oldMetric->execTime;
- }
- if(empty($metric->dateType) and isset($metric->code)) $metric->dateType = $this->getDateTypeByCode($metric->code);
- return $metric;
- }
- /**
- * 根据ID获取旧版度量项信息。
- * Get old metric info by id.
- *
- * @param int $measurementID
- * @access public
- * @return object|false
- */
- public function getOldMetricByID($measurementID)
- {
- $measurement = $this->dao->select('*')->from(TABLE_BASICMEAS)->where('id')->eq($measurementID)->fetch();
- if(!$measurement) return false;
- if($measurement->collectType == 'action')
- {
- $collectConf = json_decode($measurement->collectConf);
- $measurement->collectConf = new stdclass();
- $measurement->collectConf->action = $collectConf->action;
- $measurement->collectConf->type = '';
- $measurement->collectConf->week = '';
- }
- else
- {
- $measurement->collectConf = json_decode($measurement->collectConf);
- if(is_object($measurement->collectConf))
- {
- $measurement->collectConf->module = '';
- $measurement->collectConf->action = '';
- }
- }
- return $measurement;
- }
- /**
- * 获取dao对象。
- * Get dao object.
- *
- * @access public
- * @return object
- */
- public function getDAO()
- {
- $dbType = $this->config->metricDB->type;
- if($dbType === 'sqlite')
- {
- $dao = clone $this->dao;
- $dao->reset();
- $dao->dbh = $this->app->connectSqlite();
- $dao->driver = 'sqlite';
- return $dao;
- }
- return $this->dao;
- }
- /**
- * 获取度量项数据源句柄。
- * Get data source statement of calculator.
- *
- * @param object $calculator
- * @param string $returnType
- * @access public
- * @return PDOStatement|string
- */
- public function getDataStatement($calculator, $returnType = 'statement', $vision = 'rnd')
- {
- $dao = $this->getDAO();
- $statement = null;
- if(!empty($calculator->dataset))
- {
- include_once $this->getDatasetPath();
- $dataset = new dataset($dao, $this->config, $vision);
- $dataSource = $calculator->dataset;
- $fieldList = implode(',', $calculator->fieldList);
- $statement = $dataset->$dataSource($fieldList);
- }
- if($calculator->useSCM)
- {
- $calculator->setDAO($dao);
- $scm = $this->app->loadClass('scm');
- $calculator->setSCM($scm);
- }
- if($returnType == 'sql') return $statement->get();
- return $statement;
- }
- /**
- * 获取度量数据的日期字段。
- * Get date field of metric data.
- *
- * @param object $metric
- * @access protected
- * @return array
- */
- protected function getMetricRecordDateField($metric)
- {
- $dataFields = array();
- $dateType = $this->getDateTypeByCode($metric->code);
- if($dateType != 'nodate') $dataFields[] = 'year';
- if($dateType == 'month') $dataFields[] = 'month';
- if($dateType == 'week') $dataFields[] = 'week';
- if($dateType == 'day')
- {
- $dataFields[] = 'month';
- $dataFields[] = 'day';
- }
- if($metric->scope != 'system') $dataFields[] = $metric->scope;
- return $dataFields;
- }
- /**
- * 设置默认的度量数据筛选参数。
- * Set default options of metric data.
- *
- * @param array $options
- * @param array $dataFields
- * @access public
- * @return array
- */
- public function setDefaultOptions($options, $dataFields)
- {
- if(!empty($options)) return $options;
- $dateType = $this->getDateType($dataFields);
- if($dateType != 'nodate')
- {
- $dateLabels = $this->getDateLabels($dateType);
- $defaultDate = $this->getDefaultDate($dateLabels);
- $options = array('dateType' => $dateType, 'dateLabel' => $defaultDate);
- }
- return $options;
- }
- /**
- * 根据代号计算度量项。
- * Calculate metric by code.
- *
- * @param string $code
- * @access public
- * @return void
- */
- public function calculateMetricByCode($code)
- {
- $metric = $this->getByCode($code);
- if(!$metric) return false;
- $calculator = $this->getCalculator($metric->scope, $metric->purpose, $metric->code);
- $this->calculateDefaultMetric($calculator, $this->config->vision);
- return $calculator;
- }
- /**
- * 获取数组格式的度量项结果。
- * Get result by code with array format.
- *
- * @param int $code
- * @param array $options
- * @param string $type
- * @param int $pager
- * @param string $vision
- * @access public
- * @return void
- */
- public function getResultByCodeWithArray($code, $options = array(), $type = 'realtime', $pager = null, $vision = 'rnd')
- {
- $metric = $this->getByCode($code);
- if(!$metric) return array();
- $dataFields = $this->getMetricRecordDateField($metric);
- $result = array();
- if($vision == 'rnd')
- {
- $records = $this->metricTao->fetchMetricRecordsWithOption($code, $dataFields, $options, $pager);
- if(empty($records)) return array();
- foreach($records as $index => $record)
- {
- $record = (array)$record;
- $record['value'] = (float)$record['value'];
- $result[] = $record;
- }
- return $result;
- }
- return $this->getResultByCode($code, $options, 'realtime', $pager, $vision);
- }
- /**
- * 合并度量数据。
- * Merge record.
- *
- * @param array $record
- * @param array $result
- * @access public
- * @return array
- */
- public function mergeRecord($record, $result)
- {
- $uniqueKey = $this->getUniqueKeyByRecord($record);
- if(isset($result[$uniqueKey]))
- {
- $result[$uniqueKey]['value'] += $record['value'];
- return $result;
- }
- $result[$uniqueKey] = $record;
- return $result;
- }
- /**
- * 根据度量数据的字段生成唯一键。
- * Get unique key by record field.
- *
- * @param array $record
- * @access public
- * @return string
- */
- public function getUniqueKeyByRecord($record)
- {
- $record = (array)$record;
- $uniqueKeys = array();
- $ignoreLibFields = $this->config->metric->ignoreLibFields;
- foreach($record as $field => $value)
- {
- if(in_array($field, $ignoreLibFields) || empty($value)) continue;
- $uniqueKeys[] = $field . $value;
- }
- return empty($uniqueKeys) ? 'none' : implode('_', $uniqueKeys);
- }
- /**
- * 根据代号获取计算实时度量项的结果。
- * Get result of calculate metric by code.
- *
- * @param string $code
- * @param array $options e.g. array('product' => '1,2,3', 'year' => '2023')
- * @param string $type
- * @param object|null $pager
- * @access public
- * @return array
- */
- public function getResultByCode($code, $options = array(), $type = 'realtime', $pager = null, $vision = 'rnd')
- {
- if($type == 'cron')
- {
- $metric = $this->getByCode($code);
- if(!empty($metric))
- {
- $dataFields = $this->getMetricRecordDateField($metric);
- $options = $this->setDefaultOptions($options, $dataFields);
- return $this->metricTao->fetchMetricRecords($code, $dataFields, $options, $pager);
- }
- }
- $metric = $this->getByCode($code);
- if(!$metric) return false;
- $calculator = $this->getCalculator($metric->scope, $metric->purpose, $metric->code);
- if(!$calculator) return array();
- /* 因为是单个度量项的计算,所以需要优先查看是否支持可用的性能优化,如果有的话,使用性能优化方式计算。*/
- /* Because this is a single metric calculation, it is important to first look to see if any performance optimizations are supported and, if so, use them. */
- $calculated = false;
- $calculated += $this->calculateReuseMetric($calculator, $options, $type, $pager, $vision);
- $calculated += $this->calculateSingleMetric($calculator, $vision);
- /* 如果没有可用的性能优化方式,那么使用默认的方式计算。*/
- /* If no optimizations are available, the default calculation is used. */
- if(!$calculated) $this->calculateDefaultMetric($calculator, $vision);
- $records = $calculator->getResult($options);
- if(!empty($records))
- {
- $records = json_decode(json_encode($records), true);
- $time = helper::now();
- foreach($records as $index => $record)
- {
- $records[$index]['date'] = $time;
- $records[$index]['calcType'] = 'cron';
- $records[$index]['calculatedBy'] = 'system';
- }
- }
- return $records;
- }
- /**
- * 根据度量项和数据计算度量项结果。
- * Calculate metric result by metrics and data.
- *
- * @param array $metrics
- * @param array $data
- * @access public
- * @return void
- */
- public function getResultByCodeFromData($metrics, $data)
- {
- $results = array();
- foreach($metrics as $metric)
- {
- $calculator = $this->getCalculator($metric->scope, $metric->purpose, $metric->code);
- foreach($data as $row) $calculator->calculate($row);
- $results[$metric->code] = $calculator->getResult();
- }
- return $results;
- }
- /**
- * 获取度量项计算对象。
- * Get metric calculator.
- *
- * @param string $scope
- * @param string $purpose
- * @param string $code
- * @access public
- * @return object
- */
- public function getCalculator($scope, $purpose, $code)
- {
- $calcPath = $this->getCalcRoot() . $scope . DS . $purpose . DS . $code . '.php';
- if(!is_file($calcPath)) return false;
- include_once $this->getBaseCalcPath();
- include_once $calcPath;
- $calculator = new $code();
- $calculator->setHolidays($this->loadModel('holiday')->getList());
- $calculator->setWeekend(isset($this->config->project->weekend) ? $this->config->project->weekend : 2);
- if($calculator->useSCM && ($this->config->inQuickon || $this->config->inCompose)) $calculator->setGitFoxRepos($this->loadModel('repo')->getGitFoxRepos());
- return $calculator;
- }
- /**
- * 计算重用度量项。
- * Calculate reuse metric.
- *
- * @param object $calculator
- * @param array $options
- * @param string $type
- * @param object $pager
- * @param string $vision
- * @access public
- * @return bool
- */
- public function calculateReuseMetric($calculator, $options, $type, $pager, $vision)
- {
- if(!$calculator->reuse) return false;
- $reuseMetrics = array();
- foreach($calculator->reuseMetrics as $key => $reuseMetric)
- {
- $reuseMetrics[$key] = $this->getResultByCode($reuseMetric, $options, $type, $pager, $vision);
- }
- $calculator->calculate($reuseMetrics);
- return true;
- }
- /**
- * 计算可独立计算度量项。
- * Calculate single metric.
- *
- * @param object $calculator
- * @param string $vision
- * @access public
- * @return bool
- */
- public function calculateSingleMetric($calculator, $vision)
- {
- if(!$calculator->supportSingleQuery) return false;
- $sql = $this->getDataStatement($calculator, 'sql', $vision);
- $calculator->setDAO($this->getDAO());
- $calculator->setSingleSql($sql);
- $calculator->enableSingleQuery();
- return true;
- }
- /**
- * 计算普通度量项。
- * Calculate default metric.
- *
- * @param object $calculator
- * @param string $vision
- * @access public
- * @return void
- */
- public function calculateDefaultMetric($calculator, $vision)
- {
- $statement = $this->getDataStatement($calculator, 'statement', $vision);
- $dbType = $this->config->metricDB->type;
- if($dbType == 'duckdb')
- {
- $this->loadModel('bi');
- $sql = $statement->get();
- $dbh = $this->app->loadDriver('duckdb');
- $statement = $dbh->query($sql);
- }
- if($statement) $rows = $statement->fetchAll('', false);
- if(!empty($rows)) foreach($rows as $row) $calculator->calculate($row);
- }
- /**
- * 根据代号获取计算最新度量项的结果。
- * Get result of latest calculate metric by code.
- *
- * @param string $code
- * @param array $options e.g. array('product' => '1,2,3', 'year' => '2023')
- * @param string $type
- * @param object|null $pager
- * @access public
- * @return array
- */
- public function getLatestResultByCode($code, $options = array(), $pager = null, $vision = 'rnd')
- {
- $metric = $this->getByCode($code);
- $dataFields = $this->getMetricRecordDateField($metric);
- $options = $this->setDefaultOptions($options, $dataFields);
- return $this->metricTao->fetchLatestMetricRecords($code, $dataFields, $options, $pager);
- }
- /**
- * 根据代号列表批量获取度量项的结果。
- * Get result of calculate metric by code list.
- *
- * @param array $codes e.g. array('code1', 'code2')
- * @param array $options e.g. array('product' => '1,2,3', 'year' => '2023')
- * @access public
- * @return array
- */
- public function getResultByCodes($codes, $options = array(), $vision = 'rnd')
- {
- $results = array();
- foreach($codes as $code)
- {
- $result = $this->getResultByCode($code, $options, 'realtime', null, $vision);
- if($result) $results[$code] = $result;
- }
- return $results;
- }
- /**
- * 获取可计算的度量项列表。
- * Get executable metric list.
- *
- * @access public
- * @return array
- */
- public function getExecutableMetric($includes = 'all')
- {
- return $this->dao->select('id, code')->from(TABLE_METRIC)
- ->where('deleted')->eq('0')
- ->beginIF(is_array($includes))->andWhere('code')->in($includes)->fi()
- ->fetchPairs();
- }
- /**
- * 删除度量数据中的重复数据。
- * Delete duplication record in metric data.
- *
- * @param string $code
- * @access public
- * @return bool
- */
- public function deduplication($code)
- {
- $metric = $this->getByCode($code);
- $fields = $this->getRecordFields($code);
- if(!in_array($metric->scope, $fields)) $fields[] = $metric->scope;
- if(empty($fields)) return false;
- $this->metricTao->setDeleted($code, 1);
- $this->metricTao->keepLatestRecords($code, $fields);
- $this->metricTao->executeDelete($code);
- return dao::isError();
- }
- /**
- * 获取日志文件路径。
- * Get log file.
- *
- * @access public
- * @return string
- */
- public function getLogFile()
- {
- return $this->app->getTmpRoot() . 'log/metriclib.' . date('Ymd') . '.log.php';
- }
- /**
- * 存储日志。
- * Save logs.
- *
- * @param string $log
- * @access public
- * @return void
- */
- public function saveLogs($log)
- {
- $logFile = $this->getLogFile();
- $log = date('Y-m-d H:i:s') . ' ' . trim($log) . "\n";
- if(!file_exists($logFile)) $log = "<?php\ndie();\n?" . ">\n" . $log;
- file_put_contents($logFile, $log, FILE_APPEND);
- }
- /**
- * 获取度量数据有效字段。
- * Get metric record fields.
- *
- * @param string $code
- * @access public
- * @return array|false
- */
- public function getRecordFields($code)
- {
- $metric = $this->getByCode($code);
- $dateType = $this->getDateTypeByCode($code);
- $fields = array($metric->scope);
- if($dateType == 'nodate') return $fields;
- $fields[] = 'year';
- if($dateType == 'month' || $dateType == 'day') $fields[] = 'month';
- if($dateType == 'week') $fields[] = 'week';
- if($dateType == 'day') $fields[] = 'day';
- return $fields;
- }
- /**
- * 重建主键顺序。
- * Rebuild primary key order.
- *
- * @access public
- * @return void
- */
- public function rebuildPrimaryKey()
- {
- $this->metricTao->rebuildIdColumn();
- }
- /**
- * 根据度量项收集周期来清理过期的度量库数据。
- * Clear outdated metric records by cycle.
- *
- * @param string $code
- * @param string $cycle
- * @access public
- * @return array
- */
- public function clearOutDatedRecords($code, $cycle)
- {
- $year = date('Y');
- $month = date('n');
- $week = date('W');
- $day = date('j');
- if($cycle == 'year')
- {
- $this->dao->delete()->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('year')->eq($year)
- ->exec();
- }
- elseif($cycle == 'month')
- {
- $this->dao->delete()->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('year')->eq($year)
- ->andWhere('month')->eq($month)
- ->exec();
- }
- elseif($cycle == 'week')
- {
- $this->dao->delete()->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('year')->eq($year)
- ->andWhere('week')->eq($week)
- ->exec();
- }
- elseif($cycle == 'day')
- {
- $this->dao->delete()->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('year')->eq($year)
- ->andWhere('month')->eq($month)
- ->andWhere('day')->eq($day)
- ->exec();
- }
- }
- /**
- * 插入度量库数据。
- * Insert into metric lib.
- *
- * @param array $recordWithCode
- * @param string $calcType
- * @access public
- * @return void
- */
- public function insertMetricLib($recordWithCode, $calcType = 'cron')
- {
- foreach($recordWithCode as $code => $records)
- {
- $schema = '';
- $values = array();
- foreach($records as $record)
- {
- if(empty($record)) continue;
- $record->calcType = $calcType;
- $record->calculatedBy = $calcType == 'inference' ? $this->app->user->account : 'system';
- $sql = $this->dao->insert(TABLE_METRICLIB)->data($record)->get();
- $position = strpos($sql, 'VALUES');
- if(empty($schema)) $schema .= substr($sql, 0, $position + 6);
- $values[] = substr($sql, $position + 6);
- }
- $rows = count($records);
- $time = helper::now();
- $this->dao->update(TABLE_METRIC)
- ->set('lastCalcRows')->eq($rows)
- ->set('lastCalcTime')->eq($time)
- ->where('code')->eq($code)
- ->exec();
- while($rows > 0)
- {
- $sql = $schema . implode(',', array_splice($values, 0, 10000));
- $this->dao->exec($sql);
- $rows -= 10000;
- }
- }
- return dao::isError();
- }
- /**
- * 获取可计算的度量项对象列表。
- * Get executable calculator list.
- *
- * @access public
- * @return array
- */
- public function getExecutableCalcList($includes = 'all')
- {
- $funcRoot = $this->getCalcRoot();
- $fileList = array();
- foreach($this->config->metric->scopeList as $scope)
- {
- foreach($this->config->metric->purposeList as $purpose)
- {
- $pattern = $funcRoot . $scope . DS . $purpose . DS . '*.php';
- $matchedFiles = glob($pattern);
- if($matchedFiles !== false) $fileList = array_merge($fileList, $matchedFiles);
- }
- }
- $calcList = array();
- $excutableMetric = $this->getExecutableMetric($includes);
- foreach($fileList as $file)
- {
- $code = rtrim(basename($file), '.php');
- if(!in_array($code, $excutableMetric)) continue;
- $id = array_search($code, $excutableMetric);
- $calc = new stdclass();
- $calc->code = $code;
- $calc->file = $file;
- $calcList[$id] = $calc;
- }
- return $calcList;
- }
- /**
- * 获取度量项计算实例列表。
- * Get calculator instance list.
- *
- * @access public
- * @return array
- */
- public function getCalcInstanceList($includes = 'all')
- {
- $calcList = $this->getExecutableCalcList($includes);
- include_once $this->getBaseCalcPath();
- $calcInstances = array();
- foreach($calcList as $id => $calc)
- {
- $file = $calc->file;
- $className = $calc->code;
- require_once $file;
- $metricInstance = new $className();
- $metricInstance->id = $id;
- $calcInstances[$className] = $metricInstance;
- }
- return $this->filterCalcByEdition($calcInstances);
- }
- /**
- * 获取通用数据集对象。
- * Get instance of data set object.
- *
- * @access public
- * @return dataset
- */
- public function getDataset($dao)
- {
- $datasetPath = $this->getDatasetPath();
- include_once $datasetPath;
- return new dataset($dao, $this->config);
- }
- /**
- * 对度量项按照通用数据集进行归类,没有数据集不做归类。
- * Classify calculator instance list by its data set.
- *
- * @param array $calcList
- * @access public
- * @return array
- */
- public function classifyCalc($calcList)
- {
- $datasetCalcGroup = array();
- $otherCalcList = array();
- foreach($calcList as $code => $calc)
- {
- if(empty($calc->dataset))
- {
- $otherCalcList[$code] = $calc;
- continue;
- }
- $dataset = $calc->dataset;
- if(!isset($datasetCalcGroup[$dataset])) $datasetCalcGroup[$dataset] = array();
- $datasetCalcGroup[$dataset][$code] = $calc;
- }
- $classifiedCalcGroup = array();
- foreach($datasetCalcGroup as $dataset => $calcList) $classifiedCalcGroup[] = (object)array('dataset' => $dataset, 'calcList' => $calcList);
- foreach($otherCalcList as $code => $calc) $classifiedCalcGroup[] = (object)array('dataset' => '', 'calcList' => array($code => $calc));
- return $classifiedCalcGroup;
- }
- /**
- * 对度量项的字段列表取并集。
- * Unite field list of each calculator.
- *
- * @param array $calcList
- * @access public
- * @return string
- */
- public function uniteFieldList($calcList)
- {
- $fieldList = array();
- foreach($calcList as $calcInstance) $fieldList = array_merge($fieldList, $calcInstance->fieldList);
- $uniqueList = array_unique($fieldList);
- $aliasList = array();
- foreach($uniqueList as $field)
- {
- if(strpos(strtoupper($field), ' AS ') !== false)
- {
- $aliasList[] = $field;
- continue;
- }
- if(strpos($field, '.') !== false)
- {
- $alias = str_replace('.', '_', $field);
- list($table, $field) = explode('.', $field);
- $aliasList[] = "`$table`.`$field` AS `$alias`";
- continue;
- }
- $aliasList[] = "`$field`";
- }
- return implode(',', $aliasList);
- }
- /**
- * Build search form.
- *
- * @param int $queryID
- * @param string $actionURL
- * @access public
- * @return void
- */
- public function buildSearchForm($queryID, $actionURL)
- {
- $this->config->metric->browse->search['actionURL'] = $actionURL;
- $this->config->metric->browse->search['queryID'] = $queryID;
- $this->loadModel('search')->setSearchParams($this->config->metric->browse->search);
- }
- /**
- * 为度量详情页构建操作按钮
- * Build operate menu.
- *
- * @param object $metric
- * @access public
- * @return array
- */
- public function buildOperateMenu($metric)
- {
- $menuList = array
- (
- 'main' => array(),
- 'suffix' => array()
- );
- if($metric->builtin === '0')
- {
- $stage = $metric->stage;
- if($stage == 'wait' and common::haspriv('metric', 'edit'))
- {
- $editAction = $this->config->metric->actionList['edit'];
- $editAction['data-toggle'] = 'modal';
- $editAction['url'] = helper::createLink('metric', 'edit', "metricID={$metric->id}&viewType=view");
- $menuList['suffix']['edit'] = $editAction;
- }
- if($stage == 'wait' and common::haspriv('metric', 'implement') and !$this->isOldMetric($metric))
- {
- $menuList['main']['implement'] = $this->config->metric->actionList['implement'];
- }
- if($stage != 'wait' and common::haspriv('metric', 'delist'))
- {
- $menuList['main']['delist'] = $this->config->metric->actionList['delist'];
- }
- if(common::haspriv('metric', 'delete'))
- {
- $deleteAction = $this->config->metric->actionList['delete'];
- if(isset($metric->isUsed) && $metric->isUsed) $deleteAction['data-confirm'] = $this->lang->metric->confirmDeleteInUsed;
- $menuList['suffix']['delete'] = $deleteAction;
- }
- }
- if($metric->stage == 'released' && !empty($metric->dateType) && $metric->dateType != 'nodate' && common::haspriv('metric', 'recalculate'))
- {
- $menuList['main']['recalculate'] = $this->config->metric->actionList['recalculate'];
- $menuList['main']['recalculate']['text'] = $this->lang->metric->recalculateBtnText;
- $menuList['main']['recalculate']['hint'] = $this->lang->metric->recalculateBtnText;
- }
- return $menuList;
- }
- /**
- * 获取瀑布范围的瀑布对象列表。
- * Get object pairs by scope.
- *
- * @param string $vision
- * @access public
- * @return array
- */
- public function getWaterfullProjectPairs($vision = 'rnd')
- {
- return $this->dao->select('id, name')->from(TABLE_PROJECT)
- ->where('deleted')->eq(0)
- ->andWhere('type')->eq('project')
- ->andWhere('model')->in(array('waterfall', 'waterfallplus'))
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- }
- /**
- * 获取范围的对象列表。
- * Get object pairs by scope.
- *
- * @param string $scope
- * @param bool $withHierarchy
- * @access public
- * @return array
- */
- public function getPairsByScope($scope, $withHierarchy = false, $vision = 'rnd')
- {
- if(empty($scope) || $scope == 'system') return array();
- if($scope == 'dept' && $withHierarchy) $scope = 'deptWithHierarchy';
- if($scope == 'program' && $withHierarchy) $scope = 'programWithHierarchy';
- $objectPairs = array();
- switch($scope)
- {
- case 'dept':
- $objectPairs = $this->loadModel('dept')->getDeptPairs();
- break;
- case 'deptWithHierarchy':
- $objectPairs = $this->loadModel('dept')->getOptionMenu();
- break;
- case 'user':
- $objectPairs = $this->loadModel('user')->getPairs('noletter|noempty|noclosed');
- break;
- case 'program':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PROGRAM)
- ->where('deleted')->eq(0)
- ->andWhere('type')->eq('program')
- ->fetchPairs();
- break;
- case 'programWithHierarchy':
- $objectPairs = $this->loadModel('program')->getParentPairs();
- break;
- case 'product':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PRODUCT)
- ->where('deleted')->eq(0)
- ->andWhere('shadow')->eq(0)
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'project':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PROJECT)
- ->where('deleted')->eq(0)
- ->andWhere('type')->eq('project')
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'execution':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PROJECT)
- ->where('deleted')->eq(0)
- ->andWhere('type')->in('sprint,stage,kanban')
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'repo':
- $objectPairs = $this->loadModel('repo')->getRepoPairs('repo');
- break;
- case 'artifactrepo':
- $serverID = 0;
- $this->loadModel('instance');
- if(method_exists($this->instance, 'getSystemServer'))
- {
- $server = $this->instance->getSystemServer();
- if(!empty($server)) $serverID = $server->id;
- }
- $objectPairs = $this->dao->select('id, name')->from(TABLE_ARTIFACTREPO)
- ->where('deleted')->eq(0)
- ->andWhere('type')->eq('gitfox')
- ->andWhere('serverID')->eq($serverID)
- ->fetchPairs();
- break;
- default:
- $objectPairs = $this->loadModel($scope)->getPairs();
- break;
- }
- return $objectPairs;
- }
- /**
- * 根据范围和创建日期获取对象列表。
- * Get object pairs by scope and createdDate.
- *
- * @param string $scope
- * @param string $date
- * @param string $vision
- * @access public
- * @return array
- */
- public function getPairsByScopeAndDate($scope, $date, $vision = 'rnd')
- {
- $objectPairs = array();
- switch($scope)
- {
- case 'product':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PRODUCT)
- ->where('deleted')->eq(0)
- ->andWhere('shadow')->eq(0)
- ->andWhere('createdDate')->le($date)
- ->andWhere("`closedDate` IS NULL OR YEAR(`closedDate`)='0000'", true)
- ->orWhere('closedDate')->ge($date)
- ->markRight(1)
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'project':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PROJECT)
- ->where('deleted')->eq(0)
- ->andWhere('type')->eq('project')
- ->andWhere('openedDate')->le($date)
- ->andWhere("`closedDate` IS NULL OR YEAR(`closedDate`)='0000'", true)
- ->orWhere('closedDate')->ge($date)
- ->markRight(1)
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'execution':
- $objectPairs = $this->dao->select('id, name')->from(TABLE_PROJECT)
- ->where('deleted')->eq(0)
- ->andWhere('type')->in('sprint,stage,kanban')
- ->andWhere('openedDate')->le($date)
- ->andWhere("`closedDate` IS NULL OR YEAR(`closedDate`)='0000'", true)
- ->orWhere('closedDate')->ge($date)
- ->markRight(1)
- ->andWhere("vision LIKE '%{$vision}%'", true)
- ->orWhere("vision")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- case 'user':
- $objectPairs = $this->dao->select('account,realname')->from(TABLE_USER)
- ->where('deleted')->eq(0)
- ->andWhere('join', true)->le(substr($date, 0, 10))
- ->orWhere("`join` IS NULL")->markRight(1)
- ->andWhere("visions LIKE '%{$vision}%'", true)
- ->orWhere("visions")->isNull()->markRight(1)
- ->fetchPairs();
- break;
- }
- return $objectPairs;
- }
- /**
- * Get object pairs by id list.
- *
- * @param string $scope
- * @param array $idList
- * @access public
- * @return array
- */
- public function getPairsByIdList($scope, $idList)
- {
- $field = $scope == 'user' ? 'realname' : 'name';
- $where = $scope == 'user' ? 'account' : 'id';
- return $this->dao->select("id, $field")->from($this->config->objectTables[$scope])
- ->where($where)->in($idList)
- ->fetchPairs();
- }
- /**
- * 获取度量项的日期字符串。
- * Build date cell.
- *
- * @param array $record
- * @param string $dateType
- * @access public
- * @return object
- */
- public function buildDateCell($record, $dateType)
- {
- $row = new stdclass();
- $year = isset($record['year']) ? $record['year'] : '';
- $month = isset($record['month']) ? $record['month'] : '';
- $week = isset($record['week']) ? $record['week'] : '';
- $day = isset($record['day']) ? $record['day'] : '';
- $calcTime = isset($record['calcTime']) ? $record['calcTime'] : '';
- $date = $dateString = false;
- if($dateType == 'nodate')
- {
- $date = $dateString = substr($calcTime, 0, 10);
- }
- elseif($dateType == 'day')
- {
- $date = $dateString = "{$year}-{$month}-{$day}";
- }
- elseif($dateType == 'week')
- {
- $date = sprintf($this->lang->metric->weekCell, $year, $week);
- $dateString = "{$year}-{$week}";
- }
- elseif($dateType == 'month')
- {
- $date = sprintf($this->lang->metric->yearMonthFormat, $year, $month);
- $dateString = "{$year}-{$month}";
- }
- elseif($dateType == 'year')
- {
- $date = sprintf($this->lang->metric->yearFormat, $year);
- $dateString = $year;
- }
- $row->date = $date;
- $row->dateString = $dateString;
- $row->dateType = $dateType;
- $row->calcTime = $calcTime;
- return $row;
- }
- /**
- * 检查度量项的计算文件是否存在。
- * Check if metric's calculator exists.
- *
- * @param object $metric
- * @access public
- * @return bool
- */
- public function checkCalcExists($metric)
- {
- $calcName = $this->getCalcRoot() . $metric->scope . DS . $metric->purpose . DS . $metric->code . '.php';
- return file_exists($calcName);
- }
- /**
- * 没有度量的显示范围不做显示。
- * Unset scope item that have no metric.
- *
- * @param string $stage
- * @access public
- * @return void
- */
- public function processScopeList($stage = 'all')
- {
- $metrics = $this->metricTao->fetchMetricsByScope($this->config->metric->scopeList);
- foreach($this->lang->metric->scopeList as $scope => $name)
- {
- if(!isset($metrics[$scope]))
- {
- unset($this->lang->metric->scopeList[$scope]);
- unset($this->lang->metric->featureBar['preview'][$scope]);
- }
- }
- if(count($this->lang->metric->featureBar['preview']) >= 7)
- {
- $i = 0;
- foreach($this->lang->metric->featureBar['preview'] as $key => $name)
- {
- $i ++;
- if($i >= 7)
- {
- unset($this->lang->metric->featureBar['preview'][$key]);
- $this->lang->metric->moreSelects[$key] = $name;
- }
- }
- $this->lang->metric->featureBar['preview']['more'] = $this->lang->metric->more;
- }
- $this->lang->metric->featureBar['preview']['collect'] = $this->lang->metric->collect;
- }
- /**
- * 根据后台配置的估算单位对列表赋值。
- * Assign unitList['measure'] by custom hourPoint.
- *
- * @access public
- * @return void
- */
- public function processUnitList()
- {
- $this->app->loadLang('custom');
- $key = zget($this->config->custom, 'hourPoint', '0');
- $this->lang->metric->unitList['measure'] = $this->lang->custom->conceptOptions->hourPoint[$key];
- }
- /**
- * 根据后台配置的是否开启用户需求设置对象列表。
- * Unset objectList['requirement'] if custom requirement is close.
- *
- * @access public
- * @return void
- */
- public function processObjectList()
- {
- if(!isset($this->config->custom->URAndSR) or !$this->config->custom->URAndSR) unset($this->lang->metric->objectList['requirement']);
- }
- public function buildFilterCheckList($filters)
- {
- $filterItems = array();
- $onchange = 'window.handleFilterCheck(this)';
- $scopeItems = array();
- foreach($this->lang->metric->scopeList as $value => $text)
- {
- $isChecked = (isset($filters['scope']) and in_array($value, $filters['scope']));
- $scopeItems[] = array('text' => $text, 'value' => $value, 'onchange' => $onchange, 'checked' => $isChecked);
- }
- $filterItems['scope'] = array
- (
- 'class' => 'flex3 divider',
- 'items' => $scopeItems
- );
- $objectItems = array();
- foreach($this->lang->metric->objectList as $value => $text)
- {
- $isChecked = (isset($filters['object']) and in_array($value, $filters['object']));
- $collapse = in_array($value, $this->config->metric->collapseList) && !$isChecked;
- $objectItems[] = array('text' => $text, 'value' => $value, 'onchange' => $onchange, 'checked' => $isChecked, 'data-collapse' => $collapse);
- }
- $filterItems['object'] = array
- (
- 'class' => 'flex5 divider',
- 'items' => $objectItems
- );
- $purposeItems = array();
- foreach($this->lang->metric->purposeList as $value => $text)
- {
- $isChecked = (isset($filters['purpose']) and in_array($value, $filters['purpose']));
- $purposeItems[] = array('text' => $text, 'value' => $value, 'onchange' => $onchange, 'checked' => $isChecked);
- }
- $filterItems['purpose'] = array
- (
- 'class' => 'flex2',
- 'items' => $purposeItems
- );
- return $filterItems;
- }
- /**
- * 根据数据初始化操作按钮。
- * Init action button by data.
- *
- * @param array $metrics
- * @access public
- * @return array
- * @param mixed[] $cols
- */
- public function initActionBtn($metrics, $cols)
- {
- foreach($metrics as $metric)
- {
- foreach($metric->actions as $key => $action)
- {
- $isClick = true;
- if($action['name'] == 'edit') $isClick = $metric->canEdit;
- if($action['name'] == 'implement') $isClick = $metric->canImplement;
- if($action['name'] == 'delist')
- {
- $isClick = $metric->canDelist;
- if(!$isClick && $metric->builtin == '1') $metric->actions[$key]['hint'] = $this->lang->metric->builtinMetric;
- }
- if($action['name'] == 'recalculate')
- {
- $isClick = $metric->canRecalculate;
- if($metric->stage != 'released' || (!empty($metric->dateType) && $metric->dateType == 'nodate')) $metric->actions[$key]['hint'] = $this->lang->metric->tips->banRecalculate;
- }
- $metric->actions[$key]['disabled'] = !$isClick;
- }
- }
- $hasAction = !empty($metrics) && !empty($metrics[0]->actions);
- if(!$hasAction) unset($cols['actions']);
- return array($cols, $metrics);
- }
- /**
- * 判断度量项是否是旧版度量项。
- * Judge if the metric is old.
- *
- * @param object $metric
- * @access public
- * @return bool
- */
- public function isOldMetric($metric)
- {
- return isset($metric->type) && $metric->type == 'sql';
- }
- /**
- * 创建SQL函数。
- * Create sql function.
- *
- * @param string $sql
- * @param object $measurement
- * @access public
- * @return array
- */
- public function createSqlFunction($sql, $measurement)
- {
- $measFunction = $this->getSqlFunctionName($measurement);
- $postFunction = $this->parseSqlFunction($sql);
- if(!$measFunction || !$postFunction) return array('result' => 'fail', 'errors' => $this->lang->metric->tips->nameError);
- $sql = str_replace($postFunction, $measFunction, $sql);
- try
- {
- $this->dbh->exec("DROP FUNCTION IF EXISTS `$measFunction`");
- $result = $this->dbh->exec($sql);
- }
- catch(PDOException $exception)
- {
- $message = sprintf($this->lang->metric->tips->createError, $exception->getMessage());
- return array('result' => 'fail', 'errors' => $message);
- }
- return array('result' => 'success');
- }
- /**
- * 获取旧度量项的SQL函数名。
- * Get sql function name of a old metric.
- *
- * @param object $measurement
- * @access public
- * @return string
- */
- public function getSqlFunctionName($measurement)
- {
- if(!$measurement) return '';
- return strtolower("qc_{$measurement->code}");
- }
- /**
- * 处理请求参数。
- * Process post params.
- *
- * @access public
- * @return array
- */
- public function processPostParams()
- {
- return array_combine($this->post->varName, $this->post->queryValue);
- }
- /**
- * 执行旧版度量项。
- * Execute a sql metric.
- *
- * @param object $measurement
- * @param array $vars
- * @access public
- * @return int|string
- */
- public function execSqlMeasurement($measurement, $vars)
- {
- $function = $this->getSqlFunctionName($measurement);
- if(!$function)
- {
- $this->errorInfo = $this->lang->metric->tips->nameError;
- return false;
- }
- $vars = (array) $vars;
- foreach($vars as $key => $param)
- {
- if(is_object($param))
- {
- unset($vars[$key]);
- continue;
- }
- $vars[$key] = $this->dbh->quote($param);
- }
- $params = join(',', $vars);
- try
- {
- $result = $this->dbh->query("select $function($params)")->fetch(PDO::FETCH_NUM);
- }
- catch(PDOException $exception)
- {
- $this->errorInfo = $exception->getMessage();
- return false;
- }
- return isset($result[0]) && $measurement->unit ? $result[0] : null;
- }
- /**
- * 获取度量筛选器的下拉选项。
- * Get options of a control.
- *
- * @param string $optionType
- * @access public
- * @return array
- */
- public function getControlOptions($optionType)
- {
- $optionList = array();
- if($optionType == 'project')
- {
- $options = $this->loadModel('project')->getPairsByProgram();
- }
- elseif($optionType == 'product')
- {
- $options = $this->loadModel('product')->getPairs('nocode');
- }
- elseif($optionType == 'sprint')
- {
- $options = $this->loadModel('execution')->getPairs();
- }
- elseif($optionType == 'user')
- {
- $options = $this->loadModel('user')->getPairs('noletter');
- }
- elseif(strpos($optionType, '.') !== false)
- {
- list($moduleName, $varListName) = explode('.', $optionType);
- $this->app->loadLang($moduleName);
- $varListName .= 'List';
- $options = $this->lang->$moduleName->$varListName;
- unset($options[0]);
- unset($options['']);
- }
- else
- {
- $options = array('' => '');
- }
- return $options;
- }
- /**
- * 处理实现提示文本信息。
- * Process implement tips.
- *
- * @param string $code
- * @access public
- * @return void
- */
- public function processImplementTips($code)
- {
- $tmpRoot = $this->app->getTmpRoot();
- $instructionTips = $this->lang->metric->implement->instructionTips;
- foreach($instructionTips as $index => $tip)
- {
- $instructionTips[$index] = str_replace("{code}", $code, $instructionTips[$index]);
- $instructionTips[$index] = str_replace("{tmpRoot}", $tmpRoot, $instructionTips[$index]);
- }
- $this->lang->metric->implement->instructionTips = $instructionTips;
- }
- /**
- * 更新一个度量项的字段内容。
- * Update metric fields
- *
- * @param string $metricID
- * @param object $metric
- * @access public
- * @return void
- */
- public function updateMetricFields($metricID, $metric)
- {
- $this->dao->update(TABLE_METRIC)->data($metric)->where('id')->eq($metricID)->exec();
- }
- /**
- * 将旧度量项的信息附加到度量项列表中。
- * Append old metric info to metric list.
- *
- * @param array $metrics
- * @access public
- * @return array
- */
- public function processOldMetrics($metrics)
- {
- $metricList = array();
- if(!in_array($this->config->edition, array('max', 'ipd')))
- {
- foreach($metrics as $metric)
- {
- $metric->isOldMetric = false;
- $metricList[] = $metric;
- }
- }
- else
- {
- $oldMetricList = $this->getOldMetricList();
- foreach($metrics as $metric)
- {
- $metric->isOldMetric = $this->isOldMetric($metric);
- $fromID = $metric->fromID;
- if($metric->isOldMetric && isset($oldMetricList[$fromID])) $metric->unit = $oldMetricList[$fromID]->unit;
- $metricList[] = $metric;
- }
- }
- return $metricList;
- }
- /**
- * 获取定时设置的标签。
- * Get label of collect configure.
- *
- * @param object $metric
- * @access public
- * @return string
- */
- public function getCollectConfText($metric)
- {
- $collectConf = $metric->collectConf;
- $dateType = $this->lang->metric->dateList[$collectConf->type];
- $dateConf = $collectConf->type == 'week' ? $collectConf->week : $collectConf->month;
- $dateListText = '';
- if($collectConf->type == 'week')
- {
- foreach(explode(',', $dateConf) as $date) $dateListText .= $this->lang->metric->weekList[$date] . ',';
- }
- else
- {
- foreach(explode(',', $dateConf) as $date) $dateListText .= sprintf($this->lang->metric->monthText, $date) . ',';
- }
- return sprintf($this->lang->metric->collectConfText, $dateType, rtrim($dateListText, ','), $metric->execTime);
- }
- /**
- * 获取度量项的数据类型。
- * Get data type of metric.
- *
- * @param array $tableData
- * @access public
- * @return string|false
- */
- public function getMetricRecordType($code, $scope)
- {
- if(!$code) return false;
- $type = array();
- $dateType = $this->getDateTypeByCode($code);
- if($scope != 'system') $type[] = 'scope';
- if($dateType != 'nodate') $type[] = 'date';
- if(empty($type)) $type[] = 'system';
- return implode('-', $type);
- }
- /**
- * 获取一个echarts的legend配置。
- * Get lengend options of echarts by head and series.
- *
- * @param array $series
- * @param string $range object|time
- * @access public
- * @return array
- */
- public function getEchartLegend($series, $range = 'time')
- {
- $legend = array('type' => 'scroll');
- if($range == 'object')
- {
- $selectedScope = array();
- foreach($series as $index => $se)
- {
- $selectedScope[$se['name']] = $index == 0 ? true : false;
- }
- $legend['selector'] = true;
- $legend['selected'] = $selectedScope;
- }
- return $legend;
- }
- /**
- * 通过header来判断一个度量项有没有对象的概念。
- * Judge whether a metric has the concept of an object.
- *
- * header 通过 metric 模块的 getViewTableHeader 方法取得
- * @param array $header 表头
- * @access public
- * @return bool
- */
- public function isObjectMetric($header)
- {
- return in_array('scope', array_column($header, 'name'));
- }
- /**
- * 通过header来判断一个度量项有没有日期的概念。
- * Judge whether a metric has the concept of an date.
- *
- * header 通过 metric 模块的 getViewTableHeader 方法取得
- * @param array $header 表头
- * @access public
- * @return bool
- */
- public function isDateMetric($header)
- {
- return in_array('date', array_column($header, 'name'));
- }
- /**
- * 获取一个echarts的配置项。
- * Get options of echarts by head and data.
- *
- * @param array $head 表头
- * @param array $data 数据
- * @param string $chartType 图表类型 barX|barY|line|pie
- * @access public
- * @return array|false
- * @param mixed[] $header
- */
- public function getEchartsOptions($header, $data, $chartType = 'line')
- {
- if(!$header || !$data) return false;
- $type = in_array($chartType, array('barX', 'barY')) ? 'bar' : $chartType;
- if($type == 'pie') return $this->getPieEchartsOptions($header, $data);
- $headLength = count($header);
- $options = array();
- if($headLength == 2)
- {
- $options = $this->getTimeOptions($header, $data, $type, $chartType);
- }
- elseif($headLength == 3)
- {
- if($this->isObjectMetric($header))
- {
- $options = $this->getObjectOptions($data, $type, $chartType);
- }
- else
- {
- $options = $this->getTimeOptions($header, $data, $type, $chartType);
- }
- }
- elseif($headLength == 4)
- {
- $options = $this->getObjectOptions($data, $type, $chartType);
- }
- if($type == 'bar')
- {
- $xAxis = $options['xAxis'];
- $yAxis = $options['yAxis'];
- $options['xAxis'] = $chartType == 'barY' ? $yAxis : $xAxis;
- $options['yAxis'] = $chartType == 'barY' ? $xAxis : $yAxis;
- }
- return $options;
- }
- /**
- * 获取一个 对象属性度量的 echart options。
- * Get options of echarts by data.
- *
- * @param array $data 数据
- * @param string $type 类型 line|bar
- * @access public
- * @return array
- * @param string $chartType
- */
- public function getObjectOptions($data, $type, $chartType)
- {
- $dateField = !isset(current($data)->dateString) ? 'calcTime' : 'dateString';
- usort($data, function($a, $b) use ($dateField)
- {
- $keyA = $a->$dateField;
- $keyB = $b->$dateField;
- if($keyA == $keyB) return 0;
- return $keyA < $keyB ? -1 : 1;
- });
- $times = array();
- $objects = array();
- foreach($data as $dataInfo)
- {
- $time = substr($dataInfo->$dateField, 0, 10);
- $object = $dataInfo->scope;
- $value = $dataInfo->value;
- if(!isset($times[$time])) $times[$time] = $time;
- if(!isset($objects[$object])) $objects[$object] = array();
- $objects[$object][$time] = $value;
- }
- $xAxis = array('type' => 'category', 'data' => array_keys($times));
- $yAxis = array('type' => 'value', 'min' => 'dataMin', 'max' => 'dataMax');
- $series = array();
- if($type != 'line') $yAxis = array('type' => 'value');
- foreach($objects as $object => $datas)
- {
- $seriesData = array();
- foreach($times as $time)
- {
- $seriesData[] = isset($datas[$time]) ? $datas[$time] : 0;
- }
- $series[] = array('type' => $type, 'name' => $object, 'data' => $seriesData);
- }
- $legend = $this->getEchartLegend($series, 'object');
- $options = array();
- $options['xAxis'] = $xAxis;
- $options['yAxis'] = $yAxis;
- $options['legend'] = $legend;
- $options['series'] = $series;
- $options['grid'] = $this->config->metric->chartConfig->grid;
- $options['tooltip'] = $this->config->metric->chartConfig->tooltip;
- $dataLength = count($series[0]['data']);
- if($dataLength > 15) $options['dataZoom'] = $this->genDataZoom($dataLength, 15, $chartType == 'barY' ? 'y' : 'x');
- return $options;
- }
- /**
- * 获取一个 时间属性度量的 echart options。
- * Get options of echarts by head and data.
- *
- * @param array $head 表头
- * @param string $type 类型 line|bar
- * @access public
- * @return array
- * @param mixed[] $header
- * @param mixed[] $data
- * @param string $chartType
- */
- public function getTimeOptions($header, $data, $type, $chartType)
- {
- list($x, $y) = $this->getEchartXY($header);
- usort($data, function($a, $b) use ($x)
- {
- $keyA = $a->$x;
- $keyB = $b->$x;
- if($keyA == $keyB) return 0;
- return $keyA < $keyB ? -1 : 1;
- });
- $xTime = array_column($data, $x);
- $xAxis = array('type' => 'category', 'data' => $xTime);
- $yAxis = array('type' => 'value', 'min' => 'dataMin', 'max' => 'dataMax');
- if($type != 'line') $yAxis = array('type' => 'value');
- $series = array('type' => $type, 'data' => array_column($data, $y));
- $legend = $this->getEchartLegend($series);
- $options = array();
- $options['xAxis'] = $xAxis;
- $options['yAxis'] = $yAxis;
- $options['legend'] = $legend;
- $options['series'] = $series;
- $options['grid'] = $this->config->metric->chartConfig->grid;
- $options['tooltip'] = $this->config->metric->chartConfig->tooltip;
- $dataLength = count($data);
- if($dataLength > 15) $options['dataZoom'] = $this->genDataZoom($dataLength, 15, $chartType == 'barY' ? 'y' : 'x');
- return $options;
- }
- /**
- * 生成数据缩放的配置。
- * Generate data zoom config.
- *
- * @param int $dataLength
- * @param int $initZoom
- * @param string $axis
- * @access public
- * @return array
- */
- public function genDataZoom($dataLength, $initZoom = 10, $axis = 'x')
- {
- $percent = $initZoom / $dataLength * 100;
- $percent = $percent > 100 ? 100 : $percent;
- $dataZoom = $this->config->metric->chartConfig->dataZoom;
- $dataZoom['start'] = 0;
- $dataZoom['end'] = $percent;
- if($axis == 'x') $dataZoom['xAxisIndex'] = array(0);
- if($axis == 'y') $dataZoom['yAxisIndex'] = array(0);
- return array($dataZoom);
- }
- /**
- * 获取一个echarts pie的配置项。
- * Get options of echarts pie by head and data.
- *
- * @param array $head 表头
- * @param array $datas 数据
- * @access public
- * @return array
- * @param mixed[] $header
- */
- public function getPieEchartsOptions($header, $datas)
- {
- list($x, $y) = $this->getEchartXY($header);
- $seriesData = array();
- foreach($datas as $data)
- {
- $seriesData[] = array('name' => $data->$x, 'value' => $data->$y);
- }
- $options = array();
- $options['tooltip'] = array('trigger' => 'item');
- $options['legend'] = array('orient' => 'vertical', 'left' => 'left', 'type' => 'scroll');
- $options['series'] = array(array('type' => 'pie', 'radius' => '50%', 'data' => $seriesData, 'emphasis' => array('itemStyle' => array('shadowBlur' => 10, 'shadowOffsetX' => 0, 'shadowColor' => 'rgba(0, 0, 0, 0.5)'))));
- return $options;
- }
- /**
- * 获取图表 x轴 和 y轴的字段。
- * Get echart x and y field.
- *
- * @param array $header 表头
- * @access public
- * @return array
- */
- public function getEchartXY($header)
- {
- $x = $y = '';
- $headLength = count($header);
- if($headLength == 2)
- {
- $x = $header[1]['name'];
- $y = $header[0]['name'];
- }
- elseif($headLength == 3)
- {
- $x = $header[0]['name'];
- $y = $header[1]['name'];
- }
- return array($x, $y);
- }
- /**
- * 获取一个图表可选的类型。
- * Get a echart typeList.
- *
- * @param array $header 表头
- * @access public
- * @return array
- */
- public function getChartTypeList($header)
- {
- $chartTypeList = $this->lang->metric->chartTypeList;
- if($this->isObjectMetric($header)) unset($chartTypeList['pie']);
- return $chartTypeList;
- }
- /**
- * 更新度量项的创建时间
- * Update created date of metrics.
- *
- * @access public
- * @return void
- */
- public function updateMetricDate()
- {
- $this->dao->update(TABLE_METRIC)->set('createdDate')->eq(helper::now())->where('createdDate')->isNull()->exec();
- }
- /**
- * 获取度量项计算文件的根目录。
- * Get root of metric calculator.
- *
- * @access public
- * @return string
- */
- public function getCalcRoot()
- {
- return $this->app->getModuleRoot() . 'metric' . DS . 'calc' . DS;
- }
- /**
- * 获取数据集文件的路径
- * Get path of calculator data set.
- *
- * @access public
- * @return string
- */
- public function getDatasetPath()
- {
- return $this->app->getModuleRoot() . 'metric' . DS . 'dataset.php';
- }
- /**
- * 获取度量项基类文件的路径。
- * Get path of base calculator class.
- *
- * @access public
- * @return string
- */
- public function getBaseCalcPath()
- {
- return $this->app->getModuleRoot() . 'metric' . DS . 'calc.class.php';
- }
- /**
- * 处理度量数据查询条件。
- * Process metric data query.
- *
- * @param array $query
- * @param string $key
- * @param string $type
- * @access public
- * @return object|string|false
- */
- public function processRecordQuery($query, $key, $type = 'common')
- {
- if($key == 'dateLabel')
- {
- if(isset($query[$key]))
- {
- $dateType = $query['dateType'];
- $dateLabel = $query['dateLabel'];
- if($dateLabel == 'all') $dateStr = '1970-01-01';
- if(is_numeric($dateLabel)) $dateStr = date('Y-m-d', strtotime('-' . ((int)$dateLabel - 1) . " {$dateType}s"));
- $query['dateBegin'] = $dateStr;
- $query['dateEnd'] = date('Y-m-d');
- }
- $begin = $this->processRecordQuery($query, 'dateBegin', 'date');
- $end = $this->processRecordQuery($query, 'dateEnd', 'date');
- return array($begin, $end);
- }
- if($key == 'calcDate' && $query['dateType'] == 'nodate')
- {
- if(!isset($query[$key])) $query['calcDate'] = 7;
- return date('Y-m-d', strtotime("-{$query[$key]} days"));
- }
- if(!isset($query[$key]) || empty($query[$key])) return false;
- if($type == 'date')
- {
- $dateStr = $query[$key];
- if($query['dateType'] == 'year')
- {
- $dateStr = "{$dateStr}-01-01";
- }
- elseif($query['dateType'] == 'month')
- {
- $dateStr = "{$dateStr}-01";
- }
- $timestamp = strtotime($dateStr);
- $year = date('Y', $timestamp);
- $month = date('m', $timestamp);
- $day = date('d', $timestamp);
- $week = date('oW', $timestamp);
- $dateParse = new stdClass();
- $dateParse->year = $year;
- $dateParse->month = "{$year}{$month}";
- $dateParse->week = $week;
- $dateParse->day = "{$year}{$month}{$day}";
- return $dateParse;
- }
- return $query[$key];
- }
- /**
- * 根据日期类型和日期,拆解出日期的年、月、周、日。
- * Get date values by date type and date.
- *
- * @param string $date
- * @param string $dateType
- * @access public
- * @return object|array
- */
- public function parseDateStr($date, $dateType = 'all')
- {
- $timestamp = strtotime($date);
- $year = date('Y', $timestamp);
- $month = date('m', $timestamp);
- $day = date('d', $timestamp);
- $week = date('oW', $timestamp);
- $week = substr($week, -2);
- $dateValues = new stdClass();
- $dateValues->year = array('year' => $year);
- $dateValues->month = array('year' => $year, 'month' => $month);
- $dateValues->week = array('year' => $year, 'week' => $week);
- $dateValues->day = array('year' => $year, 'month' => $month, 'day' => $day);
- $dateValues->nodate = array();
- return $dateType == 'all' ? $dateValues : $dateValues->$dateType;
- }
- /**
- * 获取度量数据的日期类型。
- * Get date type of metric data.
- *
- * @param array $dateFields
- * @access public
- * @return string
- */
- public function getDateType($dateFields)
- {
- $dateList = array_intersect($dateFields, $this->config->metric->dateList);
- if(in_array('day', $dateList)) return 'day';
- if(in_array('week', $dateList)) return 'week';
- if(in_array('month', $dateList)) return 'month';
- if(in_array('year', $dateList)) return 'year';
- return 'nodate';
- }
- /**
- * 根据编号获取度量项的日期属性。
- * Get date type by metric code.
- *
- * @param string $code
- * @access public
- * @return string
- */
- public function getDateTypeByCode($code)
- {
- static $dateTypes = array();
- if(!isset($dateTypes[$code]))
- {
- /* Get dateType form db first. */
- $metric = $this->getByCode($code, 'dateType');
- if(!empty($metric->dateType)) $dateTypes[$code] = $metric->dateType;
- /* Get dateType from config second. */
- elseif(isset($this->config->metric->dateType[$code])) $dateTypes[$code] = $this->config->metric->dateType[$code];
- /* Return nodate if no matches. */
- else $dateTypes[$code] = 'nodate';
- }
- return $dateTypes[$code];
- }
- /**
- * 获取度量数据的日期类型。
- * Get date type of metric data.
- *
- * @param array $dateFields
- * @access public
- * @return string
- * @param string $dateType
- */
- public function getDateByDateType($dateType)
- {
- if($dateType == 'day') $sub = '-7 days';
- if($dateType == 'week') $sub = '-1 month';
- if($dateType == 'month') $sub = '-1 year';
- if($dateType == 'year') $sub = '-3 years';
- return date('Y-m-d', strtotime($sub));
- }
- /**
- * 解析SQL函数。
- * Parsing SQL function.
- *
- * @param string $sql
- * @access public
- * @return string
- */
- public function parseSqlFunction($sql)
- {
- $pattern = "/create\s+function\s+`{0,1}([\$,a-z,A-z,_,0-9,\(,|)]+`{0,1})\(+/Ui";
- preg_match_all($pattern, $sql, $matches);
- if(empty($matches[1][0])) return null;
- return trim($matches[1][0], '`');
- }
- /**
- * 替换换行符和回车符为指定字符。
- * Replace CR and LF to char.
- *
- * @param string $str
- * @param string $replace
- * @access public
- * @return string
- */
- public function replaceCRLF($str, $replace = ';')
- {
- $str = trim($str);
- if(strpos($str, "\n\r") !== false) $str = str_replace("\n\r", $replace, $str);
- if(strpos($str, "\r\n") !== false) $str = str_replace("\r\n", $replace, $str);
- if(strpos($str, "\n") !== false) $str = str_replace("\n", $replace, $str);
- if(strpos($str, "\r") !== false) $str = str_replace("\r", $replace, $str);
- return $str;
- }
- /**
- * 判断 header 是否有分组(合并单元格)。
- * Judge header whether there are merges cell.
- *
- * @param array $header
- * @access protected
- * @return array
- */
- protected function isHeaderGroup($header)
- {
- if(!$header) return false;
- foreach($header as $head)
- {
- if(isset($head['headerGroup'])) return true;
- }
- return false;
- }
- /**
- * 判断是否是按系统统计的度量项。
- * Determine whether it is metric in system.
- *
- * @param array $results
- * @access public
- * @return bool
- */
- public function isSystemMetric($results)
- {
- $firstRecord = (object)current($results);
- foreach($this->config->metric->excludeGlobal as $exclude)
- {
- if(isset($firstRecord->$exclude)) return false;
- }
- return true;
- }
- /**
- * 获取某一周的第一天和最后一天的日期。
- * Get the first and last day of a week.
- *
- * @param int|string $year
- * @param int|string $week
- * @param string $type date|datetime
- * @access public
- * @return bool
- */
- public function getStartAndEndOfWeek($year, $week, $type = 'datetime')
- {
- $firstDayOfYear = date('Y-01-01', strtotime("$year-01-01"));
- $firstDayOfWeek = date('N', strtotime($firstDayOfYear));
- $offsetDays = ($week - 1) * 7 - ($firstDayOfWeek - 1);
- $firstDayOfWeek = date('Y-m-d', strtotime("$firstDayOfYear +$offsetDays days"));
- $lastDayOfWeek = date('Y-m-d', strtotime("$firstDayOfWeek +6 days"));
- if($type == 'datetime') return array("$firstDayOfWeek 00:00:00", "$lastDayOfWeek 23:59:59");
- if($type == 'date') return array($firstDayOfWeek, $lastDayOfWeek);
- }
- /**
- * 判断某个度量项在某天是否被定时任务执行过。
- * Determine whether a metric has been executed by scheduled task on a certain day.
- *
- * @param string $code
- * @param string $date
- * @param string $dateType
- * @access public
- * @return bool
- */
- public function isCalcByCron($code, $date, $dateType)
- {
- $startDate = '';
- $endDate = '';
- $parsedDate = $this->parseDateStr($date, $dateType);
- if($dateType == 'year')
- {
- $startDate = "{$parsedDate['year']}-01-01 00:00:00";
- $endDate = "{$parsedDate['year']}-12-31 23:59:59";
- }
- if($dateType == 'month')
- {
- $startDate = "{$parsedDate['year']}-{$parsedDate['month']}-01 00:00:00";
- $nextMonth = date('Y-m-01', strtotime("$startDate +1 month"));
- $endDate = date('Y-m-d', strtotime("$nextMonth -1 day"));
- $endDate = "{$endDate} 23:59:59";
- }
- if($dateType == 'day')
- {
- $startDate = "{$parsedDate['year']}-{$parsedDate['month']}-{$parsedDate['day']} 00:00:00";
- $endDate = "{$parsedDate['year']}-{$parsedDate['month']}-{$parsedDate['day']} 23:59:59";
- }
- if($dateType == 'week') list($startDate, $endDate) = $this->getStartAndEndOfWeek($parsedDate['year'], $parsedDate['week']);
- $record = $this->dao->select('id')->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('calcType')->eq('cron')
- ->andWhere('date')->ge($startDate)
- ->andWhere('date')->le($endDate)
- ->limit(1)
- ->fetch();
- return !empty($record);
- }
- /**
- * 获取度量项的收集周期。
- * Get collect cycle of metric.
- *
- * @param array|object $record
- * @access public
- * @return string|null
- */
- public function getMetricCycle($record)
- {
- $record = (object)$record;
- if(isset($record->year) && !isset($record->month) && !isset($record->week)) return 'year';
- if(isset($record->year, $record->month) && !isset($record->day)) return 'month';
- if(isset($record->year, $record->month, $record->day)) return 'day';
- if(isset($record->year, $record->week)) return 'week';
- return null;
- }
- /**
- * 获取日期标签。
- * Get date labels.
- *
- * @param string $dateType
- * @access public
- * @return array|false
- */
- public function getDateLabels($dateType)
- {
- if($dateType == 'nodate') return array();
- $objectKey = "{$dateType}Labels";
- return $this->lang->metric->query->$objectKey;
- }
- /**
- * 获取默认选中的日期。
- * Get default selected date.
- *
- * @param array $dateLabels
- * @param array $filters
- * @access public
- * @return string
- */
- public function getDefaultDate($dateLabels, $filters = array())
- {
- $defaultDate = '';
- $dates = array_keys($dateLabels);
- return (string)current($dates);
- }
- /**
- * 获取没有数据时的提示信息。
- * Get no data tip.
- *
- * @param string $code
- * @access public
- * @return string
- */
- public function getNoDataTip($code)
- {
- $recordOfCode = $this->metricTao->fetchMetricRecordByDate($code, null, 1);
- $recordOfAll = $this->metricTao->fetchMetricRecordByDate('all', null, 1);
- /* 如果度量库没有任何数据,则说明度量项从来没有被采集过。*/
- /* If the metric library does not have any data, it means that the metric has never been collected. */
- if(empty($recordOfAll)) return $this->lang->metric->noDataBeforeCollect;
- /* 如果度量库有数据但没有该度量项的数据,则可以理解为收集过数据但是该度量项无数据。*/
- /* If the metric library has data but does not have data for this metric, it means that metric has been collected but there is no data for this metric. */
- if(!empty($recordOfAll) && empty($recordOfCode)) return $this->lang->metric->noDataAfterCollect;
- return $this->lang->metric->noData;
- }
- /**
- * 检查某个度量项在某个日期中是否被推算过。
- * Check whether a metric has been inferenced on a date
- *
- * @param string $code
- * @param string $dateType
- * @param string $date
- * @access public
- * @return bool
- */
- public function checkHasInferenceOfDate($code, $dateType, $date)
- {
- if($dateType == 'day' || $dateType == 'nodate') return false;
- $date = $this->parseDateStr($date, $dateType);
- $records = $this->dao->select('id')->from(TABLE_METRICLIB)
- ->where('metricCode')->eq($code)
- ->andWhere('calcType')->eq('inference')
- ->beginIF($dateType == 'year')->andWhere('year')->eq($date['year'])->fi()
- ->beginIF($dateType == 'month')->andWhere('year')->eq($date['year'])->andWhere('month')->eq($date['month'])->fi()
- ->beginIF($dateType == 'week')->andWhere('year')->eq($date['year'])->andWhere('week')->eq($date['week'])->fi()
- ->fetch();
- return !empty($records);
- }
- /**
- * 检查是否是第一次执行重算。
- * Check if this is the first time inference record.
- *
- * @param string|array|null $codes
- * @access public
- * @return bool
- */
- public function isFirstInference($codes = null)
- {
- $inferenceRecordCount = $this->dao->select('COUNT(id) AS count')->from(TABLE_METRICLIB)
- ->where('calcType')->eq('inference')
- ->beginIF($codes != null && !is_array($codes))->andWhere('metricCode')->eq($codes)->fi()
- ->beginIF($codes != null && is_array($codes))->andWhere('metricCode')->in($codes)->fi()
- ->fetch('count');
- return $inferenceRecordCount == 0;
- }
- /**
- * 根据动态获取安装禅道的大概时间。
- * Get date of install zentao accorrading action.
- *
- * @access public
- * @return string
- */
- public function getInstallDate()
- {
- $installedDate = $this->dao->select('value')->from(TABLE_CONFIG)
- ->where('section')->eq('global')
- ->andWhere('key')->eq('installedDate')
- ->limit(1)
- ->fetch('value');
- if(!empty($installedDate) && substr($installedDate, 0 ,4) != '0000') return $installedDate;
- return $this->dao->select('date')->from(TABLE_ACTION)
- ->orderBy('date_asc')
- ->limit(1)
- ->fetch('date');
- }
- /**
- * 根据版本过滤度量项。
- * Filter metric instance by edition.
- *
- * @param array $calcInstances
- * @access public
- * @return array
- */
- public function filterCalcByEdition($calcInstances)
- {
- foreach($calcInstances as $code => $instance)
- {
- $excludeDatasetConfig = $this->config->metric->excludeDatasetList;
- if(!empty($instance->dataset) && !empty($excludeDatasetConfig[$this->config->edition]))
- {
- $excludeDatasetList = $excludeDatasetConfig[$this->config->edition];
- if(in_array($instance->dataset, $excludeDatasetList)) unset($calcInstances[$code]);
- }
- }
- return $calcInstances;
- }
- }
|