Code

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