1<?php
2
3/**
4 * statdisplay plugin graph helper component
5 *
6 * @author Andreas Gohr <gohr@cosmocode.de>
7 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 */
9class helper_plugin_statdisplay_graph extends DokuWiki_Plugin
10{
11    /** @var helper_plugin_statdisplay_log */
12    private $log = null;
13
14    /**
15     * Outputs a Graph image
16     *
17     * @param string $command
18     * @param string $from
19     * @param string $to
20     * @return void
21     */
22    public function sendgraph($command, $from = '', $to = '')
23    {
24        require dirname(__FILE__) . '/../pchart/pData.php';
25        require dirname(__FILE__) . '/../pchart/pChart.php';
26        require dirname(__FILE__) . '/../pchart/GDCanvas.php';
27        require dirname(__FILE__) . '/../pchart/PieChart.php';
28
29        $this->log = plugin_load('helper', 'statdisplay_log');
30
31        header('Content-Type: image/png');
32        switch ($command) {
33            case 'all':
34                $this->summary();
35                break;
36            case 'month by day':
37                $this->monthby('day', $from);
38                break;
39            case 'month by hour':
40                $this->monthby('hour', $from);
41                break;
42            case 'traffic by day':
43                $this->trafficby('day', $from);
44                break;
45            case 'traffic by hour':
46                $this->trafficby('hour', $from);
47                break;
48            case 'traffic by user':
49                $this->userdownloads($from);
50                break;
51            default:
52                $this->nograph('No such graph: ' . $command);
53                break;
54        }
55    }
56
57    /**
58     * Show all the access data
59     *
60     * @param string $from
61     * @param string $to
62     * @return void
63     */
64    private function summary($from = '', $to = '')
65    {
66        $times = array();
67        $hits = array();
68        $pages = array();
69        $media = array();
70        $visitors = array();
71
72        foreach ($this->log->logdata as $month => $data) {
73            if ($month[0] == '_') continue;
74            if ($from && $month < $from) continue;
75            if ($to && $month > $to) break;
76
77            $times[] = $month;
78            $pages[] = $data['page']['all']['count'] ?? 0;
79            $media[] = $data['media']['all']['count'] ?? 0;
80            $hits[] = $data['hits']['all']['count'] ?? 0;
81            $visitors[] = $data['hits']['all']['visitor'] ?? 0;
82        }
83
84        $title = $this->getLang('t_summary');
85
86        $this->accessgraph(
87            $title,
88            $times,
89            array(
90                $this->getLang('hits'),
91                $this->getLang('pages'),
92                $this->getLang('media'),
93                $this->getLang('visitors'),
94            ),
95            array($hits, $pages, $media, $visitors)
96        );
97    }
98
99    /**
100     * Show month access by day or hour
101     *
102     * @param string $by either day or hour
103     * @param string $date
104     */
105    private function monthby($by, $date = '')
106    {
107        if (!$date) $date = date('Y-m');
108        $data = $this->log->logdata[$date];
109
110        $times = array();
111        $hits = array();
112        $pages = array();
113        $media = array();
114        $visitors = array();
115
116        $keys = array_keys((array)$data['hits'][$by]);
117        sort($keys);
118        foreach ($keys as $idx) {
119            $times[] = $idx;
120            $pages[] = $data['page'][$by][$idx]['count'];
121            $media[] = $data['media'][$by][$idx]['count'];
122            $hits[] = $data['hits'][$by][$idx]['count'];
123            $visitors[] = $data['hits'][$by][$idx]['visitor'];
124        }
125
126        $title = sprintf($this->getLang('t_' . $by), $date);
127
128        $this->accessgraph(
129            $title,
130            $times,
131            array(
132                $this->getLang('hits'),
133                $this->getLang('pages'),
134                $this->getLang('media'),
135                $this->getLang('visitors'),
136            ),
137            array($hits, $pages, $media, $visitors)
138        );
139    }
140
141    /**
142     * Show month traffic by day or hour
143     *
144     * @param string $by either day or hour
145     * @param string $date
146     */
147    private function trafficby($by, $date = '')
148    {
149        if (!$date) $date = date('Y-m');
150        $data = $this->log->logdata[$date];
151
152        $times = array();
153        $hits = array();
154        $pages = array();
155        $media = array();
156
157        foreach (array_keys((array)$data['hits'][$by]) as $idx) {
158            $times[] = $idx;
159            $pages[] = $data['page'][$by][$idx]['bytes'] / 1024;
160            $media[] = $data['media'][$by][$idx]['bytes'] / 1024;
161            $hits[] = $data['hits'][$by][$idx]['bytes'] / 1024;
162        }
163
164        $title = 'Traffic';
165
166        $this->accessgraph(
167            $title,
168            $times,
169            array(
170                $this->getLang('all'),
171                $this->getLang('pages'),
172                $this->getLang('media'),
173            ),
174            array($hits, $pages, $media)
175        );
176    }
177
178    /**
179     * @param string $date month to display
180     */
181    private function userdownloads($date)
182    {
183        $usertraffic = $this->log->usertraffic($date);
184        if (!$usertraffic) $this->nograph($this->getLang('t_usertraffic') . ': no data');
185
186        $usertraffic = array_map(function ($in) {
187            return $in / 1024 / 1024;
188        }, $usertraffic);
189
190        // get work day average
191        if (count($usertraffic)) {
192            $avg = $this->log->avg($usertraffic);
193            // $avg = $avg / 7 *5; //work day average
194        } else {
195            $avg = 0;
196        }
197        arsort($usertraffic); // highest first
198
199        // limit number of users shown
200        $maxusers = 10;
201        if (count($usertraffic) > $maxusers + 1) {
202            $others = array_slice($usertraffic, $maxusers);
203            $usertraffic = array_slice($usertraffic, 0, $maxusers);
204
205            $other = 0;
206            foreach ($others as $user => $traffic) {
207                $other += $traffic;
208            }
209
210            $usertraffic[sprintf($this->getLang('others'), count($others))] = $other;
211        }
212
213        // prepare the graph datasets
214        $DataSet = new pData();
215        $DataSet->addPoints(array_values($usertraffic), "traffic");
216
217        // setup axis
218        $DataSet->AddPoints(array_keys($usertraffic), 'names');
219        $DataSet->AddAllSeries();
220        $DataSet->SetAbscissaLabelSeries('names');
221        $DataSet->removeSeries('names');
222        $DataSet->removeSeriesName('names');
223
224        // create the bar graph
225        $Canvas = new GDCanvas(600, 300, false);
226        $Chart = new pChart(600, 300, $Canvas);
227
228        $Chart->setFontProperties(dirname(__FILE__) . '/../pchart/Fonts/DroidSans.ttf', 8);
229        $Chart->setGraphArea(50, 40, 600, 200);
230        $Chart->drawScale(
231            $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)),
232            45, 1, true
233        );
234
235        $Chart->drawBarGraph($DataSet->GetData(), $DataSet->GetDataDescription());
236        //$Chart->drawLegend(500, 40, $DataSet->GetDataDescription(), new Color(250));
237
238        $Chart->drawTreshold($avg, new Color(128, 0, 0));
239
240        $Chart->setFontProperties(dirname(__FILE__) . '/../pchart/Fonts/DroidSans.ttf', 12);
241        $Chart->drawTitle(10, 10, $this->getLang('t_usertraffic') . ' (MB)', new Color(0), 590, 30);
242
243        $Chart->Render(null);
244
245    }
246
247    /**
248     * Draws a line or bargraph depending on the number of data points
249     *
250     * @param string $title the graph's title
251     * @param array $axis the axis points
252     * @param array $labels the labels for the datasets
253     * @param array $datasets any number of data arrays
254     */
255    private function accessgraph($title, $axis, $labels, $datasets)
256    {
257        if (!count($axis)) {
258            $this->nograph($title . ': no data');
259            return;
260        }
261
262        // add the data and labels
263        $DataSet = new pData();
264        foreach ($datasets as $num => $set) {
265            $DataSet->AddPoints($set, "series$num");
266            $DataSet->SetSeriesName($labels[$num], "series$num");
267        }
268
269        // setup axis
270        $DataSet->AddPoints($axis, 'times');
271        $DataSet->AddAllSeries();
272        $DataSet->SetAbscissaLabelSeries('times');
273        $DataSet->removeSeries('times');
274        $DataSet->removeSeriesName('times');
275
276        $Canvas = new GDCanvas(600, 300, false);
277        $Chart = new pChart(600, 300, $Canvas);
278        $usebargraph = (count($axis) < 10);
279
280        $Chart->setFontProperties(dirname(__FILE__) . '/../pchart/Fonts/DroidSans.ttf', 8);
281        $Chart->setGraphArea(50, 40, 500, 200);
282        $Chart->drawScale(
283            $DataSet, new ScaleStyle(SCALE_NORMAL, new Color(127)),
284            45, 1, $usebargraph
285        );
286
287        if ($usebargraph) {
288            $Chart->drawBarGraph($DataSet->GetData(), $DataSet->GetDataDescription());
289        } else {
290            $Chart->drawLineGraph($DataSet->GetData(), $DataSet->GetDataDescription());
291        }
292        $Chart->drawLegend(500, 40, $DataSet->GetDataDescription(), new Color(250));
293
294        $Chart->setFontProperties(dirname(__FILE__) . '/../pchart/Fonts/DroidSans.ttf', 12);
295        $Chart->drawTitle(10, 10, $title, new Color(0), 590, 30);
296
297        $Chart->Render(null);
298    }
299
300    /**
301     * Just creates a message
302     *
303     * @param $title
304     */
305    private function nograph($title)
306    {
307        $Canvas = new GDCanvas(300, 40, false);
308        $Chart = new pChart(300, 40, $Canvas);
309        $Chart->setFontProperties(dirname(__FILE__) . '/../pchart/Fonts/DroidSans.ttf', 10);
310        $Chart->drawTitle(0, 0, $title, new Color(128, 0, 0), 300, 40);
311        $Chart->Render(null);
312        exit;
313    }
314}
315