| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471 |
- <?php
- /**
- * The model file of screen 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 Mengyi Liu <liumengyi@cnezsoft.com>
- * @package task
- * @version $Id: control.php 5106 2022-02-08 17:15:54Z $
- * @link https://www.zentao.net
- */
- class screenModel extends model
- {
- /**
- * 全局过滤器。
- * Global filter.
- *
- * @var object
- * @access public
- */
- public $filter;
- /**
- * 初始化函数。
- * Construct function.
- *
- * @access public
- * @return void
- */
- public function __construct()
- {
- parent::__construct();
- $this->loadBIDAO();
- $this->loadModel('bi');
- $this->filter = new stdclass();
- $this->filter->screen = '';
- $this->filter->year = '';
- $this->filter->month = '';
- $this->filter->dept = '';
- $this->filter->account = '';
- $this->filter->charts = array();
- }
- /**
- * 判断是否有权限访问。
- * Check screen access.
- *
- * @param int $dimensionID
- * @access public
- * @return array
- */
- public function checkAccess($screenID)
- {
- $viewableObjects = $this->bi->getViewableObject('screen');
- if(!in_array($screenID, $viewableObjects))
- {
- return $this->app->control->sendError($this->lang->screen->accessDenied, helper::createLink('screen', 'browse'));
- }
- }
- /**
- * 通过维度id获取大屏列表。
- * Get screen list by dimension id.
- *
- * @param int $dimensionID
- * @access public
- * @return array
- */
- public function getList($dimensionID)
- {
- return $this->dao->select('*')->from(TABLE_SCREEN)
- ->where('dimension')->eq($dimensionID)
- ->beginIF($this->config->edition == 'open')->andWhere('id')->ne($this->config->screen->phpScreen['usageReport'])->fi()
- ->andWhere('deleted')->eq('0')
- ->fetchAll('id', false);
- }
- /**
- * 通过id获取大屏信息。
- * Get screen by id.
- *
- * @param int $screenID
- * @param int $year
- * @param int $dept
- * @param string $account
- * @access public
- * @return object|bool
- * @param int $month
- */
- public function getByID($screenID, $year = 0, $month = 0, $dept = 0, $account = '', $withChartData = true)
- {
- $screen = $this->dao->select('*')->from(TABLE_SCREEN)->where('id')->eq($screenID)->fetch();
- if(!$screen) return false;
- if(empty($screen->scheme)) $screen->scheme = file_get_contents(__DIR__ . '/json/screen.json');
- if($withChartData) $screen->chartData = $this->genChartData($screen, $year, $month, $dept, $account);
- return $screen;
- }
- /**
- * 添加组件列表。
- * Add component list.
- *
- * @param object $scheme
- * @param array $componentList
- * @access public
- * @return object
- */
- public function addComponentList($scheme, $componentList)
- {
- if(is_string($scheme)) $scheme = json_decode($scheme);
- $scheme->componentList = $componentList;
- return $scheme;
- }
- /**
- * 构建大屏图表数据。
- * Generate chartData of screen.
- *
- * @param object $screen
- * @param int $year
- * @param int $dept
- * @param string $account
- * @access public
- * @return object
- * @param int $month
- */
- public function genChartData($screen, $year, $month, $dept, $account)
- {
- $this->filter = new stdclass();
- $this->filter->screen = $screen->id;
- $this->filter->year = $year;
- $this->filter->month = $month;
- $this->filter->dept = $dept;
- $this->filter->account = $account;
- $this->filter->charts = array();
- $scheme = json_decode($screen->scheme);
- if(!$screen->builtin or in_array($screen->id, $this->config->screen->builtinScreen))
- {
- foreach($scheme->componentList as $component)
- {
- if(!empty($component->isGroup))
- {
- foreach($component->groupList as $key => $groupComponent)
- {
- if(isset($groupComponent->key) and $groupComponent->key === 'Select') $groupComponent = $this->buildSelect($groupComponent);
- $this->setComponentDefaults($groupComponent);
- }
- }
- else
- {
- if(isset($component->key) and $component->key === 'Select') $component = $this->buildSelect($component);
- $this->setComponentDefaults($component);
- }
- }
- return $scheme;
- }
- $scheme->componentList = $this->buildComponentList($scheme->componentList);
- return $scheme;
- }
- /**
- * Get the latest chart.
- *
- * @param object $component
- * @access public
- * @return void
- */
- public function getLatestChart($component)
- {
- if(isset($component->key) and $component->key === 'Select') return $component;
- $chartID = zget($component->chartConfig, 'sourceID', '');
- if(!$chartID) return $component;
- $type = $component->chartConfig->package;
- $type = $this->getChartType($type);
- $table = $this->config->objectTables[$type];
- if($type == 'metric')
- {
- $chart = $this->loadModel('metric')->getByID($chartID);
- }
- else
- {
- $chart = $this->dao->select('*')->from($table)->where('id')->eq($chartID)->fetch();
- }
- if($type == 'metric') return $this->genMetricComponent($chart);
- return $this->genComponentData($chart, $type, $component);
- }
- /**
- * mergeChartAndPivotFilters
- *
- * @param int $type
- * @param int $chartOrPivot
- * @param int $sourceID
- * @param int $filters
- * @access public
- * @return void
- */
- public function mergeChartAndPivotFilters($type, $chartOrPivot, $sourceID, $filters)
- {
- $filterFormat = array();
- $chartOrPivotFilters = json_decode($chartOrPivot->filters, true);
- $mergeFilters = array();
- foreach($chartOrPivotFilters as $index => $chartOrPivotFilter)
- {
- if(!isset($filters[$index]['default'])) continue;
- $filterDefault = $filters[$index]['default'];
- if($filterDefault === null) continue;
- $filterType = $chartOrPivotFilter['type'];
- $filterFrom = zget($chartOrPivotFilter, 'from', '');
- if($filterType == 'date' or $filterType == 'datetime')
- {
- if($filterFrom == 'query')
- {
- if(is_numeric($filterDefault)) $filterDefault = date('Y-m-d H:i:s', $filterDefault / 1000);
- }
- else
- {
- if(is_array($filterDefault))
- {
- $begin = $filterDefault[0];
- $end = $filterDefault[1];
- $begin = date('Y-m-d H:i:s', $begin / 1000);
- $end = date('Y-m-d H:i:s', $end / 1000);
- $filterDefault = array('begin' => $begin, 'end' => $end);
- }
- else
- {
- $filterDefault = array('begin' => '', 'end' => '');
- }
- }
- }
- $chartOrPivotFilter['default'] = $filterDefault;
- $mergeFilters[] = $chartOrPivotFilter;
- }
- if($type == 'pivot')
- {
- list($sql, $filterFormat) = $this->loadModel($type)->getFilterFormat($chartOrPivot->sql, $mergeFilters);
- $chartOrPivot->sql = $sql;
- }
- else
- {
- $filterFormat = $this->loadModel($type)->getFilterFormat($mergeFilters);
- }
- return array($chartOrPivot, $filterFormat);
- }
- /**
- * Generate a component of screen.
- *
- * @param object $chart
- * @param string $type
- * @param object $component
- * @access public
- * @return object
- */
- public function genComponentData($chart, $type = 'chart', $component = null, $filters = array())
- {
- if(empty($chart) || ($chart->stage == 'draft' || $chart->deleted == '1'))
- {
- return $this->genNotFoundOrDraftComponentOption($component, $chart, $type);
- }
- $component = $this->unsetComponentDraftMarker($component);
- $chart = clone($chart);
- if($type == 'pivot' and $chart)
- {
- $this->loadModel('pivot')->processNameDesc($chart);
- }
- if(empty($filters) and !empty($chart->filters))
- {
- if($type == 'pivot')
- {
- list($sql, $filters) = $this->loadModel($type)->getFilterFormat($chart->sql, json_decode($chart->filters, true));
- $chart->sql = $sql;
- }
- else
- {
- $filters = $this->loadModel($type)->getFilterFormat(json_decode($chart->filters, true));
- }
- }
- list($component, $typeChanged) = $this->initComponent($chart, $type, $component);
- $component = $this->getChartOption($chart, $component, $filters);
- if($type == 'chart') $component = $this->getAxisRotateOption($chart, $component);
- $latestFilters = $this->getChartFilters($chart);
- $component = $this->updateComponentFilters($component, $latestFilters);
- if($type == 'chart' && (!$chart->builtin or in_array($chart->id, $this->config->screen->builtinChart)))
- {
- if(!empty($component->option->series))
- {
- $defaultSeries = $component->option->series;
- if($component->type == 'radar')
- {
- $component->option->radar->indicator = $component->option->dataset->radarIndicator;
- $defaultSeries[0]->data = $component->option->dataset->seriesData;
- $legends = array();
- foreach($component->option->dataset->seriesData as $seriesData) $legends[] = $seriesData->name;
- $component->option->legend->data = $legends;
- }
- elseif($component->type != 'waterpolo')
- {
- $series = array();
- for($i = 1; $i < count($component->option->dataset->dimensions); $i ++) $series[] = $defaultSeries[0];
- $component->option->series = $series;
- }
- }
- }
- return $component;
- }
- /**
- * 通过路径设置值。
- * Set value by path.
- *
- * @param object $option
- * @param string $path
- * @param mixed $value
- * @access public
- * @return void
- */
- public function setValueByPath(&$option, $path, $value)
- {
- $keys = explode('.', $path);
- $keyCount = count($keys);
- $current = &$option;
- foreach ($keys as $index => $key)
- {
- $isEnd = ($index + 1) >= $keyCount;
- $nextKey = $isEnd ? null : $keys[$index + 1];
- $isArray = is_numeric($key);
- $nextIsArray = is_numeric($nextKey);
- if($isArray && !isset($current[$key])) $current[$key] = $nextIsArray ? array() : new stdclass();
- if(!$isArray && !isset($current->$key)) $current->$key = $nextIsArray ? array() : new stdclass();
- if (is_array($current)) {
- $current = &$current[$key];
- } else {
- $current = &$current->$key;
- }
- }
- $current = $value;
- }
- /**
- * 通过静态数据生成组件。
- * Generate component.
- *
- * @param string $type
- * @param string $title
- * @param mixed $data
- * @param array $attr
- * @param array $options
- * @access public
- * @return object
- */
- public function genComponentFromData($type, $title, $data, $attr = array(), $options = array())
- {
- $component = new stdclass();
- $component->title = $title;
- $component->type = $type;
- $component->chartConfig = json_decode(zget($this->config->screen->chartConfig, $type));
- $component->key = $component->chartConfig->key;
- $component->attr = array('w' => 450, 'h' => 300, 'x' => 0, 'y' => 0, 'offsetX' => 0, 'offsetY' => 0, 'zIndex' => -1);
- $component->attr = array_merge($component->attr, $attr);
- $component->option = json_decode(zget($this->config->screen->chartOption, $type));
- $component->option->dataset = new stdclass();
- foreach($options as $keys => $value) $this->setValueByPath($component->option, $keys, $value);
- $component->chartConfig->title = $title;
- switch($type)
- {
- case 'line':
- case 'cluBarY':
- case 'stackedBarY':
- case 'cluBarX':
- case 'stackedBar':
- case 'bar':
- case 'pie':
- list($dimensions, $sourceData) = $data;
- return $this->prepareChartDataset($component, $dimensions, $sourceData);
- case 'box':
- return $this->prepareBoxDataset($component, $data);
- case 'table':
- list($headers, $align, $colspan, $dataset, $drills, $rowspans) = $data;
- $formattedDataset = array();
- foreach($dataset as $row)
- {
- $row = array_values($row);
- $formattedDataset[] = array_map('strval', $row);
- }
- $skipCellList = array();
- foreach($rowspans as $i => $rowspan)
- {
- foreach($rowspan as $j => $rowspanValue)
- {
- if(in_array(array($i, $j), $skipCellList)) continue;
- for($k = 1; $k < $rowspanValue; $k ++)
- {
- unset($formattedDataset[$i + $k][$j]);
- $skipCellList[] = array($i + $k, $j);
- }
- }
- }
- return $this->prepareTableDataset($component, $headers, $align, $colspan, $rowspans, $formattedDataset, $drills);
- case 'radar':
- list($radarIndicator, $seriesData) = $data;
- return $this->prepareRadarDataset($component, $radarIndicator, $seriesData);
- case 'card':
- return $this->prepareCardDataset($component, $data);
- case 'waterpolo':
- return $this->prepareWaterPoloDataset($component, $data);
- case 'sunburst':
- return $this->prepareSunburstDataset($component, $data);
- case 'group':
- return $this->prepareGroupDataset($component, $data);
- case 'text':
- return $this->prepareTextDataset($component, $data);
- case 'help':
- return $this->setComponentDefaults($component);
- default:
- return $component;
- }
- }
- /**
- * Update piovt or chart component chartConfig filters.
- *
- * @param object $component
- * @param array $latestFilters
- * @access public
- * @return object
- */
- public function updateComponentFilters($component, $latestFilters)
- {
- // 如果传过来的component->chartConfig中有filters,判断filters是否发生了改变,改变则重置filters,其中单个filter的linkedGlobalFilter属性就不存在了
- if(!isset($component->chartConfig->filters))
- {
- $component->chartConfig->filters = $latestFilters;
- $component->chartConfig->noSetupGlobalFilterList = array();
- }
- else
- {
- $oldFilters = $component->chartConfig->filters;
- $filterChanged = $this->isFilterChange($oldFilters, $latestFilters);
- if($filterChanged)
- {
- $component->chartConfig->filters = $latestFilters;
- $component->chartConfig->noSetupGlobalFilterList = array();
- }
- else
- {
- if(!isset($_POST['filters'])) // 如果不是前端进行筛选操作,则说明是第一次打开页面,此时看看后台的默认值有没有发生变更
- {
- foreach($oldFilters as $index => $filter)
- {
- $newFilter = $latestFilters[$index];
- $oldDefault = isset($filter->default) ? $filter->default : '';
- $newDefault = isset($newFilter->default) ? $newFilter->default : '';
- if($oldDefault !== $newDefault) $component->chartConfig->filters[$index]->default = $newDefault;
- }
- }
- foreach($oldFilters as $index => $filter)
- {
- $isSelect = $filter->type == 'select' && $latestFilters[$index]->type == 'select';
- $oldSaveAs = zget($filter, 'saveAs', '');
- $newSaveAs = zget($latestFilters[$index], 'saveAs', '');
- if($oldSaveAs !== $newSaveAs) $component->chartConfig->filters[$index]->default = $newSaveAs;
- if($isSelect and $oldSaveAs !== $newSaveAs) $component->chartConfig->filters[$index]->saveAs = $newSaveAs;
- }
- }
- }
- return $component;
- }
- /**
- * 判断图表/透视表的筛选器是否发生了变化。
- * Determine if the filter for the chart/pivot has changed.
- *
- * @param array $oldFilters
- * @param array $latestFilters
- * @access public
- * @return void
- */
- public function isFilterChange($oldFilters, $latestFilters)
- {
- $filterChanged = false;
- if(empty($latestFilters)) return true;
- if(count($oldFilters) != count($latestFilters)) $filterChanged = true;
- foreach($oldFilters as $index => $oldFilter)
- {
- $newFilter = $latestFilters[$index];
- // 结果筛选器和查询筛选器都有的三个字段
- if($oldFilter->field != $newFilter->field || $oldFilter->name != $newFilter->name || $oldFilter->type != $newFilter->type) $filterChanged = true;
- // 如果一个是查询筛选器一个不是,也判定为更改
- $oldHaveQuery = isset($oldFilter->from) and $oldFilter->from = 'query';
- $newHaveQuery = isset($newFilter->from) and $newFilter->from = 'query';
- if($oldHaveQuery != $newHaveQuery) $filterChanged = true;
- // 对于查询筛选器,可以再做进一步判断,如果下拉选择器的typeOption发生了变化,也应该判定为更改
- if($oldHaveQuery and $newHaveQuery)
- {
- if($oldFilter->type == 'select' && $newFilter->type == 'select' && $oldFilter->typeOption != $newFilter->typeOption) $filterChanged = true;
- }
- // 如果发生了变化,不必再去判断后续
- if($filterChanged) break;
- }
- return $filterChanged;
- }
- /**
- * 生成不存在的图表或者草稿图表的参数。
- * Generate not found or draft chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return void
- */
- public function genNotFoundOrDraftComponentOption($component, $chart, $type)
- {
- if(empty($component)) $component = new stdclass();
- $noDataLang = $type == 'chart' ? 'noChartData' : 'noPivotData';
- if(!isset($component->option)) $component->option = new stdclass();
- if(!isset($component->option->title)) $component->option->title = new stdclass();
- $name = zget($chart, 'name', '');
- $component->option->title->notFoundText = sprintf($this->lang->screen->$noDataLang, $name);
- $component->option->isDeleted = true;
- return $component;
- }
- /**
- * 生成已下架或者已删除度量项的参数。
- * Generate delist or deleted metric options.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function genDelistOrDeletedMetricOption($component)
- {
- if(empty($component)) $component = new stdclass();
- if(!isset($component->option)) $component->option = new stdclass();
- if(!isset($component->option->title)) $component->option->title = new stdclass();
- $component->option->title->notFoundText = $this->lang->screen->noMetricData;
- $component->option->isDeleted = true;
- return $component;
- }
- /**
- * 删除component的已删除标记。
- * Unset component option isDeleted.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function unsetComponentDraftMarker($component)
- {
- if(isset($component->option->isDeleted)) unset($component->option->isDeleted);
- if(isset($component->option->title)) unset($component->option->title->notFoundText);
- return $component;
- }
- /**
- * 生成全局筛选器的组件。
- * Generate component of global filters.
- *
- * @param string $filterType
- * @access public
- * @return object
- */
- public function genFilterComponent($filterType)
- {
- $this->loadModel('metric');
- $type = ucfirst($filterType);
- $component = new stdclass();
- $component->chartConfig = new stdclass();
- $component->chartConfig->id = $type;
- $component->chartConfig->key = "{$type}Filter";
- $component->chartConfig->chartKey = "V{$type}Filter";
- $component->chartConfig->conKey = "VC{$type}Filter";
- $component->chartConfig->category = 'Filters';
- $component->chartConfig->categoryName = $this->lang->screen->globalFilter;
- $component->chartConfig->package = 'Decorates';
- if(in_array($filterType, $this->config->screen->scopeList))
- {
- $objectPairs = $this->metric->getPairsByScope($filterType, true);
- $component->chartConfig->objectList = array_map(function($objectID, $objectTitle)
- {
- $object = new stdclass();
- $object->label = $objectTitle;
- $object->value = (string)$objectID;
- return $object;
- }, array_keys($objectPairs), array_values($objectPairs));
- }
- $firstAction = $this->dao->select('YEAR(date) AS year')->from(TABLE_ACTION)->orderBy('id_asc')->limit(1)->fetch();
- $yearRange = range((int)date('Y'), $firstAction->year);
- $component->chartConfig->yearList = array_map(function($year)
- {
- $yearObject = new stdclass();
- $yearObject->label = $year;
- $yearObject->value = $year;
- return $yearObject;
- }, $yearRange);
- return $component;
- }
- /**
- * Generate metric component.
- *
- * @param object $metric
- * @param object|null $component
- * @param array $filterParams
- * @access public
- * @return object
- */
- public function genMetricComponent($metric, $component = null, $filterParams = array())
- {
- $this->loadModel('metric');
- if($metric->deleted == '1' || $metric->stage == 'wait') return $this->genDelistOrDeletedMetricOption($component);
- $pagination = $this->getMetricPagination($component);
- $filters = $this->processMetricFilter($filterParams, $metric->dateType);
- list($pager, $pagination) = $this->preparePaginationBeforeFetchRecords($pagination);
- $result = $this->metric->getResultByCode($metric->code, $filters, 'cron', $pager);
- $pagination['total'] = $pager->recTotal;
- $pagination['pageTotal'] = $pager->pageTotal;
- $resultHeader = $this->metric->getViewTableHeader($metric);
- $resultData = $this->metric->getViewTableData($metric, $result);
- $isObjectMetric = $metric->scope != 'system';
- $isDateMetric = $metric->dateType != 'nodate';
- $tableOption = $this->getMetricTableOption($metric, $resultHeader, $resultData, $component);
- $chartOption = $this->getMetricChartOption($metric, $resultHeader, $resultData, $component);
- $card = $this->getMetricCardOption($metric, $resultData, $component);
- $tableOption->pagination = $pagination;
- $card->pagination = $pagination;
- list($component, $typeChanged) = $this->initMetricComponent($metric, $component);
- $component->chartConfig->title = $metric->name;
- $component->chartConfig->sourceID = $metric->id;
- $component->chartConfig->scope = $metric->scope;
- $component->chartConfig->dateType = $metric->dateType;
- $latestFilters = $this->buildMetricFilters($metric, $isObjectMetric, $isDateMetric);
- $component = $this->updateMetricFilters($component, $latestFilters);
- $component->option->chartOption = $chartOption;
- $component->option->tableOption = $tableOption;
- $component->option->card = $card;
- $component->option->card->isDateMetric = $isDateMetric;
- $component->option->card->isObjectMetric = $isObjectMetric;
- $component->option->card->cardDateDefault = $component->option->card->filterValue ? $component->option->card->filterValue->dateString : '';
- $component->option->noDataTip = $this->metric->getNoDataTip($metric->code);
- return $component;
- }
- public function getMetricPagination($component)
- {
- $tablePagination = array('index' => 1, 'size' => 5, 'total' => 0, 'pageTotal' => 1);
- $cardPagination = array('index' => 1, 'size' => 2 * 6, 'total' => 0, 'pageTotal' => 1);
- if(empty($component)) return $tablePagination;
- $option = $component->option;
- $displayType = $option->displayType;
- if(isset($option->card->pagination)) $cardPagination = array_merge($cardPagination, (array)$option->card->pagination);
- if(isset($option->tableOption->pagination)) $tablePagination = array_merge($tablePagination, (array)$option->tableOption->pagination);
- $tableOption = $option->tableOption;
- $cardOption = $option->card;
- if(isset($tableOption->rowNum)) $tablePagination['size'] = $tableOption->rowNum;
- $cardRow = isset($cardOption->countEachRow) ? $cardOption->countEachRow : 2;
- $cardColumn = isset($cardOption->countEachColumn) ? $cardOption->countEachColumn : 6;
- $cardPagination['size'] = $cardRow * $cardColumn;
- return $displayType == 'normal' ? $tablePagination : $cardPagination;
- }
- /**
- * Prepare pagination before fetch records.
- *
- * @param object $pagination
- * @access public
- * @return array
- */
- public function preparePaginationBeforeFetchRecords($pagination)
- {
- $defaultPagination = array('index' => 1, 'size' => 2 * 6, 'total' => 0);
- if(is_string($pagination)) $pagination = json_decode($pagination, true);
- if(empty($pagination)) return $pagination;
- $pagination = array_merge($defaultPagination, (array)$pagination);
- extract($pagination);
- $this->app->loadClass('pager', true);
- $pager = new pager($total, $size, $index);
- return array($pager, $pagination);
- }
- /**
- * Update metric component chartConfig filters.
- *
- * @param object $component
- * @param array $latestFilters
- * @access public
- * @return object
- */
- public function updateMetricFilters($component, $latestFilters)
- {
- if(!isset($component->chartConfig->filters))
- {
- $component->chartConfig->filters = $latestFilters;
- }
- return $component;
- }
- /**
- * Build filters for metric.
- *
- * @param object $metric
- * @access public
- * @return array
- */
- public function buildMetricFilters($metric, $isObjectMetric, $isDateMetric)
- {
- $this->loadModel('metric');
- $scope = $metric->scope;
- $filters = array();
- if($isObjectMetric)
- {
- $scopeFilter = new stdclass();
- $scopeFilter->belong = 'metric';
- $scopeFilter->field = $scope;
- $scopeFilter->name = $this->lang->screen->belong . $this->lang->$scope->common;
- $scopeFilter->type = 'select';
- $scopeFilter->typeOption = $scope;
- $scopeFilter->default = null;
- $filters[] = $scopeFilter;
- }
- if($isDateMetric)
- {
- $dateFilter = new stdclass();
- $dateFilter->belong = 'metric';
- $dateFilter->field = 'date';
- $dateFilter->name = $this->lang->screen->dateRange;
- $dateFilter->type = 'dateRange';
- $dateFilter->typeOption = null;
- $dateFilter->default = null;
- $filters[] = $dateFilter;
- }
- return $filters;
- }
- /**
- * Get chart option.
- *
- * @param object $chart
- * @param object $component
- * @param array $filters
- * @access public
- * @return object
- */
- public function getChartOption($chart, $component, $filters = '')
- {
- switch($component->type)
- {
- case 'line':
- return $this->getLineChartOption($component, $chart, $filters);
- case 'cluBarY':
- case 'stackedBarY':
- case 'cluBarX':
- case 'stackedBar':
- case 'bar':
- return $this->getBarChartOption($component, $chart, $filters);
- case 'piecircle':
- return $this->buildPieCircleChart($component, $chart);
- case 'pie':
- return $this->getPieChartOption($component, $chart, $filters);
- case 'table':
- return $this->getTableChartOption($component, $chart, $filters);
- case 'radar':
- return $this->getRadarChartOption($component, $chart, $filters);
- case 'card':
- return $this->buildCardChart($component, $chart);
- case 'waterpolo':
- if(strpos($chart->settings, 'waterpolo') === false) return $this->buildWaterPolo($component, $chart);
- return $this->getWaterPoloOption($component, $chart, $filters);
- case 'metric':
- return $this->getMetricOption($component, $chart, $filters);
- default:
- return '';
- }
- }
- /**
- * Get chart option about rotate.
- *
- * @param object $chart
- * @param object $component
- * @access public
- * @return object
- */
- public function getAxisRotateOption($chart, $component)
- {
- $this->loadModel('chart');
- if(!in_array($chart->type, $this->config->chart->canLabelRotate)) return $component;
- $settings = json_decode($chart->settings, true);
- if(!isset($settings[0])) return $component;
- $setting = $settings[0];
- $component->chartConfig->xAxis = new stdclass();
- $component->chartConfig->yAxis = new stdclass();
- $component->chartConfig->xAxis->axisLabel = new stdclass();
- $component->chartConfig->yAxis->axisLabel = new stdclass();
- $component->chartConfig->xAxis->axisLabel->rotate = 0;
- $component->chartConfig->yAxis->axisLabel->rotate = 0;
- if(isset($setting['rotateX']) && $setting['rotateX'] == 'use') $component->chartConfig->xAxis->axisLabel->rotate = 30;
- if(isset($setting['rotateY']) && $setting['rotateY'] == 'use') $component->chartConfig->yAxis->axisLabel->rotate = 30;
- return $component;
- }
- /**
- * 准备图表数据集。
- * Prepare chart dataset.
- *
- * @param object $component
- * @param array $dimensions
- * @param array $sourceData
- * @access public
- * @return object
- */
- public function prepareChartDataset($component, $dimensions, $sourceData)
- {
- $component->option->dataset->dimensions = $dimensions;
- $component->option->dataset->source = array_values($sourceData);
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备箱线图数据集。
- * Prepare box dataset.
- *
- * @param object $component
- * @param array $data
- * @param object $layout
- * @param object $config
- * @access public
- * @return object
- */
- public function prepareBoxDataset($component, $data)
- {
- $component->option->data = $data;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备组合数据集。
- * Prepare group dataset.
- *
- * @param object $component
- * @param array $componentList
- * @access public
- * @return object
- */
- public function prepareGroupDataset($component, $componentList)
- {
- $component->isGroup = true;
- $component->groupList = $componentList;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备雷达图数据集。
- * Prepare radar chart dataset.
- *
- * @param object $component
- * @param array $radarIndicator
- * @param array $seriesData
- * @access public
- * @return object
- */
- public function prepareRadarDataset($component, $radarIndicator, $seriesData)
- {
- $component->option->dataset->radarIndicator = $radarIndicator;
- $component->option->dataset->seriesData = $seriesData;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备文本数据集。
- * Prepare text dataset.
- *
- * @param object $component
- * @param string $text
- * @access public
- * @return object
- */
- public function prepareTextDataset($component, $text)
- {
- $component->option->dataset = $text;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备表格数据集。
- * Prepare table dataset.
- *
- * @param object $component
- * @param array $headers
- * @param array $align
- * @param array $colspan
- * @param array $rowspan
- * @param array $dataset
- * @param array $drills
- * @access public
- * @return object
- */
- public function prepareTableDataset($component, $headers, $align, $colspan, $rowspan, $dataset, $drills)
- {
- if(!isset($component->chartConfig->tableInfo)) $component->chartConfig->tableInfo = new stdclass();
- $component->option->header = $headers;
- $component->option->align = $align;
- $component->option->columnWidth = array();
- $component->option->rowspan = $rowspan;
- $component->option->colspan = $colspan;
- $component->option->dataset = $dataset;
- $component->option->drills = $drills;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备卡片数据集。
- * Prepare card dataset.
- *
- * @param object $component
- * @param string $value
- * @access public
- * @return object
- */
- public function prepareCardDataset($component, $value)
- {
- $component->option->dataset = (string)$value;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备水球图数据集。
- * Prepare waterpolo dataset.
- *
- * @param object $component
- * @param float $value
- * @access public
- * @return object
- */
- public function prepareWaterPoloDataset($component, $value)
- {
- $component->option->dataset = $value;
- return $this->setComponentDefaults($component);
- }
- /**
- * 准备旭日图数据集。
- * Prepare sunburst dataset.
- *
- * @param object $component
- * @param array $data
- * @access public
- * @return object
- */
- public function prepareSunburstDataset($component, $data)
- {
- $component->option->series->data = $data;
- return $this->setComponentDefaults($component);
- }
- /**
- * Get bar chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getBarChartOption($component, $chart, $filters = '')
- {
- $dimensions = array();
- $sourceData = array();
- if($chart->sql)
- {
- $settings = json_decode($chart->settings, true);
- $langs = json_decode($chart->langs, true);
- $settings = $settings[0];
- $isSort = in_array($chart->id, $this->config->screen->annualRankingChart) ? true : false;
- list($group, $metrics, $aggs, $xLabels, $yStats) = $this->bi->getMultiData($settings, $chart->sql, $filters, $chart->driver, $isSort);
- $fields = json_decode($chart->fields, true);
- $dimensions = array($settings['xaxis'][0]['field']);
- $clientLang = $this->app->getClientLang();
- $xLabelValues = $this->processXLabel($xLabels, $fields[$group]['type'], $fields[$group]['object'], $fields[$group]['field']);
- foreach($yStats as $index => $dataList)
- {
- $field = zget($fields, $metrics[$index]);
- $fieldName = $field['name'];
- if(isset($langs[$field['field']]) and !empty($langs[$field['field']][$clientLang])) $fieldName = $langs[$field['field']][$clientLang];
- $field = $fieldName . '(' . zget($this->lang->chart->aggList, $aggs[$index]) . ')';
- $dimensions[] = $field;
- foreach($dataList as $valueField => $value)
- {
- $valueField = $xLabelValues[$valueField];
- if(empty($sourceData[$valueField]))
- {
- $sourceData[$valueField] = new stdclass();
- $sourceData[$valueField]->{$settings['xaxis'][0]['field']} = $valueField;
- }
- $sourceData[$valueField]->{$field} = $value;
- }
- }
- }
- return $this->prepareChartDataset($component, $dimensions, $sourceData);
- }
- /**
- * Get line chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getLineChartOption($component, $chart, $filters = '')
- {
- $dimensions = array();
- $sourceData = array();
- if($chart->sql)
- {
- $settings = json_decode($chart->settings, true);
- $langs = json_decode($chart->langs, true);
- $settings = $settings[0];
- list($group, $metrics, $aggs, $xLabels, $yStats) = $this->bi->getMultiData($settings, $chart->sql, $filters, $chart->driver);
- $fields = json_decode($chart->fields, true);
- $dimensions = array($settings['xaxis'][0]['field']);
- $clientLang = $this->app->getClientLang();
- $xLabelValues = $this->processXLabel($xLabels, $fields[$group]['type'], $fields[$group]['object'], $fields[$group]['field']);
- foreach($yStats as $index => $dataList)
- {
- $field = zget($fields, $metrics[$index]);
- $fieldName = $field['name'];
- if(isset($langs[$field['field']]) and !empty($langs[$field['field']][$clientLang])) $fieldName = $langs[$field['field']][$clientLang];
- $field = $fieldName . '(' . zget($this->lang->chart->aggList, $aggs[$index]) . ')';
- $dimensions[] = $field;
- foreach($dataList as $valueField => $value)
- {
- $valueField = $xLabelValues[$valueField];
- if(empty($sourceData[$valueField]))
- {
- $sourceData[$valueField] = new stdclass();
- $sourceData[$valueField]->{$settings['xaxis'][0]['field']} = $valueField;
- }
- $sourceData[$valueField]->{$field} = $value;
- }
- }
- /* Completing empty values. */
- foreach($sourceData as $lineData)
- {
- foreach($dimensions as $dimension)
- {
- if(empty($lineData->{$dimension})) $lineData->{$dimension} = 0;
- }
- }
- }
- return $this->prepareChartDataset($component, $dimensions, $sourceData);
- }
- /**
- * Get pie chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getPieChartOption($component, $chart, $filters = '')
- {
- $dimensions = array();
- $sourceData = array();
- if($chart->sql)
- {
- $settings = json_decode($chart->settings, true);
- $settings = $settings[0];
- $options = $this->loadModel('chart')->genPie(json_decode($chart->fields, true), $settings, $chart->sql, $filters, $chart->driver);
- if($settings['group'][0]['field'] == $settings['metric'][0]['field']) $settings['group'][0]['field'] = $settings['group'][0]['field'] . '1';
- $dimensions = array($settings['group'][0]['field'], $settings['metric'][0]['field']);
- $sourceData = array();
- foreach($options['series'] as $dataList)
- {
- $field = $settings['metric'][0]['field'];
- foreach($dataList['data'] as $data)
- {
- $fieldValue = $data['name'];
- if(empty($sourceData[$fieldValue]))
- {
- $sourceData[$fieldValue] = new stdclass();
- $sourceData[$fieldValue]->{$settings['group'][0]['field']} = (string)$fieldValue;
- }
- $sourceData[$fieldValue]->{$field} = $data['value'];
- }
- }
- if(empty($sourceData)) $dimensions = array();
- }
- return $this->prepareChartDataset($component, $dimensions, $sourceData);
- }
- /**
- * Get radar chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getRadarChartOption($component, $chart, $filters = '')
- {
- $indicator = array();
- $seriesData = array();
- $radarIndicator = array();
- $seriesData = array();
- if($chart->sql)
- {
- $settings = json_decode($chart->settings, true);
- $langs = json_decode($chart->langs, true);
- $settings = $settings[0];
- list($group, $metrics, $aggs, $xLabels, $yStats) = $this->bi->getMultiData($settings, $chart->sql, $filters, $chart->driver);
- $fields = json_decode($chart->fields, true);
- $max = 0;
- $clientLang = $this->app->getClientLang();
- $xLabelValues = $this->processXLabel($xLabels, $fields[$group]['type'], $fields[$group]['object'], $fields[$group]['field']);
- foreach($yStats as $index => $dataList)
- {
- $fieldObj = zget($fields, $metrics[$index]);
- $fieldName = $fieldObj['name'];
- $field = $fieldObj['field'];
- if(isset($langs[$field])) $fieldName = zget($langs[$field], $clientLang, $fieldName);
- $seriesData[$index] = new stdclass();
- $seriesData[$index]->name = $fieldName . '(' . zget($this->lang->chart->aggList, $aggs[$index]) . ')';
- $values = array();
- foreach($dataList as $valueField => $value)
- {
- $values[] = (float)$value;
- $max = $max < $value ? (float)$value : (float)$max;
- }
- $seriesData[$index]->value = $values;
- }
- if(!empty($dataList))
- {
- foreach($dataList as $valueField => $value)
- {
- $indicator = new stdclass();
- $indicator->name = $xLabelValues[$valueField];
- $indicator->max = $max;
- $radarIndicator[] = $indicator;;
- }
- }
- }
- return $this->prepareRadarDataset($component, $radarIndicator, $seriesData);
- }
- /**
- * Get table chart option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getTableChartOption($component, $chart, $filters = array())
- {
- $align = array();
- $headers = array();
- $colspans = array();
- $dataset = array();
- $drills = array();
- $config = array();
- if($chart->sql)
- {
- $chart->settings = json_decode($chart->settings, true);
- $this->loadModel('pivot')->addDrills($chart);
- $settings = $chart->settings;
- $fields = json_decode($chart->fields, true);
- $langs = json_decode($chart->langs, true);
- $chartFilters = json_decode($chart->filters, true);
- if(empty($langs)) $langs = array();
- if(empty($fields)) $fields = array();
- if(!is_array($filters)) $filters = array();
- if(empty($chartFilters)) $filters = false;
- if(isset($settings['summary']) and $settings['summary'] == 'notuse')
- {
- list($options, $config) = $this->loadModel('pivot')->genOriginSheet($fields, $settings, $chart->sql, $filters, $langs, $chart->driver);
- }
- else
- {
- list($options, $config) = $this->loadModel('pivot')->genSheet($fields, $settings, $chart->sql, $filters, $langs, $chart->driver);
- }
- $showColPosition = $this->pivot->getShowColPosition($options);
- $isShowLastRow = $this->pivot->isShowLastRow($showColPosition);
- if($isShowLastRow and !empty($options->array))
- {
- $count = count($options->array);
- $colspans[$count - 1][0] = count($options->groups);
- }
- foreach($options->array as $data)
- {
- $data = array_values($data);
- $dataset[] = array_map('strval', $data);
- }
- $skipCellList = array();
- foreach($config as $i => $data)
- {
- foreach($data as $j => $rowspan)
- {
- if(in_array(array($i, $j), $skipCellList)) continue;
- for($k = 1; $k < $rowspan; $k ++)
- {
- unset($dataset[$i + $k][$j]);
- $skipCellList[] = array($i + $k, $j);
- }
- }
- }
- $groupCount = 0;
- $drillConfigs = array();
- foreach($options->cols as $cols)
- {
- $count = 1;
- $header = array();
- foreach($cols as $data)
- {
- $colspan = zget($data, 'colspan', 1);
- $isDrilling = zget($data, 'isDrilling', 0);
- $conditions = zget($data, 'condition', array());
- $drillField = zget($data, 'drillField', '');
- $isGroup = zget($data, 'isGroup', 0);
- $isSlice = zget($data, 'isSlice', 0);
- if($isGroup)
- {
- $groupCount += 1;
- }
- elseif(!$isSlice)
- {
- $drillConfigs[] = $isDrilling ? array('drillField' => $drillField, 'conditions' => $conditions) : false;
- if($colspan > 1) $drillConfigs = array_merge($drillConfigs, array_fill(0, $colspan - 1, false));
- }
- $header[] = $data;
- if($count == 1) $align[] = 'center';
- }
- $headers[] = $header;
- $count ++;
- }
- $groupFields = array_fill(0, $groupCount, 'groupCol');
- foreach($options->drills as $drill)
- {
- if(!isset($drill['drillFields'])) continue;
- $drillFields = array_values($drill['drillFields']);
- $drillRow = array();
- foreach($drillFields as $index => $drillField)
- {
- $drillConfig = $drillConfigs[$index];
- $drillRow[] = $drillConfig ? array('fields' => $drillField, 'config' => $drillConfig) : false;
- }
- $drillRow = array_merge($groupFields, $drillRow);
- $drills[] = $drillRow;
- }
- }
- return $this->prepareTableDataset($component, $headers, $align, $colspans, $config, $dataset, $drills);
- }
- /**
- * Get chart filters
- *
- * @param object $chart
- * @access public
- * @return void
- */
- public function getChartFilters($chart)
- {
- $filters = json_decode($chart->filters, true);
- $fields = json_decode($chart->fields, true);
- if(empty($filters)) return array();
- $this->loadModel('pivot');
- $screenFilters = array();
- foreach($filters as $filter)
- {
- $isQuery = (isset($filter['from']) and $filter['from'] == 'query');
- if($isQuery)
- {
- if($filter['type'] == 'date' or $filter['type'] == 'datetime')
- {
- if(isset($filter['default']))
- {
- $default = $this->pivot->processDateVar($filter['default']);
- $filter['default'] = empty($default) ? null : strtotime($default) * 1000;
- }
- }
- $screenFilters[] = (object)$filter;
- continue;
- }
- if($filter['type'] == 'date' or $filter['type'] == 'datetime')
- {
- if(isset($filter['default']))
- {
- $default = $filter['default'];
- $begin = $default['begin'];
- $end = $default['end'];
- if(empty($begin) and empty($end))
- {
- $filter['default'] = null;
- }
- else if(empty($begin) or empty($end))
- {
- $filter['default'] = empty($begin) ? strtotime($end) * 1000 : strtotime($begin) * 1000;
- }
- else
- {
- $filter['default'] = array(strtotime($begin) * 1000, strtotime($end) * 1000);
- }
- }
- else
- {
- $filter['default'] = null;
- }
- }
- $screenFilters[] = (object)$filter;
- }
- return $screenFilters;
- }
- /**
- * Process xLabel with lang
- *
- * @param array $xLabel
- * @param string $type
- * @param string $object
- * @param string $field
- * @access public
- * @return array
- */
- public function processXLabel($xLabels, $type, $object, $field)
- {
- $options = $this->getSysOptions($type, $object, $field);
- $xLabelValues = array();
- foreach($xLabels as $index => $label)
- {
- $xLabelValues[$label] = isset($options[$label]) ? $options[$label] : $label;
- }
- return $xLabelValues;
- }
- /**
- * Process metric filter.
- *
- * @param array $filterParams
- * @param string $dateType
- * @access public
- * @return array
- */
- public function processMetricFilter($filterParams, $dateType)
- {
- $filters = array();
- foreach($filterParams as $filterParam)
- {
- $field = $filterParam['field'];
- $value = $filterParam['default'];
- if(empty($value)) continue;
- if($field == 'date')
- {
- $beginValue = $value[0];
- $endValue = $value[1];
- $beginFilter = $this->formatMetricDateByType($beginValue, $dateType);
- $endFilter = $this->formatMetricDateByType($endValue, $dateType);
- $beginFilter->value = $beginValue;
- $endFilter->value = $endValue;
- $filters['dateBegin'] = $beginFilter->$dateType;
- $filters['dateEnd'] = $endFilter->$dateType;
- }
- else
- {
- $scopeFilter = new stdclass();
- $scopeFilter->value = $value;
- $scopeFilter->type = $field;
- $filters['scope'] = implode(',', $value);
- }
- }
- return $filters;
- }
- /**
- * Format date of metric's filter by type.
- *
- * @param string $stamp
- * @param string $dateType
- * @access public
- * @return array
- */
- public function formatMetricDateByType($stamp, $dateType)
- {
- $formatedDate = new stdclass();
- $formatedDate->year = date('Y', $stamp/1000);
- if($dateType == 'month') $formatedDate->month = date('Y-m', $stamp/1000);
- if($dateType == 'week') $formatedDate->week = date('Y-W', $stamp/1000);
- if($dateType == 'day') $formatedDate->day = date('Y-m-d', $stamp/1000);
- return $formatedDate;
- }
- /**
- * Get system options.
- *
- * @param string $type
- * @param string $object
- * @param string $field
- * @param string $sql
- * @param string $saveAs
- * @access public
- * @return array
- */
- public function getSysOptions($type, $object = '', $field = '', $sql = '', $saveAs = '')
- {
- $options = array();
- switch($type)
- {
- case 'user':
- $options = $this->loadModel('user')->getPairs();
- break;
- case 'product':
- $options = $this->loadModel('product')->getPairs();
- break;
- case 'project':
- $options = $this->loadModel('project')->getPairsByProgram();
- break;
- case 'execution':
- $options = $this->loadModel('execution')->getPairs();
- break;
- case 'dept':
- $options = $this->loadModel('dept')->getOptionMenu(0);
- break;
- case 'project.status':
- $this->app->loadLang('project');
- $options = $this->lang->project->statusList;
- break;
- case 'option':
- if($field)
- {
- $path = $this->app->getModuleRoot() . 'dataview' . DS . 'table' . DS . "$object.php";
- if(is_file($path))
- {
- include $path;
- $options = $schema->fields[$field]['options'];
- }
- }
- break;
- case 'object':
- if($field)
- {
- $useField = $field;
- $useTable = $object;
- $path = $this->app->getModuleRoot() . 'dataview' . DS . 'table' . DS . "$object.php";
- if(is_file($path))
- {
- include $path;
- if(isset($schema->fields[$field]['object']))
- {
- $fieldObject = $schema->fields[$field]['object'];
- $fieldShow = explode('.', $schema->fields[$field]['show']);
- if($fieldObject) $useTable = $fieldObject;
- if(count($fieldShow) == 2) $useField = $fieldShow[1];
- }
- }
- $table = isset($this->config->objectTables[$useTable]) ? $this->config->objectTables[$useTable] : zget($this->config->objectTables, $object, '');
- if($table)
- {
- $columns = $this->dbh->query("SHOW COLUMNS FROM $table")->fetchAll();
- foreach($columns as $id => $column) $columns[$id] = (array)$column;
- $fieldList = array_column($columns, 'Field');
- $useField = in_array($useField, $fieldList) ? $useField : 'id';
- $options = $this->dao->select("id, {$useField}")->from($table)->fetchPairs();
- }
- }
- break;
- case strpos($type, '.') !== false:
- $params = explode('.', $type);
- if(empty(array_filter($params)))
- {
- $options = array();
- }
- else
- {
- $module = $params[0];
- $typeList = $params[1] . 'List';
- $this->app->loadLang($module);
- $options = $this->lang->$module->$typeList;
- }
- break;
- default:
- if($field and $sql)
- {
- $keyField = $field;
- $valueField = $saveAs ? $saveAs : $field;
- $options = $this->getOptionsFromSql($sql, $keyField, $valueField);
- }
- break;
- }
- if($sql and $field and $saveAs and in_array($type, array('user', 'product', 'project', 'execution', 'dept', 'project.status', 'option', 'object')))
- {
- $options = $this->getOptionsFromSql($sql, $field, $saveAs);
- }
- return array_filter($options);
- }
- /**
- * Get pairs from column by keyField and valueField.
- *
- * @param string $sql
- * @param string $keyField
- * @param string $valueField
- * @access public
- * @return array
- */
- public function getOptionsFromSql($sql, $keyField, $valueField)
- {
- $options = array();
- $cols = $this->dbh->query($sql)->fetchAll();
- $sample = current($cols);
- if(!isset($sample->$keyField) or !isset($sample->$valueField)) return $options;
- foreach($cols as $col)
- {
- $key = $col->$keyField;
- $value = $col->$valueField;
- $options[$key] = $value;
- }
- return $options;
- }
- /**
- * Build component list.
- *
- * @param array $componentList
- * @access public
- * @return array
- */
- public function buildComponentList($componentList)
- {
- $components = array();
- foreach($componentList as $component)
- {
- if($component) $components[] = $this->buildComponent($component);
- }
- return $components;
- }
- /**
- * Build component.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function buildComponent($component)
- {
- /* If chart is builtin, build it. */
- if(isset($component->sourceID) and $component->sourceID) return $this->buildChart($component);
- if(isset($component->key) and $component->key === 'Select') return $this->buildSelect($component);
- if(empty($component->isGroup)) return $this->setComponentDefaults($component);
- $component->groupList = $this->buildComponentList($component->groupList);
- return $this->buildGroup($component);
- }
- /**
- * Build chart group.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function buildGroup($component)
- {
- return $this->setComponentDefaults($component);
- }
- /**
- * Set component defaults.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function setComponentDefaults($component)
- {
- $this->loadModel('bi');
- if(!isset($component->styles)) $component->styles = $this->config->bi->default->styles;
- if(!isset($component->status)) $component->status = $this->config->bi->default->status;
- if(!isset($component->request)) $component->request = $this->config->bi->default->request;
- if(!isset($component->events)) $component->events = $this->config->bi->default->events;
- return $component;
- }
- /**
- * Build select.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function buildSelect($component)
- {
- switch($component->type)
- {
- case 'year':
- $component->option->value = $this->filter->year;
- $beginYear = $this->dao->select('YEAR(MIN(date)) year')->from(TABLE_ACTION)->where('date')->notZeroDate()->fetch('year');
- if($beginYear < 2009) $beginYear = 2009;
- $options = array();
- for($year = date('Y'); $year >= $beginYear; $year--) $options[] = array('label' => $year, 'value' => $year);
- $component->option->dataset = $options;
- $url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen. "&year=' + value + '&month=" . $this->filter->month . "&dept=" . $this->filter->dept . "&account=" . $this->filter->account . "')";
- $component->option->onChange = "window.location.href = $url";
- break;
- case 'month':
- $component->option->value = $this->filter->month;
- $beginYear = $this->dao->select('YEAR(MIN(date)) year')->from(TABLE_ACTION)->where('date')->notZeroDate()->fetch('year');
- $beginMonth = $this->dao->select('MONTH(MIN(date)) month')->from(TABLE_ACTION)->where('date')->notZeroDate()->fetch('month');
- $currentYear = date('Y');
- $currentMonth = date('n');
- $options = array();
- for($month = 12; $month >= 1; $month--)
- {
- if($currentYear == $this->filter->year && $month > $currentMonth) continue;
- if($currentYear == $beginYear && $month < $beginMonth) continue;
- $options[] = array('label' => $month, 'value' => $month);
- }
- $component->option->dataset = $options;
- $url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen. "&year=" . $this->filter->year . "&month=' + value + '&dept=" . $this->filter->dept . "&account=" . $this->filter->account . "')";
- $component->option->onChange = "window.location.href = $url";
- break;
- case 'dept':
- $component->option->value = (string)$this->filter->dept;
- $options = array(array('label' => $this->lang->screen->allDepts, 'value' => '0'));
- $depts = $this->dao->select('id,name')->from(TABLE_DEPT)->where('grade')->eq(1)->fetchAll();
- foreach($depts as $dept)
- {
- $options[] = array('label' => $dept->name, 'value' => $dept->id);
- }
- $component->option->dataset = $options;
- $url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen . "&year=" . $this->filter->year . "&dept=' + value + '&account=')";
- $component->option->onChange = "window.location.href = $url";
- break;
- case 'account':
- $component->option->value = $this->filter->account;
- $options = array(array('label' => $this->lang->screen->allUsers, 'value' => ''));
- $depts = array();
- if($this->filter->dept) $depts = $this->dao->select('id')->from(TABLE_DEPT)->where('path')->like(',' . $this->filter->dept . ',%')->fetchPairs();
- $users = $this->dao->select('account,realname')->from(TABLE_USER)
- ->where('deleted')->eq(0)
- ->beginIF($this->filter->dept)->andWhere('dept')->in($depts)->fi()
- ->fetchAll();
- foreach($users as $user)
- {
- $options[] = array('label' => $user->realname, 'value' => $user->account);
- }
- $component->option->dataset = $options;
- $url = "createLink('screen', 'view', 'screenID=" . $this->filter->screen . "&year=" . $this->filter->year . "&dept=" . $this->filter->dept . "&account=' + value)";
- $component->option->onChange = "window.location.href = $url";
- break;
- }
- foreach($component->filterCharts as $chart)
- {
- if(!isset($this->filter->charts[$chart->chart])) $this->filter->charts[$chart->chart] = array();
- $this->filter->charts[$chart->chart][$component->type] = $chart->field;
- }
- return $this->setComponentDefaults($component);
- }
- /**
- * Build chart.
- *
- * @param object $component
- * @access public
- * @return object
- */
- public function buildChart($component)
- {
- $chart = $this->dao->select('*')->from(TABLE_CHART)->where('id')->eq($component->sourceID)->fetch();
- switch($chart->type)
- {
- case 'card':
- return $this->buildCardChart($component, $chart);
- break;
- case 'line':
- if($chart->builtin == '0')
- {
- $chart->sql = $this->setFilterSQL($chart);
- return $this->getLineChartOption($component, $chart, array());
- }
- return $this->buildLineChart($component, $chart);
- break;
- case 'bar':
- return $this->buildBarChart($component, $chart);
- break;
- case 'piecircle':
- return $this->buildPieCircleChart($component, $chart);
- break;
- case 'pie':
- if($chart->builtin == '0') return $this->getPieChartOption($component, $chart, array());
- return $this->buildPieChart($component, $chart);
- break;
- case 'radar':
- return $this->buildRadarChart($component, $chart);
- break;
- case 'org':
- return $this->buildOrgChart($component, $chart);
- break;
- case 'funnel':
- return $this->buildFunnelChart($component, $chart);
- break;
- case 'table':
- return $this->buildTableChart($component, $chart);
- break;
- case 'cluBarY':
- case 'stackedBarY':
- case 'cluBarX':
- case 'stackedBar':
- $chart->sql = $this->setFilterSQL($chart);
- return $this->getBarChartOption($component, $chart);
- break;
- case 'waterpolo':
- return $this->getWaterPoloOption($component, $chart, array());
- }
- }
- /**
- * Set SQL filter
- *
- * @param object $chart
- * @access public
- * @return string
- */
- public function setFilterSQL($chart)
- {
- if(isset($this->filter->charts[$chart->id]))
- {
- $conditions = array();
- foreach($this->filter->charts[$chart->id] as $key => $field)
- {
- switch($key)
- {
- case 'year':
- $conditions[] = $field . " = '" . $this->filter->$key . "'";
- break;
- case 'month':
- $conditions[] = $field . " = '" . $this->filter->$key . "'";
- break;
- case 'dept':
- if($this->filter->dept and !$this->filter->account)
- {
- $accountField = $this->filter->charts[$chart->id]['account'];
- $users = $this->dao->select('account')->from(TABLE_USER)->alias('t1')
- ->leftJoin(TABLE_DEPT)->alias('t2')
- ->on('t1.dept = t2.id')
- ->where('t2.path')->like(',' . $this->filter->dept . ',%')
- ->fetchPairs('account');
- $accounts = array();
- foreach($users as $account) $accounts[] = "'" . $account . "'";
- $conditions[] = $accountField . ' IN (' . implode(',', $accounts) . ')';
- }
- break;
- case 'account':
- if($this->filter->account) $conditions[] = $field . " = '" . $this->filter->$key . "'";
- break;
- }
- }
- if($conditions) return 'SELECT * FROM (' . str_replace(';', '', $chart->sql) . ') AS t1 WHERE ' . implode(' AND ', $conditions);
- }
- return $chart->sql;
- }
- /**
- * Build card chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildCardChart($component, $chart)
- {
- $value = '?';
- if($chart->settings)
- {
- $value = 0;
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->value))
- {
- $field = $settings->value->field;
- $sql = $this->setFilterSQL($chart);
- $results = $this->buildDataset($chart->id, $chart->driver, $sql);
- if($settings->value->type === 'text')
- {
- $value = empty($results[0]) ? '' : $results[0]->$field;
- }
- if($settings->value->type === 'value')
- {
- $value = empty($results[0]) ? 0 : $results[0]->$field;
- }
- if($settings->value->agg === 'count')
- {
- $value = count($results);
- }
- else if($settings->value->agg === 'sum')
- {
- foreach($results as $result)
- {
- $value += intval($result->$field);
- }
- $value = round($value);
- }
- }
- else
- {
- $value = '?';
- }
- }
- }
- return $this->prepareCardDataset($component, $value);
- }
- /**
- * Build line chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildLineChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "LineCommon";
- $component->chartConfig = json_decode('{"key":"LineCommon","chartKey":"VLineCommon","conKey":"VCLineCommon","title":"折线图","category":"Lines","categoryName":"折线图","package":"Charts","chartFrame":"echarts","image":"/static/png/line-e714bc74.png"}');
- $component->option = json_decode('{"legend":{"show":true,"top":"5%","textStyle":{"color":"#B9B8CE"}},"xAxis":{"type":"category"},"yAxis":{"show":true,"axisLine":{"show":true},"type":"value"},"backgroundColor":"rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->xaxis))
- {
- $dimensions = array($settings->xaxis[0]->name);
- foreach($settings->yaxis as $yaxis) $dimensions[] = $yaxis->name;
- $sourceData = array();
- $sql = $this->setFilterSQL($chart);
- $results = $this->bi->queryWithDriver($chart->driver, $sql);
- foreach($results as $result)
- {
- $key = $settings->xaxis[0]->name;
- $field = $settings->xaxis[0]->field;
- $row = array($key => $result->$field);
- foreach($settings->yaxis as $yaxis)
- {
- $field = $yaxis->field;
- $row[$yaxis->name] = $result->$field;
- }
- $sourceData[] = $row;
- }
- $component->option->dataset->dimensions = $dimensions;
- $component->option->dataset->source = $sourceData;
- }
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build table chart
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildTableChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "TableScrollBoard";
- $component->chartConfig = json_decode('{"key":"TableScrollBoard","chartKey":"VTableScrollBoard","conKey":"VCTableScrollBoard","title":"轮播列表","category":"Tables","categoryName":"表格","package":"Tables","chartFrame":"common","image":"/static/png/table_scrollboard-fb642e78.png"}');
- $component->option = json_decode('{"header":["列1","列2","列3"],"dataset":[["行1列1","行1列2","行1列3"],["行2列1","行2列2","行2列3"],["行3列1","行3列2","行3列3"]],"rowNum":2,"waitTime":2,"headerHeight":35,"carousel":"single","headerBGC":"#00BAFF","oddRowBGC":"#003B51","evenRowBGC":"#0A2732"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->column))
- {
- $header = array();
- $dataset = array();
- foreach($settings->column as $column)
- {
- $header[$column->field] = $column->name;
- }
- $sql = $this->setFilterSQL($chart);
- $results = $this->buildDataset($chart->id, $chart->driver, $sql);
- foreach($results as $result)
- {
- $row = array();
- foreach($header as $field => $name)
- {
- $row[] = $result->$field;
- }
- $dataset[] = $row;
- }
- $component->option->header = array_values($header);
- $component->option->dataset = $dataset;
- }
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build bar chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildBarChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType": 0, "requestHttpType": "get", "requestUrl": "", "requestIntervalUnit": "second", "requestContentType": 0, "requestParamsBodyType": "none", "requestSQLContent": { "sql": "select * from where" }, "requestParams": { "Body": { "form-data": {}, "x-www-form-urlencoded": {}, "json": "", "xml": "" }, "Header": {}, "Params": {}}}');
- $component->events = json_decode('{"baseEvent": {}, "advancedEvents": {}}');
- $component->key = "BarCrossrange";
- $component->chartConfig = json_decode('{"key": "BarCrossrange", "chartKey": "VBarCrossrange", "conKey": "VCBarCrossrange", "title": "横向柱状图", "category": "Bars", "categoryName": "柱状图", "package": "Charts", "chartFrame": "echarts", "image": "/static/png/bar_y-05067169.png" }');
- $component->option = json_decode('{"xAxis": { "show": true, "type": "category" }, "yAxis": { "show": true, "axisLine": { "show": true }, "type": "value" }, "series": [], "backgroundColor": "rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->xaxis))
- {
- $dimensions = array($settings->xaxis[0]->name);
- foreach($settings->yaxis as $yaxis) $dimensions[] = $yaxis->name;
- $sourceData = array();
- $sql = $this->setFilterSQL($chart);
- $results = $this->bi->queryWithDriver($chart->driver, $sql);
- foreach($results as $result)
- {
- $key = $settings->xaxis[0]->name;
- $field = $settings->xaxis[0]->field;
- if($settings->yaxis[0]->agg == 'sum')
- {
- if(!isset($sourceData[$result->$field])) $sourceData[$result->$field] = array($key => $result->$field);
- foreach($settings->yaxis as $yaxis)
- {
- $valueField = $yaxis->field;
- if(!isset($sourceData[$result->$field][$yaxis->name])) $sourceData[$result->$field][$yaxis->name] = 0;
- $sourceData[$result->$field][$yaxis->name] += $result->$valueField;
- }
- }
- else
- {
- $row = array($key => $result->$field);
- foreach($settings->yaxis as $yaxis)
- {
- $field = $yaxis->field;
- $row[$yaxis->name] = $result->$field;
- }
- $sourceData[] = $row;
- }
- }
- $component->option->dataset->dimensions = $dimensions;
- $component->option->dataset->source = array_values($sourceData);
- }
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build pie chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildPieChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "PieCommon";
- $component->chartConfig = json_decode('{"key":"PieCommon","chartKey":"VPieCommon","conKey":"VCPieCommon","title":"饼图","category":"Pies","categoryName":"饼图","package":"Charts","chartFrame":"echarts","image":"/static/png/pie-9620f191.png"}');
- $component->option = json_decode('{"type":"nomal","series":[{"type":"pie","radius":"70%","roseType":false}],"backgroundColor":"rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->metric))
- {
- $dimensions = array($settings->group[0]->name, $settings->metric[0]->field);
- $sourceData = array();
- $sql = $this->setFilterSQL($chart);
- $results = $this->bi->queryWithDriver($chart->driver, $sql);
- $group = $settings->group[0]->field;
- $groupCount = array();
- foreach($results as $result)
- {
- if($settings->metric[0]->agg == 'count')
- {
- if(!isset($groupCount[$result->$group])) $groupCount[$result->$group] = 0;
- $groupCount[$result->$group]++;
- }
- }
- arsort($groupCount);
- foreach($groupCount as $groupValue => $groupCount)
- {
- $sourceData[] = array($settings->group[0]->name => $groupValue, $settings->metric[0]->field => $groupCount);
- }
- }
- if(empty($sourceData)) $dimensions = array();
- $component->option->dataset->dimensions = $dimensions;
- $component->option->dataset->source = $sourceData;
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build piecircle chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildPieCircleChart($component, $chart)
- {
- $option = new stdclass();
- $option->type = 'nomal';
- $option->series = array();
- $option->series[0] = new stdclass();
- $option->series[0]->type = 'pie';
- $option->series[0]->radius = '70%';
- $option->series[0]->roseType = false;
- $option->backgroundColor = 'rgba(0,0,0,0)';
- $option->series[0]->data = array();
- $option->series[0]->data[0] = new stdclass();
- $option->series[0]->data[0]->value = array();
- $option->series[0]->data[0]->value[0] = 0;
- $option->series[0]->data[1] = new stdclass();
- $option->series[0]->data[1]->value = array();
- $option->series[0]->data[1]->value[0] = 0;
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "PieCircle";
- $component->chartConfig = json_decode('{"key":"PieCircle","chartKey":"VPieCircle","conKey":"VCPieCircle","title":"饼图","category":"Pies","categoryName":"饼图","package":"Charts","chartFrame":"echarts","image":"/static/png/pie-circle-258fcce7.png"}');
- $component->option = $option;
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->metric))
- {
- $sourceData = array();
- $sql = $this->setFilterSQL($chart);
- $results = $this->bi->queryWithDriver($chart->driver, $sql);
- $group = $settings->group[0]->field;
- $groupCount = array();
- foreach($results as $result)
- {
- if($settings->metric[0]->agg == 'count')
- {
- if(!isset($groupCount[$result->$group])) $groupCount[$result->$group] = 0;
- $groupCount[$result->$group]++;
- }
- }
- foreach($groupCount as $groupValue => $groupCount) $sourceData[$groupValue] = $groupCount;
- }
- $doneData = round((array_sum($sourceData) != 0 and !empty($sourceData['done'])) ? $sourceData['done'] / array_sum($sourceData) : 0, 4);
- $component->option->dataset = $doneData;
- if(!isset($component->option->series) || !is_array($component->option->series)) $component->option->series = $option->series;
- if(!isset($component->option)) $component->option = $option;
- $component->option->series[0]->data = array((object)array('value'=> $doneData), (object)array('value'=> 1 - $doneData));
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build water polo chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildWaterPolo($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "PieCircle";
- $component->chartConfig = json_decode('{"key":"WaterPolo","chartKey":"VWaterPolo","conKey":"VCWaterPolo","title":"水球图","category":"Mores","categoryName":"更多","package":"Charts","chartFrame":"common","image":"water_WaterPolo.png"}');
- $component->option = json_decode('{"type":"nomal","series":[{"type":"liquidFill","radius":"90%","roseType":false}],"backgroundColor":"rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- $sourceData = 0;
- if($settings and isset($settings->metric))
- {
- $sql = $this->setFilterSQL($chart);
- $result = $this->dao->query($sql)->fetch();
- $group = $settings->group[0]->field;
- $sourceData = zget($result, $group, 0);
- if(empty($sourceData)) $sourceData = 0;
- }
- $component->option->dataset = $sourceData;
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Get waterpolo option.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function getWaterPoloOption($component, $chart, $filters)
- {
- $value = 0;
- if(!$chart->settings)
- {
- $component->chartConfig = json_decode($this->config->screen->chartConfig['WaterPolo']);
- $component->option = json_decode($this->config->screen->chartOption['WaterPolo']);
- $component->key = $component->chartConfig->key;
- }
- else
- {
- $setting = json_decode($chart->settings, true)[0];
- $options = $this->bi->genWaterPolo(json_decode($chart->fields, true), $setting, $chart->sql, $filters, $chart->driver);
- $value = $options['series'][0]['data'][0];
- }
- return $this->prepareWaterPoloDataset($component, $value);
- }
- /**
- * Get option of metric chart.
- *
- * @param object $metric
- * @param array $resultHeader
- * @param array $resultData
- * @param object $component
- * @access public
- * @return object
- */
- public function getMetricChartOption($metric, $resultHeader, $resultData, $component = null)
- {
- $chartOption = $this->metric->getEchartsOptions($resultHeader, $resultData);
- if(!$chartOption) return false;
- if(isset($component) && isset($component->option->chartOption))
- {
- $preChartOption = $component->option->chartOption;
- $preChartOption->series = $chartOption['series'];
- if(!isset($preChartOption->xAxis)) $preChartOption->xAxis = $chartOption['xAxis'];
- $preChartOption->xAxis->data = $chartOption['xAxis']['data'];
- return $preChartOption;
- }
- if(!isset($chartOption['title']))
- {
- $chartOption['title'] = array();
- $chartOption['title']['show'] = false;
- $chartOption['title']['titleShow'] = true;
- $chartOption['title']['textStyle'] = array('color' => '#BFBFBF');
- }
- $chartOption['title']['text'] = $metric->name;
- $chartOption['backgroundColor'] = "#0B1727FF";
- if(!isset($chartOption['legend'])) $chartOption['legend'] = array();
- $chartOption['legend']['textStyle']['color'] = 'white';
- $chartOption['legend']['inactiveColor'] = 'gray';
- return $chartOption;
- }
- /**
- * Get option of metric table.
- *
- * @param array $resultHeader
- * @param array $resultData
- * @param array $filterParams
- * @param object $component
- * @access public
- * @return object
- */
- public function getMetricTableOption($metric, $resultHeader, $resultData, $component = null)
- {
- $this->loadModel('metric');
- $isObjectMetric = $this->metric->isObjectMetric($resultHeader);
- $dateType = $metric->dateType;
- list($groupHeader, $groupData) = $this->metric->getGroupTable($resultHeader, $resultData, $metric->dateType, false);
- $tableOption = new stdclass();
- if(!empty($component) && isset($component->option->tableOption)) $tableOption = $component->option->tableOption;
- $tableOption->headers = $isObjectMetric ? $this->getMetricHeaders($groupHeader, $dateType) : array($groupHeader);
- $tableOption->data = $groupData;
- $tableOption->scope = $metric->scope;
- return $tableOption;
- }
- /**
- * Filter metric data.
- *
- * @param array $data
- * @param string $dateType
- * @param bool $isObjectMetric
- * @param array $filters
- * @access public
- * @return array
- */
- public function filterMetricData($data, $dateType, $isObjectMetric, $filters = array())
- {
- if(empty($filters)) return $data;
- if($isObjectMetric)
- {
- if(isset($filters['scope']))
- {
- $scopeFilter = $filters['scope'];
- $objectPairs = $this->loadModel('metric')->getPairsByScope($scopeFilter->type);
- $selectedObjects = array_intersect_key($objectPairs, array_flip($scopeFilter->value));
- foreach($data as $index => $row)
- {
- if(!in_array($row['scope'], $selectedObjects)) unset($data[$index]);
- }
- }
- $filteredData = array();
- if(isset($filters['begin'], $filters['end']))
- {
- $beginFilter = $filters['begin']->$dateType;
- $endFilter = $filters['end']->$dateType;
- foreach($data as $index => $row)
- {
- $filteredData[$index] = array_filter($row, function($value, $key) use ($beginFilter, $endFilter)
- {
- if($key == 'scope') return true;
- if($key >= $beginFilter && $key <= $endFilter) return true;
- return false;
- }, ARRAY_FILTER_USE_BOTH);
- }
- }
- else
- {
- $filteredData = $data;
- }
- }
- else
- {
- $beginFilter = $filters['begin']->$dateType;
- $endFilter = $filters['end']->$dateType;
- $filteredData = array_filter($data, function($row) use ($beginFilter, $endFilter)
- {
- if($row['date'] >= $beginFilter && $row['date'] <= $endFilter) return true;
- });
- }
- return array_values($filteredData);
- }
- /**
- * 获取度量项卡片参数。
- * Get card option of metric.
- *
- * @param object $metric
- * @access public
- * @return object
- */
- public function getMetricCardOption($metric, $resultData, $component = null)
- {
- $this->loadModel('metric');
- $option = new stdclass();
- if(empty($component))
- {
- $option->displayType = 'normal';
- $option->cardType = 'A';
- $option->dateType = $metric->dateType;
- $option->bgColor = '#26292EFF';
- $option->border = array('color' => '#515458FF', 'width' => 1, 'radius' => 2);
- $option->scope = $metric->scope;
- $option->objectPairs = array();
- }
- else
- {
- $option = $component->option->card;
- }
- $option->data = $resultData;
- $option->filterValue = (is_array($option->data) && !empty($option->data)) ? current($option->data) : array();
- return $option;
- }
- /**
- * Get table headers of metric in screen designer.
- *
- * @param array $resultHeaders
- * @param string $dateType
- * @param object $filters
- * @access public
- * @return object
- */
- public function getMetricHeaders($resultHeader, $dateType)
- {
- $headers = array_fill(0, 2, array());
- foreach($resultHeader as $head)
- {
- if($head['name'] == 'scope')
- {
- if($dateType != 'year') $head['rowspan'] = 2;
- $headers[0][] = $head;
- }
- else
- {
- $row = $dateType == 'year' ? 0 : 1;
- $head['type'] = $dateType;
- $head['value'] = $head['name'];
- $headers[$row][] = $head;
- }
- }
- $dateGroups = array_count_values(array_column($resultHeader, 'headerGroup'));
- foreach($dateGroups as $date => $count)
- {
- $dateGroup = array();
- $dateGroup['colspan'] = $count;
- $dateGroup['title'] = $date;
- $dateGroup['name'] = $date;
- $dateGroup['value'] = substr($date, 0, 4);
- $dateGroup['type'] = 'year';
- $headers[0][] = $dateGroup;
- }
- if($dateType == 'year') unset($headers[1]);
- return $headers;
- }
- /**
- * Build radar chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildRadarChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "Radar";
- $component->chartConfig = json_decode('{"key":"Radar","chartKey":"VRadar","conKey":"VCRadar","title":"雷达图","category":"Mores","categoryName":"更多","package":"Charts","chartFrame":"common","image":"/static/png/radar-91567f95.png"}');
- $component->option = json_decode('{"radar":{"indicator":[{"name":"数据1","max":6500},{"name":"数据2","max":16000},{"name":"数据3","max":30000},{"name":"数据4","max":38000},{"name":"数据5","max":52000}]},"series":[{"name":"radar","type":"radar","areaStyle":{"opacity":0.1},"data":[{"name":"data1","value":[4200,3000,20000,35000,50000]}]}],"backgroundColor":"rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- else
- {
- $indicator = array();
- $seriesData = array();
- if($chart->sql)
- {
- $settings = json_decode($chart->settings);
- if($settings and isset($settings->metric))
- {
- $sql = $this->setFilterSQL($chart);
- $results = $this->bi->queryWithDriver($chart->driver, $sql);
- $group = $settings->group[0]->field;
- $metrics = array();
- foreach($settings->metric as $metric)
- {
- $metrics[$metric->key] = array('field' => $metric->field, 'name' => $metric->name, 'value' => 0);
- }
- foreach($results as $result)
- {
- if(isset($metrics[$result->$group]))
- {
- $field = $metrics[$result->$group]['field'];
- $metrics[$result->$group]['value'] += $result->$field;
- }
- }
- $max = 0;
- foreach($metrics as $data)
- {
- if($data['value'] > $max) $max = $data['value'];
- }
- $data = array('name' => '', 'value' => array());
- $value = array();
- foreach($metrics as $key => $metric)
- {
- $indicator[] = array('name' => $metric['name'], 'max' => $max);
- $data['value'][] = $metric['value'];
- $value[] = $metric['value'];
- }
- $seriesData[] = $data;
- }
- $component->option->dataset->radarIndicator = $indicator;
- $component->option->radar->indicator = $indicator;
- $component->option->dataset->seriesData = $seriesData;
- $component->option->series[0]->data[0]->value = $value;
- }
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Build org chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildOrgChart($component, $chart)
- {
- //TODO
- }
- /**
- * Build funnel chart.
- *
- * @param object $component
- * @param object $chart
- * @access public
- * @return object
- */
- public function buildFunnelChart($component, $chart)
- {
- if(!$chart->settings)
- {
- $component->request = json_decode('{"requestDataType":0,"requestHttpType":"get","requestUrl":"","requestIntervalUnit":"second","requestContentType":0,"requestParamsBodyType":"none","requestSQLContent":{"sql":"select * from where"},"requestParams":{"Body":{"form-data":{},"x-www-form-urlencoded":{},"json":"","xml":""},"Header":{},"Params":{}}}');
- $component->events = json_decode('{"baseEvent":{},"advancedEvents":{}}');
- $component->key = "Funnel";
- $component->chartConfig = json_decode('{"key":"Funnel","chartKey":"VFunnel","conKey":"VCFunnel","title":"漏斗图","category":"Mores","categoryName":"更多","package":"Charts","chartFrame":"echarts","image":"/static/png/funnel-d032fdf6.png"}');
- $component->option = json_decode('{"dataset":{"dimensions":["product","dataOne"],"source":[{"product":"data1","dataOne":20},{"product":"data2","dataOne":40},{"product":"data3","dataOne":60},{"product":"data4","dataOne":80},{"product":"data5","dataOne":100}]},"series":[{"name":"Funnel","type":"funnel","gap":5,"label":{"show":true,"position":"inside"}}],"backgroundColor":"rgba(0,0,0,0)"}');
- return $this->setComponentDefaults($component);
- }
- }
- /**
- * Get burn data.
- *
- * @access public
- * @return array
- */
- public function getBurnData()
- {
- $type = 'withdelay';
- $this->loadModel('execution');
- $executionData = array();
- $executions = $this->execution->getList(0, 'sprint', 'doing') + $this->execution->getList(0, 'stage', 'doing');
- $projectIdList = array_column($executions, 'project');
- $parents = array_column($executions, 'parent');
- $projectPairs = $this->dao->select('id,name')->from(TABLE_PROJECT)->where('id')->in($projectIdList)->fetchPairs();
- foreach($executions as $executionID => $execution)
- {
- if(in_array($executionID, $parents)) continue; // 过滤父阶段
- /* Splice project name for the execution name. */
- $projectName = zget($projectPairs, $execution->project, '');
- $execution->name = $projectName ? $projectName . '--' . $execution->name : $execution->name;
- /* Get date list. */
- if(((strpos('closed,suspended', $execution->status) === false and helper::today() > $execution->end)
- or ($execution->status == 'closed' and substr($execution->closedDate, 0, 10) > $execution->end)
- or ($execution->status == 'suspended' and $execution->suspendedDate > $execution->end))
- and strpos($type, 'delay') === false)
- $type .= ',withdelay';
- $deadline = $execution->status == 'closed' ? substr($execution->closedDate, 0, 10) : $execution->suspendedDate;
- $deadline = strpos('closed,suspended', $execution->status) === false ? helper::today() : $deadline;
- $endDate = (strpos($type, 'withdelay') !== false and $deadline > $execution->end) ? $deadline : $execution->end;
- list($dateList, $interval) = $this->execution->getDateList($execution->begin, $endDate, $type, 0, 'Y-m-d', $deadline);
- $executionEnd = strpos($type, 'withdelay') !== false ? $execution->end : '';
- $chartData = $this->execution->buildBurnData($executionID, $dateList, 'left', $executionEnd);
- $execution->chartData = $chartData;
- $executionData[$executionID] = $execution;
- }
- return $executionData;
- }
- /**
- * Init component.
- *
- * @param object $chart
- * @param string $type
- * @param object $component
- * @access public
- * @return void
- */
- public function initComponent($chart, $type, $component = null)
- {
- if($type == 'metric') return $this->initMetricComponent($chart, $component);
- if($type == 'chart' || $type == 'pivot') return $this->initChartAndPivotComponent($chart, $type, $component);
- return array($component, false);
- }
- /**
- * Init metric component.
- *
- * @param object $metric
- * @param object $component
- * @access public
- * @return array
- */
- public function initMetricComponent($metric, $component = null)
- {
- if(!$component) $component = new stdclass();
- if(!isset($component->id)) $component->id = $metric->id;
- if(!isset($component->sourceID)) $component->sourceID = $metric->id;
- if(!isset($component->title)) $component->title = $metric->name;
- if(!isset($component->type)) $component->type = 'metric';
- if(!isset($component->chartConfig)) $component->chartConfig = json_decode($this->config->screen->chartConfig['metric']);
- if(!isset($component->option)) $component->option = new stdclass();
- return array($component, false);
- }
- /**
- * Init chart or pivot component.
- *
- * @param object $metric
- * @param string $type
- * @param object $component
- * @access public
- * @return array
- */
- public function initChartAndPivotComponent($chart, $type, $component = null)
- {
- if(!$component) $component = new stdclass();
- if(!$chart) return array($component, false);
- $chartID = $chart->id;
- $chartName = $chart->name;
- $settings = is_string($chart->settings) ? json_decode($chart->settings) : $chart->settings;
- $builtin = $chart->builtin;
- $isBuiltin = ($builtin and !in_array($chartID, $this->config->screen->builtinChart));
- if(!isset($component->id)) $component->id = $chartID;
- if(!isset($component->sourceID)) $component->sourceID = $chartID;
- if(!isset($component->title)) $component->title = $chartName;
- if($type == 'chart') $chartType = ($chart->builtin and !in_array($chart->id, $this->config->screen->builtinChart)) ? $chart->type : $settings[0]->type;
- if($type == 'pivot') $chartType = 'table';
- if($type == 'metric') $chartType = 'metric';
- $component->type = $chartType;
- $typeChanged = false;
- // Get type is changed or not.
- if(isset($component->chartConfig))
- {
- $componentType = $chartType;
- foreach($this->config->screen->chartConfig as $type => $chartConfig)
- {
- $chartConfig = json_decode($chartConfig, true);
- if($chartConfig['key'] == $component->chartConfig->key) $componentType = $type;
- }
- $typeChanged = $chartType != $componentType;
- }
- // New component type or change component type.
- if(!isset($component->chartConfig) or $typeChanged)
- {
- $chartConfig = json_decode(zget($this->config->screen->chartConfig, $chartType));
- if(empty($chartConfig)) return null;
- $component->chartConfig = $chartConfig;
- $component->key = $chartConfig->key;
- }
- if(!isset($component->option) or $typeChanged)
- {
- $component->option = new stdclass();
- $component->option->dataset = new stdclass();
- }
- $component = $this->initOptionTitle($component, $type, $chartName);
- if(!isset($component->option->dataset)) $component->option->dataset = new stdclass();
- $component->chartConfig->title = $chartName;
- $component->chartConfig->sourceID = $component->sourceID;
- if($component->type != 'metric') $component->chartConfig->version = $chart->version;
- return array($component, $typeChanged);
- }
- public function initOptionTitle($component, $type, $chartName)
- {
- if($type == 'pivot')
- {
- if(!isset($component->option->caption)) $component->option->caption = $chartName;
- }
- elseif($type == 'chart')
- {
- if(!isset($component->option->title))
- {
- $component->option->title = new stdclass();
- $component->option->title->text = $chartName;
- $component->option->title->show = false;
- $component->option->title->titleShow = true;
- }
- }
- return $component;
- }
- /**
- * Check if the Chart is in use.
- *
- * @param int $chartID
- * @param string $type
- * @access public
- * @return void
- */
- public function checkIFChartInUse($chartID, $type = 'chart')
- {
- static $screenList = array();
- if(empty($screenList)) $screenList = $this->dao->select('scheme')->from(TABLE_SCREEN)->where('deleted')->eq(0)->andWhere('status')->eq('published')->fetchAll();
- foreach($screenList as $screen)
- {
- $scheme = json_decode($screen->scheme);
- if(empty($scheme->componentList)) continue;
- foreach($scheme->componentList as $component)
- {
- if(!empty($component->isGroup))
- {
- foreach($component->groupList as $key => $groupComponent)
- {
- if(!isset($groupComponent->chartConfig)) continue;
- $sourceID = zget($groupComponent->chartConfig, 'sourceID', '');
- $package = zget($groupComponent->chartConfig, 'package', '');
- $sourceType = $this->getChartType($package);
- if($chartID == $sourceID and $type == $sourceType) return true;
- }
- }
- else
- {
- if(!isset($component->chartConfig)) continue;
- $sourceID = zget($component->chartConfig, 'sourceID', '');
- $package = zget($component->chartConfig, 'package', '');
- $sourceType = $this->getChartType($package);
- if($chartID == $sourceID and $type == $sourceType) return true;
- }
- }
- }
- return false;
- }
- /**
- * Get chart type.
- *
- * @param string $type
- * @access public
- * @return string
- */
- public function getChartType($type)
- {
- if($type == 'Tables' || $type == 'pivot') return 'pivot';
- if($type == 'Metrics') return 'metric';
- return 'chart';
- }
- /**
- * 构建大屏图表的数据源。
- * Build dataset of chart.
- *
- * @param int $chartID
- * @param string $sql
- * @access public
- * @return array
- */
- public function buildDataset($chartID, $driver, $sql = '')
- {
- if(in_array($chartID, $this->config->screen->phpChart)) return $this->getDatasetForUsageReport($chartID);
- return $this->bi->queryWithDriver($driver, $sql);
- }
- /**
- * 获取应用健康度体检报告的数据源。
- * Build dataset for usage report.
- *
- * @param int $chartID
- * @access public
- * @return array
- */
- public function getDatasetForUsageReport($chartID)
- {
- $year = $this->filter->year;
- $month = $this->filter->month;
- $projectList = $this->getUsageReportProjects($year, $month);
- $productList = $this->getUsageReportProducts($year, $month);
- if($chartID == 20002) return $this->getActiveUserTable($year, $month, $projectList);
- if($chartID == 20012) return $this->getProductStoryTable($year, $month, $productList);
- if($chartID == 20011) return $this->getProductTestTable($year, $month, $productList);
- if($chartID == 20004) return $this->getActiveProductCard($year, $month);
- if($chartID == 20007) return $this->getActiveProjectCard($year, $month);
- if($chartID == 20013) return $this->getProjectStoryTable($year, $month, $projectList);
- if($chartID == 20010) return $this->getProjectTaskTable($year, $month, $projectList);
- }
- /**
- * 获取应用健康度体检报告的活跃账号数项目间对比表格。
- * Get table of active account per project in usage report.
- *
- * @param string $year
- * @param string $month
- * @param array $projectList
- * @access public
- * @return array
- */
- public function getActiveUserTable($year, $month, $projectList)
- {
- $date = date("Y-m-t", strtotime("$year-$month"));
- $loginUserList = $this->dao->select('distinct actor')->from(TABLE_ACTION)
- ->where('objectType')->eq('user')
- ->andWhere('action')->eq('login')
- ->andWhere('year(date)')->eq($year)
- ->andWhere('month(date)')->eq($month)
- ->fetchPairs();
- $dataset = array();
- foreach($projectList as $projectID => $projectName)
- {
- $teamMemberList = $this->dao->select('t2.id, t2.account')->from(TABLE_TEAM)->alias('t1')
- ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account=t2.account')
- ->where('t1.root')->eq($projectID)
- ->andWhere('t1.type')->eq('project')
- ->andWhere('date(t1.join)')->le($date)
- ->andWhere('t2.deleted')->eq('0')
- ->fetchPairs();
- $activeUser = array_filter($teamMemberList, function($item) use ($loginUserList)
- {
- return in_array($item, $loginUserList);
- });
- $row = new stdclass();
- $row->id = $projectID;
- $row->name = $projectName;
- $row->year = $year;
- $row->month = $month;
- $row->totalAccount = count($teamMemberList);
- $row->activeAccount = count($activeUser);
- $row->ratio = $row->totalAccount == 0 ? '0.00%' : number_format(($row->activeAccount/$row->totalAccount) * 100, 2) . '%';
- $dataset[] = $row;
- }
- return $dataset;
- }
- /**
- * 获取应用健康度体检报告的活跃项目数卡片。
- * Get card of active project in usage report.
- *
- * @param string $year
- * @param string $month
- * @access public
- * @return array
- */
- public function getActiveProjectCard($year, $month)
- {
- return $this->dao->select('count(distinct t1.project) as count')->from(TABLE_ACTION)->alias('t1')
- ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project=t2.id')
- ->where('t1.project')->ne(0)
- ->andWhere('year(t1.date)')->eq($year)
- ->andWhere('month(t1.date)')->eq($month)
- ->andWhere('t2.type')->eq('project')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere("NOT FIND_IN_SET('or', t2.vision)")
- ->fetchAll();
- }
- /**
- * 获取应用健康度体检报告的活跃产品数卡片。
- * Get card of active product in usage report.
- *
- * @param string $year
- * @param string $month
- * @access public
- * @return array
- */
- public function getActiveProductCard($year, $month)
- {
- $noDeletedProductList = $this->dao->select('id')->from(TABLE_PRODUCT)
- ->where('deleted')->eq('0')
- ->andWhere('shadow')->eq(0)
- ->fetchPairs();
- $activeProductList = $this->dao->select('distinct product')->from(TABLE_ACTION)
- ->where('product')->ne(',0,')
- ->andWhere('product')->ne(',,')
- ->andWhere('product')->ne(',,0,,')
- ->andWhere('objectType')->notin('project,execution,task')
- ->andWhere('year(date)')->eq($year)
- ->andWhere('month(date)')->eq($month)
- ->fetchPairs();
- $activeProductCount = 0;
- foreach($activeProductList as $product)
- {
- $productID = trim($product, ',');
- if(in_array($productID, $noDeletedProductList)) $activeProductCount ++;
- }
- $activeProductCard = new stdclass();
- $activeProductCard->count = $activeProductCount;
- $activeProductCard->year = $year;
- $activeProductCard->month = $month;
- return array($activeProductCard);
- }
- /**
- * 获取应用健康度体检报告的 产品测试表。
- * Get table of product test summary in usage report.
- *
- * @param string $year
- * @param string $month
- * @param array $productList
- * @access public
- * @return array
- */
- public function getProductTestTable($year, $month, $productList)
- {
- $dataset = array();
- foreach($productList as $productID => $productName)
- {
- $createdCaseCount = $this->dao->select('count(t2.id) as count')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.id=t2.product')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('year(t2.openedDate)')->eq($year)
- ->andWhere('month(t2.openedDate)')->eq($month)
- ->andWhere('t1.id')->eq($productID)
- ->fetch();
- $linkedBugCount = $this->dao->select('count(t3.id) as count')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.id=t2.product')
- ->leftJoin(TABLE_BUG)->alias('t3')->on('t2.id=t3.case')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t3.deleted')->eq('0')
- ->andWhere('year(t2.openedDate)')->eq($year)
- ->andWhere('month(t2.openedDate)')->eq($month)
- ->andWhere('t1.id')->eq($productID)
- ->fetch();
- $createdBugCount = $this->dao->select('count(t2.id) as count')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_BUG)->alias('t2')->on('t1.id=t2.product')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('year(t2.openedDate)')->eq($year)
- ->andWhere('month(t2.openedDate)')->eq($month)
- ->andWhere('t1.id')->eq($productID)
- ->fetch();
- $fixedBugList = $this->dao->select('t2.id,datediff(t2.closedDate, t2.openedDate) as fixedCycle')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_BUG)->alias('t2')->on('t1.id=t2.product')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t2.status')->eq('closed')
- ->andWhere('t2.resolution')->eq('fixed')
- ->andWhere('year(t2.closedDate)')->eq($year)
- ->andWhere('month(t2.closedDate)')->eq($month)
- ->andWhere('t1.id')->eq($productID)
- ->fetchPairs();
- $row = new stdclass();
- $row->id = $productID;
- $row->name = $productName;
- $row->year = $year;
- $row->month = $month;
- $row->createdCases = $createdCaseCount->count;
- $row->avgBugsOfCase = $createdCaseCount->count == 0 ? 0 : round($linkedBugCount->count/$createdCaseCount->count, 2);
- $row->createdBugs = $createdBugCount->count;
- $row->fixedBugs = count($fixedBugList);
- $row->avgFixedCycle = count($fixedBugList) == 0 ? 0 : round(array_sum($fixedBugList)/count($fixedBugList), 2);
- if($row->createdCases === 0 && $row->avgBugsOfCase === 0 && $row->createdBugs === 0 && $row->fixedBugs === 0 && $row->avgFixedCycle === 0) continue;
- $dataset[] = $row;
- }
- return $dataset;
- }
- /**
- * 获取应用健康度体检报告的项目任务概况表。
- * Get table of project task summary in usage report.
- *
- * @param string $year
- * @param string $month
- * @param array $projectList
- * @access public
- * @return array
- */
- public function getProjectTaskTable($year, $month, $projectList)
- {
- $deletedExecutionList = $this->dao->select('id')->from(TABLE_EXECUTION)
- ->where('deleted')->eq('1')
- ->andWhere('type')->in('sprint,stage,kanban')
- ->fetchPairs();
- $dataset = array();
- foreach($projectList as $projectID => $projectName)
- {
- $createdTaskList = $this->dao->select('id,openedBy')->from(TABLE_TASK)
- ->where('project')->eq($projectID)
- ->andWhere('year(openedDate)')->eq($year)
- ->andWhere('month(openedDate)')->eq($month)
- ->andWhere('deleted')->eq('0')
- ->andWhere("NOT FIND_IN_SET('or', vision)")
- ->andWhere('execution')->notin($deletedExecutionList)
- ->fetchPairs();
- $finishedTaskList = $this->dao->select('id')->from(TABLE_TASK)
- ->where('project')->eq($projectID)
- ->andWhere('year(finishedDate)')->eq($year)
- ->andWhere('month(finishedDate)')->eq($month)
- ->andWhere('deleted')->eq('0')
- ->andWhere("NOT FIND_IN_SET('or', vision)")
- ->andWhere('execution')->notin($deletedExecutionList)
- ->fetchPairs();
- $row = new stdclass();
- $row->name = $projectName;
- $row->year = $year;
- $row->month = $month;
- $row->createdTasks = count(array_unique(array_keys($createdTaskList)));
- $row->finishedTasks = count(array_unique($finishedTaskList));
- $row->contributors = count(array_unique(array_values($createdTaskList)));
- if($row->createdTasks === 0 && $row->finishedTasks === 0 && $row->contributors === 0) continue;
- $dataset[] = $row;
- }
- return $dataset;
- }
- /**
- * 获取应用健康度体检报告的产品需求概况表。
- * Get table of product story summary in usage report.
- *
- * @param string $year
- * @param string $month
- * @param array $productList
- * @access public
- * @return array
- */
- public function getProductStoryTable($year, $month, $productList)
- {
- $releasedStories = $this->dao->select('t2.id, t1.id as product')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.id=t2.product')
- ->leftJoin(TABLE_ACTION)->alias('t3')->on('t2.id=t3.objectID')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t2.type')->eq('story')
- ->andWhere("NOT FIND_IN_SET('or', t2.vision)")
- ->andWhere('t2.stage')->eq('released')
- ->andWhere('t3.objectType')->eq('story')
- ->andWhere('t3.action')->eq('linked2release')
- ->andWhere('year(t3.date)')->eq($year)
- ->andWhere('month(t3.date)')->eq($month)
- ->fetchPairs();
- $releasedStoryGroups = array();
- foreach($releasedStories as $storyID => $productID)
- {
- if(!isset($releasedStoryGroups[$productID])) $releasedStoryGroups[$productID] = array();
- $releasedStoryGroups[$productID][] = $storyID;
- }
- $dataset = array();
- foreach($productList as $productID => $productName)
- {
- $createdStoryCount = $this->dao->select('count(t2.id) as count')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.id=t2.product')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t2.type')->eq('story')
- ->andWhere("NOT FIND_IN_SET('or', t2.vision)")
- ->andWhere('t1.id')->eq($productID)
- ->andWhere('year(t2.openedDate)')->eq($year)
- ->andWhere('month(t2.openedDate)')->eq($month)
- ->fetch();
- $finishedStoryList = $this->dao->select('t2.id')->from(TABLE_PRODUCT)->alias('t1')
- ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.id=t2.product')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t2.deleted')->eq('0')
- ->andWhere('t2.type')->eq('story')
- ->andWhere("NOT FIND_IN_SET('or', t2.vision)")
- ->andWhere('t1.id')->eq($productID)
- ->andWhere('t2.closedReason')->eq('done')
- ->andWhere('year(t2.closedDate)')->eq($year)
- ->andWhere('month(t2.closedDate)')->eq($month)
- ->fetchPairs();
- $releasedStoryList = isset($releasedStoryGroups[$productID]) ? $releasedStoryGroups[$productID] : array();
- $deliveredStoryCount = count(array_merge($finishedStoryList, (array)$releasedStoryList));
- $row = new stdclass();
- $row->id = $productID;
- $row->name = $productName;
- $row->createdStories = $createdStoryCount->count;
- $row->deliveredStories = $deliveredStoryCount;
- if($row->createdStories === 0 && $row->deliveredStories === 0) continue;
- $dataset[] = $row;
- }
- return $dataset;
- }
- /**
- * 获取应用健康度体检报告的项目需求概况表。
- * Get table of project story summary in usage report.
- *
- * @param string $year
- * @param string $month
- * @param array $projectList
- * @access public
- * @return array
- */
- public function getProjectStoryTable($year, $month, $projectList)
- {
- $releasedStories = $this->dao->select('t3.id,t1.id as projectID')->from(TABLE_PROJECT)->alias('t1')
- ->leftJoin(TABLE_PROJECTSTORY)->alias('t2')->on('t1.id=t2.project')
- ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story=t3.id')
- ->leftJoin(TABLE_ACTION)->alias('t4')->on('t3.id=t4.objectID')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t1.type')->eq('project')
- ->andWhere('t3.deleted')->eq('0')
- ->andWhere('t3.type')->eq('story')
- ->andWhere("NOT FIND_IN_SET('or', t1.vision)")
- ->andWhere("NOT FIND_IN_SET('or', t3.vision)")
- ->andWhere('t3.stage')->eq('released')
- ->andWhere('t4.objectType')->eq('story')
- ->andWhere('t4.action')->eq('linked2release')
- ->andWhere('year(t4.date)')->eq($year)
- ->andWhere('month(t4.date)')->eq($month)
- ->fetchPairs();
- $releasedStoryGroups = array();
- foreach($releasedStories as $storyID => $projectID)
- {
- if(!isset($releasedStoryGroups[$projectID])) $releasedStoryGroups[$projectID] = array();
- $releasedStoryGroups[$projectID][] = $storyID;
- }
- $dataset = array();
- foreach($projectList as $projectID => $projectName)
- {
- $createdStoryCount = $this->dao->select('count(t3.id) as count')->from(TABLE_PROJECT)->alias('t1')
- ->leftJoin(TABLE_PROJECTSTORY)->alias('t2')->on('t1.id=t2.project')
- ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story=t3.id')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t1.type')->eq('project')
- ->andWhere('t3.type')->eq('story')
- ->andWhere('t3.deleted')->eq('0')
- ->andWhere("NOT FIND_IN_SET('or', t1.vision)")
- ->andWhere("NOT FIND_IN_SET('or', t3.vision)")
- ->andWhere('t1.type')->eq('project')
- ->andWhere('t3.type')->eq('story')
- ->andWhere('t1.id')->eq($projectID)
- ->andWhere('year(t3.openedDate)')->eq($year)
- ->andWhere('month(t3.openedDate)')->eq($month)
- ->fetch();
- $finishedStoryList = $this->dao->select('t3.id')->from(TABLE_PROJECT)->alias('t1')
- ->leftJoin(TABLE_PROJECTSTORY)->alias('t2')->on('t1.id=t2.project')
- ->leftJoin(TABLE_STORY)->alias('t3')->on('t2.story=t3.id')
- ->where('t1.deleted')->eq('0')
- ->andWhere('t1.type')->eq('project')
- ->andWhere('t3.deleted')->eq('0')
- ->andWhere('t3.type')->eq('story')
- ->andWhere("NOT FIND_IN_SET('or', t1.vision)")
- ->andWhere("NOT FIND_IN_SET('or', t3.vision)")
- ->andWhere('t1.id')->eq($projectID)
- ->andWhere('t3.closedReason')->eq('done')
- ->andWhere('year(t3.closedDate)')->eq($year)
- ->andWhere('month(t3.closedDate)')->eq($month)
- ->fetchPairs();
- $releasedStoryList = isset($releasedStoryGroups[$projectID]) ? $releasedStoryGroups[$projectID] : array();
- $deliveredStoryCount = count(array_merge($finishedStoryList, (array)$releasedStoryList));
- $row = new stdclass();
- $row->id = $projectID;
- $row->name = $projectName;
- $row->createdStories = $createdStoryCount->count;
- $row->deliveredStories = $deliveredStoryCount;
- if($row->createdStories === 0 && $row->deliveredStories === 0) continue;
- $dataset[] = $row;
- }
- return $dataset;
- }
- /**
- * 获取应用健康度体检报告的项目列表。
- * Get project list for usage report.
- *
- * @param string $year
- * @param string $month
- * @access public
- * @return array
- */
- public function getUsageReportProjects($year, $month)
- {
- $date = date("Y-m-t", strtotime("$year-$month"));
- return $this->dao->select('id,name')->from(TABLE_PROJECT)
- ->where('type')->eq('project')
- ->andWhere('deleted')->eq('0')
- ->andWhere('date(openedDate)')->le($date)
- ->andWhere("NOT FIND_IN_SET('or', vision)")
- ->andWhere('date(closedDate)', true)->gt($date)
- ->orWhere('date(closedDate)')->eq('0000-00-00')
- ->orWhere('closedDate')->in(NULL)
- ->markRight(true)
- ->fetchPairs();
- }
- /**
- * 获取应用健康度体检报告的产品列表。
- * Get product list for usage report.
- *
- * @param string $year
- * @param string $month
- * @access public
- * @return array
- */
- public function getUsageReportProducts($year, $month)
- {
- $date = date("Y-m-t", strtotime("$year-$month"));
- return $this->dao->select('id,name')->from(TABLE_PRODUCT)
- ->where('deleted')->eq('0')
- ->andWhere('shadow')->eq(0)
- ->andWhere('date(createdDate)')->le($date)
- ->fetchPairs();
- }
- /**
- * Get screen thumbnail.
- *
- * @param array $screens
- * @access public
- * @return array
- */
- public function getThumbnail($screens)
- {
- $screenIds = array_column($screens, 'id');
- $images = $this->loadModel('file')->getByObject('screen', $screenIds);
- foreach($screens as $screen)
- {
- $currentImages = array_filter($images, function($image) use ($screen)
- {
- return $image->objectID == $screen->id;
- });
- if(empty($currentImages)) continue;
- $image = end($currentImages);
- $screen->cover = helper::createLink('file', 'read', "fileID={$image->id}", 'png');
- }
- return $screens;
- }
- /**
- * Remove scheme field of screen.
- *
- * @param array $screens
- * @access public
- * @return array
- */
- public function removeScheme($screens)
- {
- foreach($screens as $screen) unset($screen->scheme);
- return $screens;
- }
- }
|