Code

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