Code

Updated stats
[gosa.git] / gosa-core / plugins / generic / statistics / class_statistics.inc
1 <?php
3 class statistics extends plugin
4 {
5     var $plHeadline = 'Statistics';
6     var $plDescription = 'GOsa usage statistics';
7     var $plShortIcon = 'statistics.png';
8     var $plIcon = 'plugin.png';
9     
10     var $rpcHandle = NULL;
11     var $rpcConfigured = FALSE;
13     var $statisticData = array();
15     var $graphIDs = array();
16     var $skipSeries = array();
17     var $seriesListPerGraph = array();
19     var $legendR = 235;
20     var $legendG = 235;
21     var $legendB = 235;
23     var $font = "./themes/default/fonts/LiberationSans-Regular.ttf";
25     var $graph1DatePicker1 = 0;
26     var $graph1DatePicker2 = 0;
28     var $unsbmittedFiles = array();
30     function __construct($config)
31     {
32         plugin::plugin($config, NULL);
34         // Init start and stop times for graph 1
35         $this->graph1DatePicker1 = date('d.m.Y', time() - 14 * 24 * 60 *60);
36         $this->graph1DatePicker2 = date('d.m.Y', time());
38         // First try to retrieve values via RPC
39         $this->rpcConfigured = FALSE;
40         if ($this->config->get_cfg_value("core","gosaRpcServer") != ""){
41             $this->rpcConfigured = TRUE;
42             $this->rpcHandle = $this->config->getRpcHandle(
43                     "http://10.3.64.59:4000",
44                     "65717fe6-9e3e-11df-b010-5452005f1250",
45                     "WyukwauWoid2",
46                     TRUE);
47         }
49         // Get list of unsubmitted files.
50         $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
52         // Collect category translations
53         $this->catTranslations = array();
54         foreach($this->config->configRegistry->getListOfPlugins() as $plugin => $data){
55             if(isset($data['plCategory'])){
56                 foreach($data['plCategory'] as $id => $name){
57                     if(!is_numeric($id)){
58                         $this->catTranslations[$id] = $name['description'];
59                     }
60                 }
61             }
62         }
63     }
65     /*! \brief      Returns a list local stored statistic files
66         @param      Array   A list of filenames and dates.
67      */ 
68     function getLocalStatisticsFiles()
69     {
70         $res = stats::getLocalStatFiles();
71         $tmp = array();
72         if(count($res)){
73             foreach($res as $file){
74                 $date = strtotime($file);
75                 if($date){
76                     $tmp[$file] = $date;
77                 }
78             }
79         }
80         return($tmp);
81     }
83    
84     /*! \brief      Returns a list of not transmitted stat files (except files for the current day)
85      *  @return     Array   A list of unsubmitted statistic files.
86      */ 
87     function getUnsubmittedStatistics()
88     {
89         $available = $this->getLocalStatisticsFiles();
90         $alreadyTransmitted = $this->getStatisticsDatesFromServer();
92         $unsubmitted = array();
93         foreach($available as $key => $day){
94             if(!isset($alreadyTransmitted[$key])) $unsubmitted [$key] = $day;
95         }
97         // Exclude statistic collection from today, they are still active and cannot be submitted.
98         $curDate =  date('Y-m-d');
99         if(isset($unsubmitted)) unset($unsubmitted[$curDate]);
100         return($unsubmitted);  
101     }
104     /*! \brief      Request a list of dates for which the server can return statistics.
105         @param      Array   A list of dates    $ret=[iso-str] = timestamp
106      */ 
107     function getStatisticsDatesFromServer()
108     {
109         // Do not request anything while rpc isn't configured.
110         if(!$this->rpcConfigured){
111             return(array());
112         }
113         
114         // Try to gather statistic dates from the backenbd.
115         $res = $this->rpcHandle->getInstanceStatDates();
116         $dates = array();
117         if(!$this->rpcHandle->success()){
118             msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
119         }else{
120             foreach($res as $date){
121                 $dates[$date] = strtotime($date);
122             }
123         }
124         $this->rpcHandle_Error = !$this->rpcHandle->success();
125         return($dates);
126     }
129     function execute()
130     {
131         $smarty = get_smarty();
132         $smarty->assign('graph1DatePicker1', $this->graph1DatePicker1);
133         $smarty->assign('graph1DatePicker2', $this->graph1DatePicker2);
135         $this->reloadGraphs();
137         // Do not render anything if we are not prepared to send and receive data via rpc.
138         $smarty->assign("rpcConfigured", $this->rpcConfigured);
139         $smarty->assign("validRpcHandle", TRUE);
140         if(!$this->rpcHandle){
141             $smarty->assign("validRpcHandle", FALSE);
142             return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
143         }
145         // Send stats 
146         if(isset($_POST['transmitStatistics'])){
147             $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
148             foreach($this->unsbmittedFiles as $filename => $date){
149                 $tmp = stats::dumpTables($filename);
150                 $dump = array();
151                 foreach($tmp as $entry){
152                     $dump[] = array_values($entry);
153                 }
154                 $res = $this->rpcHandle->updateInstanceStatus($dump);
155                 if(!$this->rpcHandle->success()){
156                     msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
157                 }else{
158                     stats::removeStatsFile($filename);
159                 }
160                 $this->rpcHandle_Error = !$this->rpcHandle->success();
161             }
162             $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
163         }
165         // Transmit daily statistics to GOsa-Server
166         if(isset($_POST['receiveStatistics']) && $this->rpcConfigured){
167             $start = strtotime($this->graph1DatePicker1);
168             $stop  = strtotime($this->graph1DatePicker2);
169             $res = $this->rpcHandle->getInstanceStats($start,$stop);
170             if(!$this->rpcHandle->success()){
171                 msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
172             }elseif($res){
173                 $this->statisticData = $this->prepareGraphData($res); 
174                 $this->reloadGraphs();
175             }
176             $this->rpcHandle_Error = !$this->rpcHandle->success();
177         }
180         $smarty->assign('seriesListPerGraph', $this->seriesListPerGraph);
181         $smarty->assign('skipSeries', $this->skipSeries);
182         $smarty->assign('graphIDs', $this->graphIDs);
183         $smarty->assign('unsbmittedFiles', count($this->unsbmittedFiles));
184         $smarty->assign('unsbmittedFilesMsg', sprintf(
185                     _("You have currently %s unsubmitted statistic collection, do you want to transmit them now?"),
186                     count($this->unsbmittedFiles)));
187     
188         $smarty->assign('rpcHandle_Error', $this->rpcHandle_Error);
189         return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
190     }
193     /*! \brief      Prepares the graph data we've received from the rpc-service.
194      *              This method will construct a usable data-array with converted 
195      *               date strings.
196      */
197     function prepareGraphData($res)
198     { 
200         /* Build up array which represents the amount of errors per
201          *  interval.
202          */
203         $gData = array();
204         foreach($res['errorsPerInterval'] as $dateStr => $data){
205             $date = strtotime($dateStr);
206             $gData['errorsPerInterval'][$date] = $data;
207         }
208         ksort($gData['errorsPerInterval']);
211         /* Build up timeline
212          */
213         $Xam = 5; 
214         $cnt = 0;
215         $numCnt = $res['errorsPerInterval'];
216         foreach($gData['errorsPerInterval'] as $date => $data){
217             if((count($numCnt) <= $Xam) || 
218                     ($cnt % (floor(count($numCnt) / $Xam )) == 0)){
219                 $gData['dates'][$date] = date('d.m.Y', $date);
220             }else{
221                 $gData['dates'][$date] = ' ';
222             }
223             $cnt ++;
224         }
225         ksort($gData['dates']);
227         
228         /* Build up 'actions per category' array, this will later
229          *   be represented using a pie chart.
230          */
231         $gData['actionsPerCategory'] = $res['actionsPerCategory'];
232         arsort($gData['actionsPerCategory']);
235         /* Build up system-info array per interval.
236          */
237         foreach($res['usagePerInterval'] as $dateStr => $data){
238             $date = strtotime($dateStr);
239             foreach($data as $type => $count){
240                 $gData['usagePerInterval'][$type][$date] = $count;
241             }
242         }
243         foreach($gData['usagePerInterval'] as $key => $data)
244             ksort($gData['usagePerInterval'][$key]);
247         /* Prepare actions-per-interval array.
248          */    
249         foreach($res['actionsPerInterval'] as $category => $data){
250             if(empty($category)) continue;
251             foreach($data as $dateStr => $count){
252                 $date = strtotime($dateStr);
253                 $gData['actionsPerInterval'][$category][$date]=$count;
254             }
255             ksort($gData['actionsPerInterval'][$category]);
256         }
257         $this->skipSeries[1] = array();
258         $this->skipSeries[2] = array();
259         $this->skipSeries[3] = array();
260         $this->skipSeries[4] = array();
261         $this->skipSeries[5] = array();
262         return($gData);
263     }
266     function check()
267     {
268         $messages = plugin::check();
269         return($messages);
270     }
273     function save_object()
274     {
275         plugin::save_object();
276         if(isset($_POST['graph1DatePicker1'])) $this->graph1DatePicker1 = get_post('graph1DatePicker1');
277         if(isset($_POST['graph1DatePicker2'])) $this->graph1DatePicker2 = get_post('graph1DatePicker2');
279     
280         // Get series to enable or disable
281         foreach($this->seriesListPerGraph as $graphId => $series){
282             $this->skipSeries[$graphId] = array();
283             if(isset($_POST["graphPosted_{$graphId}"])){
284                 foreach($series as $seriesName => $seriesDesc){
285                     if(!isset($_POST["addSeries_{$graphId}_{$seriesName}"])){
286                         $this->skipSeries[$graphId][] = $seriesName;
287                     }
288                 }
289             }
290         }
291     }
294     /*! \brief      This method tries to translate category names.
295      *  @param      The category name to translate
296      *  @return     String  The translated category names.
297      */
298     function getCategoryTranslation($name)
299     {
300         $ret ="";
302         // Extract category names from the given string.
303         $list = preg_split("/, /", $name);
305         // We do not have a category for systems directly, so we've to map all system types to 'System'.
306         // If we do not map to _(Systems) the graph legend will be half screen width.
307         if(count(array_intersect(array('server','terminal','workstation', 'opsi', 'component'), $list))){
308             return(_("Systems"));
309         }
311         // Walk through category names and try to find a translation.
312         foreach($list as $cat){
313             $cat = trim($cat);
314             if(isset($this->catTranslations[$cat])){
315                 $cat = _($this->catTranslations[$cat]);
316             }elseif(!empty($cat)){
317                 $cat = _($cat);
318             }
319             $ret .= $cat.", ";
320         }
321         return(rtrim($ret, ', '));
322     }
325     /*! \brief  Reload the graph images.
326      */ 
327     function reloadGraphs()
328     {
329         new pChartInclude();
331         $gData = $this->statisticData;
332         if(!count($gData)){
333             return;
334         }
335         if(count($gData['actionsPerCategory'])){
336             $this->generateCategoryPieGraph($gData['actionsPerCategory']);
337         }
338         $this->generateActionsGraph($gData);
340         // Generate graph which displays the memory usage over time
341         $series = array(
342             'max_mem' => _('Max'),
343             'avg_mem' => _('Avergae'),
344             'min_mem' => _('Min'));
345         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Memory usage"),3);
347         // Generate graph which displays the cpu load over time
348         $series = array(
349             'max_load' => _('Max'),
350             'avg_load' => _('Avergae'),
351             'min_load' => _('Min'));
352         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("CPU load"),4);
354         // Generate graph which displays the render time
355         $series = array(
356             'max_render' => _('Max'),
357             'avg_render' => _('Avergae'),
358             'min_render' => _('Min'));
359         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Render time"),5);
361         // Generate graph which displays the plugin duration
362         $series = array(
363             'max_dur' => _('Max'),
364             'avg_dur' => _('Avergae'),
365             'min_dur' => _('Min'));
366         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Seconds per action"),6);
367     }
370     /*! \brief  Generates the line-graph which displays the plugin usage over time.
371      */ 
372     function generateActionsGraph($gData)
373     {
374         $lineMax = 100;
375         $errorMax = (max($gData['errorsPerInterval']) < 100)? 100:max($gData['errorsPerInterval']);
376         $dataSet = new pData;  
377         $seriesCnt = 0;
378         $gID = 2;
379         foreach($gData['actionsPerInterval'] as $category => $entriesPerDate){
380             if(empty($category) || in_array($category, $this->skipSeries[$gID])) continue;
382             // Add results to our data set.
383             $dataSet->AddPoint($entriesPerDate, $category);
384             $dataSet->SetSerieName($this->getCategoryTranslation($category), $category);
385             $dataSet->AddSerie($category);
387             // Detect maximum value, to adjust the Y-Axis
388             $tmpMax = max($entriesPerDate);
389             if($tmpMax > $lineMax) $lineMax = $tmpMax;
390             $seriesCnt ++;
391         }
393         // Add timeline
394         $dataSet->AddPoint($gData['dates'], 'date');
395         $dataSet->SetAbsciseLabelSerie('date');  
397         $chart = new pChart(800,230);  
398         $chart->setFixedScale(0.000,$lineMax);
399         $chart->setFontProperties("./themes/default/fonts/LiberationSans-Regular.ttf",10);  
400         $chart->setGraphArea(50,30,585,200);  
401         $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);  
402         $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);  
403         $chart->drawGraphArea(255,255,255,TRUE);  
404         $chart->drawGrid(4,TRUE,200,200,200,50);  
405         $chart->drawTreshold(0,143,55,72,TRUE,TRUE);  
406         $chart->drawTitle(50,22,"Plugin usage over time",50,50,50,585);  
407         $chart->drawScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2, TRUE);     
409         // Only draw this graph if we've at least one series to draw! 
410         if($seriesCnt){
411             $chart->drawFilledLineGraph($dataSet->GetData(),$dataSet->GetDataDescription(),50,TRUE);
412         }
414         // Do we've to add the errors series?
415         // If we have to, then add the error-data-series.
416         //  and set the color for the new error-series to red.
417         if(!in_array('errorsPerInterval', $this->skipSeries[$gID])){
419             // Set the color for the error Series to 'red'. 
420             // This has to be done before drawing the legend.
421             $chart->setColorPalette($seriesCnt,255,0,0);   
423             $dataSet->AddPoint($gData['errorsPerInterval'], 'Errors');
424             $dataSet->SetSerieName(_('Error'), 'Errors');
425             $dataSet->AddSerie('Errors');
426         }
428         
429         // Draw legend, but only if there is something to draw!
430         if($seriesCnt || !in_array('errorsPerInterval', $this->skipSeries[$gID])){
431             $chart->drawLegend(650,30,$dataSet->GetDataDescription(),255,255,255);  
432         }
434         // Draw the error graph on top of the other graphs now.
435         // But remove the category-graph before. 
436         if(!in_array('errorsPerInterval', $this->skipSeries[$gID])){
438             // Remove all graph series and add the error-series, then draw the new graph.
439             // (It is not relevant if it was really added before, so we simply remove all series!)
440             foreach($gData['actionsPerInterval'] as $category => $data){
441                 $dataSet->RemoveSerie($category);
442             }
443             $chart->setFixedScale(0,$errorMax);
444             $chart->drawRightScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,120,150,150,TRUE,0,2, TRUE);
445             $chart->drawBarGraph($dataSet->GetData(),$dataSet->GetDataDescription());
446         }
448         // Generate new and unique graph id
449         $this->graphIDs[$gID] = preg_replace("/[^0-9]/","",microtime(TRUE));
450         $file = '/tmp/graph_'.$this->graphIDs[$gID];
451         $chart->Render($file);
452         session::set('statistics::graphFile'.$this->graphIDs[$gID],$file);
454         // Keep a list of all selecteable data-series, to allow the user to disable
455         //  or enable series on demand.
456         $this->seriesListPerGraph[$gID] = array();
457         foreach($gData['actionsPerInterval'] as $key => $data){
458             $this->seriesListPerGraph[$gID][$key] = $this->getCategoryTranslation($key); 
459         }
460         $this->seriesListPerGraph[$gID]['errorsPerInterval'] = _("Error");
461  
462         return;
463     }
464    
466     /*! \brief  Generates a graph about system informations.
467      */ 
468     function generateSystemStatsGraph($gData, $key = "", $series= array(), $title = "", $gID=0 )
469     { 
470         // Add series data to dataSet        
471         $dataSet = new pData;  
472         $max = 0;
473         foreach($series as $seriesName => $seriesDesc){
474             if(isset($gData[$key][$seriesName])){
475                 $dataSet->AddPoint($gData[$key][$seriesName],$seriesName);
476                 $dataSet->SetSerieName($seriesDesc,$seriesName);
477                 if($max < max($gData[$key][$seriesName])) $max = max($gData[$key][$seriesName]);
478             }
479         }
480         $dataSet->AddAllSeries();  
481         $dataSet->AddPoint($gData['dates'], 'date');
482         $dataSet->SetAbsciseLabelSerie('date');  
483         
484         $chart = new pChart(800,230);  
485         $chart->setFixedScale(0.0001,($max*1.1));  
486         $chart->setFontProperties("./themes/default/fonts/LiberationSans-Regular.ttf",10);  
487         $chart->setGraphArea(50,30,585,200);  
488         $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);  
489         $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);  
490         $chart->drawGraphArea(255,255,255,TRUE);  
491         $chart->drawGrid(4,TRUE,200,200,200,50);  
492         $chart->drawTreshold(0,143,55,72,TRUE,TRUE);  
493         $chart->drawTitle(50,22,$title,50,50,50,585);  
494         $chart->drawLegend(650,30,$dataSet->GetDataDescription(),255,255,255);  
496         $chart->drawScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2, FALSE);
497         $chart->drawFilledCubicCurve($dataSet->GetData(),$dataSet->GetDataDescription(),.1,50); 
499         $this->graphIDs[$gID] = preg_replace("/[^0-9]/","",microtime(TRUE));
500         $file = '/tmp/graph_'.$this->graphIDs[$gID];
501         $chart->Render($file);
502         session::set('statistics::graphFile'.$this->graphIDs[$gID],$file);
503     }
506     /*! \brief  Generate the pie-chart which displays the overall-plugin-usage
507      */
508     function generateCategoryPieGraph($data)
509     {
510         // Sort data by usage count and slice array to get 
511         //  the eight most used categories
512         $gID = 1;
513         arsort($data);
514         $mostUsedCategories = array_slice($data,0,7);
516         // Get the rest of categories and combine them 'others'
517         $theRest = array_slice($data,7);
518         $mostUsedCategories['remaining'] = array_sum($theRest);
520         // Try to find a translation for the given category names
521         $values = array_values($mostUsedCategories);
522         $keys = array_keys($mostUsedCategories);
523         foreach($keys as $id => $cat){
524             $keys[$id] = $this->getCategoryTranslation($cat);
525         }
527         // Dataset definition   
528         $dataSet = new pData;  
529         $dataSet->AddPoint($values,"Serie1");  
530         $dataSet->AddAllSeries();  
531         $dataSet->AddPoint($keys,"Serie2");  
532         $dataSet->SetAbsciseLabelSerie("Serie2");  
534         // Set graph area
535         $x = 400;
536         $y = 200;
538         // Initialise the graph  
539         $chart = new pChart($x,$y);  
540         $chart->setFontProperties($this->font,10);  
541         $chart->drawPieGraph($dataSet->GetData(),$dataSet->GetDataDescription(),($x/3),($y/2)-10,($y/2),PIE_PERCENTAGE,TRUE,50,20,3);  
542         $chart->drawPieLegend(($x/3*2),15,$dataSet->GetData(),$dataSet->GetDataDescription(),
543                 $this->legendR,$this->legendG,$this->legendB);
545         // Store graph data
546         $this->graphIDs[$gID] = preg_replace("/[^0-9]/","",microtime(TRUE));
547         $file = '/tmp/graph_'.$this->graphIDs[$gID];
548         $chart->Render($file);
549         session::set('statistics::graphFile'.$this->graphIDs[$gID],$file);
550     }
552 ?>