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 $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         // Do not render anything if we are not prepared to send and receive data via rpc.
133         $smarty->assign("rpcConfigured", $this->rpcConfigured);
134         $smarty->assign("validRpcHandle", TRUE);
135         if(!$this->rpcConfigured || !$this->rpcHandle){
136             $smarty->assign("validRpcHandle", FALSE);
137             return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
138         }
140         // Send stats 
141         if(isset($_POST['transmitStatistics'])){
142             $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
143             foreach($this->unsbmittedFiles as $filename => $date){
144                 $tmp = stats::dumpTables($filename);
145                 $dump = array();
146                 foreach($tmp as $entry){
147                     $dump[] = array_values($entry);
148                 }
149                 $res = $this->rpcHandle->updateInstanceStatus($dump);
150                 if(!$this->rpcHandle->success()){
151                     msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
152                 }else{
153                     stats::removeStatsFile($filename);
154                     echo "Inserted ".$res." entries for date ".date('d.m.Y', $date)."<br>";
155                 }
156             }
157             $this->unsbmittedFiles = $this->getUnsubmittedStatistics();
158         }
160         // Transmit daily statistics to GOsa-Server
161         if(isset($_POST['receiveStatistics']) && $this->rpcConfigured){
162             $start = strtotime($this->graph1DatePicker1);
163             $stop  = strtotime($this->graph1DatePicker2);
164             $res = $this->rpcHandle->getInstanceStats($start,$stop);
165             if(!$this->rpcHandle->success()){
166                 msg_dialog::display(_("Error"),msgPool::rpcError($this->rpcHandle->get_error()),ERROR_DIALOG);
167             }elseif($res){
168                 $this->statisticData = $this->prepareGraphData($res); 
169                 $this->reloadGraphs();
170             }
171         }
173         $smarty->assign('graphID_1', $this->graphID_1);
174         $smarty->assign('graphID_2', $this->graphID_2);
175         $smarty->assign('graphID_3', $this->graphID_3);
176         $smarty->assign('graphID_4', $this->graphID_4);
177         $smarty->assign('graphID_5', $this->graphID_5);
178         $smarty->assign('graphID_6', $this->graphID_6);
179         $smarty->assign('unsbmittedFiles', count($this->unsbmittedFiles));
180         $smarty->assign('unsbmittedFilesMsg', sprintf(
181                     _("You have currently %s unsubmitted statistic collection, do you want to transmit them now?"),
182                     count($this->unsbmittedFiles)));
183         return($smarty->fetch(get_template_path('statistics.tpl', TRUE)));
184     }
187     /*! \brief      Prepares the graph data we've received from the rpc-service.
188      *              This method will construct a usable data-array with converted 
189      *               date strings.
190      */
191     function prepareGraphData($res)
192     { 
194         /* Build up array which represents the amount of errors per
195          *  interval.
196          */
197         $gData = array();
198         foreach($res['errorsPerInterval'] as $dateStr => $data){
199             $date = strtotime($dateStr);
200             $gData['errorsPerInterval'][$date] = $data;
201         }
202         ksort($gData['errorsPerInterval']);
205         /* Build up timeline
206          */
207         $Xam = 5; 
208         $cnt = 0;
209         $numCnt = $res['errorsPerInterval'];
210         foreach($gData['errorsPerInterval'] as $date => $data){
211             if((count($numCnt) <= $Xam) || 
212                     ($cnt % (floor(count($numCnt) / $Xam )) == 0)){
213                 $gData['dates'][$date] = date('d.m.Y', $date);
214             }else{
215                 $gData['dates'][$date] = ' ';
216             }
217             $cnt ++;
218         }
219         ksort($gData['dates']);
221         
222         /* Build up 'actions per category' array, this will later
223          *   be represented using a pie chart.
224          */
225         $gData['actionsPerCategory'] = $res['actionsPerCategory'];
226         arsort($gData['actionsPerCategory']);
229         /* Build up system-info array per interval.
230          */
231         foreach($res['usagePerInterval'] as $dateStr => $data){
232             $date = strtotime($dateStr);
233             foreach($data as $type => $count){
234                 $gData['usagePerInterval'][$type][$date] = $count;
235             }
236         }
237         foreach($gData['usagePerInterval'] as $key => $data)
238             ksort($gData['usagePerInterval'][$key]);
241         /* Prepare actions-per-interval array.
242          */    
243         foreach($res['actionsPerInterval'] as $category => $data){
244             if(empty($category)) continue;
245             foreach($data as $dateStr => $count){
246                 $date = strtotime($dateStr);
247                 $gData['actionsPerInterval'][$category][$date]=$count;
248             }
249             ksort($gData['actionsPerInterval'][$category]);
250         }
251         return($gData);
252     }
255     function check()
256     {
257         $messages = plugin::check();
258         return($messages);
259     }
262     function save_object()
263     {
264         plugin::save_object();
265         if(isset($_POST['graph1DatePicker1'])) $this->graph1DatePicker1 = get_post('graph1DatePicker1');
266         if(isset($_POST['graph1DatePicker2'])) $this->graph1DatePicker2 = get_post('graph1DatePicker2');
267     }
270     /*! \brief      This method tries to translate category names.
271      *  @param      The category name to translate
272      *  @return     String  The translated category names.
273      */
274     function getCategoryTranslation($name)
275     {
276         $ret ="";
278         // Extract category names from the given string.
279         $list = preg_split("/, /", $name);
281         // We do not have a category for systems directly, so we've to map all system types to 'System'.
282         // If we do not map to _(Systems) the graph legend will be half screen width.
283         if(count(array_intersect(array('server','terminal','workstation', 'opsi', 'component'), $list))){
284             return(_("Systems"));
285         }
287         // Walk through category names and try to find a translation.
288         foreach($list as $cat){
289             $cat = trim($cat);
290             if(isset($this->catTranslations[$cat])){
291                 $cat = _($this->catTranslations[$cat]);
292             }elseif(!empty($cat)){
293                 $cat = _($cat);
294             }
295             $ret .= $cat.", ";
296         }
297         return(rtrim($ret, ', '));
298     }
301     /*! \brief  Reload the graph images.
302      */ 
303     function reloadGraphs()
304     {
305         new pChartInclude();
306         $gData = $this->statisticData;
307         if(count($gData['actionsPerCategory'])){
308             $this->generateCategoryPieGraph($gData['actionsPerCategory']);
309         }
310         $this->generateActionsGraph($gData);
312         // Generate graph which displays the memory usage over time
313         $series = array(
314             'max_mem' => _('Max'),
315             'avg_mem' => _('Avergae'),
316             'min_mem' => _('Min'));
317         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Memory usage"),3);
319         // Generate graph which displays the cpu load over time
320         $series = array(
321             'max_load' => _('Max'),
322             'avg_load' => _('Avergae'),
323             'min_load' => _('Min'));
324         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("CPU load"),4);
326         // Generate graph which displays the render time
327         $series = array(
328             'max_render' => _('Max'),
329             'avg_render' => _('Avergae'),
330             'min_render' => _('Min'));
331         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Render time"),5);
333         // Generate graph which displays the plugin duration
334         $series = array(
335             'max_dur' => _('Max'),
336             'avg_dur' => _('Avergae'),
337             'min_dur' => _('Min'));
338         $this->generateSystemStatsGraph($gData,'usagePerInterval',$series, _("Seconds per action"),6);
339     }
342     /*! \brief  Generates the line-graph which displays the plugin usage over time.
343      */ 
344     function generateActionsGraph($gData)
345     {
346         $lineMax = 100;
347         $errorMax = (max($gData['errorsPerInterval']) < 100)? 100:max($gData['errorsPerInterval']);
348         $dataSet = new pData;  
349         foreach($gData['actionsPerInterval'] as $category => $entriesPerDate){
350             if(empty($category)) continue;
352             // Add results to our data set.
353             $dataSet->AddPoint($entriesPerDate, $category);
354             $dataSet->SetSerieName($this->getCategoryTranslation($category), $category);
355             $dataSet->AddSerie($category);
357             // Detect maximum value, to adjust the Y-Axis
358             $tmpMax = max($entriesPerDate);
359             if($tmpMax > $lineMax) $lineMax = $tmpMax;
360         }
362         // Add timeline
363         $dataSet->AddPoint($gData['dates'], 'date');
364         $dataSet->SetAbsciseLabelSerie('date');  
366         $chart = new pChart(800,230);  
367         $chart->setFixedScale(0.000,$lineMax);
368         $chart->setFontProperties("./themes/default/fonts/LiberationSans-Regular.ttf",10);  
369         $chart->setGraphArea(50,30,585,200);  
370         $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);  
371         $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);  
372         $chart->drawGraphArea(255,255,255,TRUE);  
373         $chart->drawGrid(4,TRUE,200,200,200,50);  
374         $chart->drawTreshold(0,143,55,72,TRUE,TRUE);  
375         $chart->drawTitle(50,22,"Plugin usage over time",50,50,50,585);  
376         $chart->drawScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2, TRUE);     
377         $chart->drawFilledLineGraph($dataSet->GetData(),$dataSet->GetDataDescription(),50,TRUE);
378         $chart->setColorPalette(count($gData['actionsPerInterval']),255,0,0);   
380         // Draw legend
381         $dataSet->AddPoint($gData['errorsPerInterval'], 'Errors');
382         $dataSet->SetSerieName(_('Error'), 'Errors');
383         $dataSet->AddSerie('Errors');
384         $chart->drawLegend(650,30,$dataSet->GetDataDescription(),255,255,255);  
386         // Remove all graph series and add the error-series, then draw the new graph.
387         foreach($gData['actionsPerInterval'] as $category => $data){
388             $dataSet->RemoveSerie($category);
389         }
390         $chart->setFixedScale(0,$errorMax);
391         $chart->drawRightScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,120,150,150,TRUE,0,2, TRUE);
392         $chart->drawBarGraph($dataSet->GetData(),$dataSet->GetDataDescription());
394         // Generate new and unique graph id
395         $this->graphID_2 = preg_replace("/[^0-9]/","",microtime(TRUE));
396         $file = '/tmp/graph_'.$this->graphID_2;
397         $chart->Render($file);
398         session::set('statistics::graphFile'.$this->graphID_2,$file);
400         return;
401     }
402    
404     /*! \brief  Generates a graph about system informations.
405      */ 
406     function generateSystemStatsGraph($gData, $key = "", $series= array(), $title = "", $gID=0 )
407     { 
408         // Add series data to dataSet        
409         $dataSet = new pData;  
410         $max = 0;
411         foreach($series as $seriesName => $seriesDesc){
412             if(isset($gData[$key][$seriesName])){
413                 $dataSet->AddPoint($gData[$key][$seriesName],$seriesName);
414                 $dataSet->SetSerieName($seriesDesc,$seriesName);
415                 if($max < max($gData[$key][$seriesName])) $max = max($gData[$key][$seriesName]);
416             }
417         }
418         $dataSet->AddAllSeries();  
419         $dataSet->AddPoint($gData['dates'], 'date');
420         $dataSet->SetAbsciseLabelSerie('date');  
421         
422         $chart = new pChart(800,230);  
423         $chart->setFixedScale(0.0001,($max*1.1));  
424         $chart->setFontProperties("./themes/default/fonts/LiberationSans-Regular.ttf",10);  
425         $chart->setGraphArea(50,30,585,200);  
426         $chart->drawFilledRoundedRectangle(7,7,693,223,5,240,240,240);  
427         $chart->drawRoundedRectangle(5,5,695,225,5,230,230,230);  
428         $chart->drawGraphArea(255,255,255,TRUE);  
429         $chart->drawGrid(4,TRUE,200,200,200,50);  
430         $chart->drawTreshold(0,143,55,72,TRUE,TRUE);  
431         $chart->drawTitle(50,22,$title,50,50,50,585);  
432         $chart->drawLegend(650,30,$dataSet->GetDataDescription(),255,255,255);  
434         $chart->drawScale($dataSet->GetData(),$dataSet->GetDataDescription(),SCALE_NORMAL,150,150,150,TRUE,0,2, FALSE);
435         $chart->drawFilledCubicCurve($dataSet->GetData(),$dataSet->GetDataDescription(),.1,50); 
437         $gName = "graphID_".$gID;
438         $this->$gName = preg_replace("/[^0-9]/","",microtime(TRUE));
439         $file = '/tmp/graph_'.$this->$gName;
440         $chart->Render($file);
441         session::set('statistics::graphFile'.$this->$gName,$file);
442     }
445     /*! \brief  Generate the pie-chart which displays the overall-plugin-usage
446      */
447     function generateCategoryPieGraph($data)
448     {
449         // Sort data by usage count and slice array to get 
450         //  the eight most used categories
451         arsort($data);
452         $mostUsedCategories = array_slice($data,0,7);
454         // Get the rest of categories and combine them 'others'
455         $theRest = array_slice($data,7);
456         $mostUsedCategories['remaining'] = array_sum($theRest);
458         // Try to find a translation for the given category names
459         $values = array_values($mostUsedCategories);
460         $keys = array_keys($mostUsedCategories);
461         foreach($keys as $id => $cat){
462             $keys[$id] = $this->getCategoryTranslation($cat);
463         }
465         // Dataset definition   
466         $dataSet = new pData;  
467         $dataSet->AddPoint($values,"Serie1");  
468         $dataSet->AddAllSeries();  
469         $dataSet->AddPoint($keys,"Serie2");  
470         $dataSet->SetAbsciseLabelSerie("Serie2");  
472         // Set graph area
473         $x = 400;
474         $y = 200;
476         // Initialise the graph  
477         $chart = new pChart($x,$y);  
478         $chart->setFontProperties($this->font,10);  
479         $chart->drawPieGraph($dataSet->GetData(),$dataSet->GetDataDescription(),($x/3),($y/2)-10,($y/2),PIE_PERCENTAGE,TRUE,50,20,3);  
480         $chart->drawPieLegend(($x/3*2),15,$dataSet->GetData(),$dataSet->GetDataDescription(),
481                 $this->legendR,$this->legendG,$this->legendB);
483         // Store graph data
484         $this->graphID_1 = preg_replace("/[^0-9]/","",microtime(TRUE));
485         $file = '/tmp/graph_'.$this->graphID_1;
486         $chart->Render($file);
487         session::set('statistics::graphFile'.$this->graphID_1,$file);
488     }
490 ?>