xref: /plugin/statistics/Query.php (revision b6632b6ef30a6f5a10a0a925445db5529b84c915)
1<?php
2
3namespace dokuwiki\plugin\statistics;
4
5use helper_plugin_statistics;
6
7class Query
8{
9    protected $hlp;
10
11    public function __construct(helper_plugin_statistics $hlp)
12    {
13        $this->hlp = $hlp;
14    }
15
16    /**
17     * Return some aggregated statistics
18     */
19    public function aggregate($tlimit)
20    {
21        $data = [];
22
23        $sql = "SELECT ref_type, COUNT(*) as cnt
24                  FROM " . $this->hlp->prefix . "access as A
25                 WHERE $tlimit
26                   AND ua_type = 'browser'
27              GROUP BY ref_type";
28        $result = $this->hlp->runSQL($sql);
29
30        if (is_array($result)) foreach ($result as $row) {
31            if ($row['ref_type'] == 'search') $data['search'] = $row['cnt'];
32            if ($row['ref_type'] == 'external') $data['external'] = $row['cnt'];
33            if ($row['ref_type'] == 'internal') $data['internal'] = $row['cnt'];
34            if ($row['ref_type'] == '') $data['direct'] = $row['cnt'];
35        }
36
37        // general user and session info
38        $sql = "SELECT COUNT(DISTINCT session) as sessions,
39                       COUNT(session) as views,
40                       COUNT(DISTINCT user) as users,
41                       COUNT(DISTINCT uid) as visitors
42                  FROM " . $this->hlp->prefix . "access as A
43                 WHERE $tlimit
44                   AND ua_type = 'browser'";
45        $result = $this->hlp->runSQL($sql);
46
47        $data['users'] = max($result[0]['users'] - 1, 0); // subtract empty user
48        $data['sessions'] = $result[0]['sessions'];
49        $data['pageviews'] = $result[0]['views'];
50        $data['visitors'] = $result[0]['visitors'];
51
52        // calculate bounce rate
53        if ($data['sessions']) {
54            $sql = "SELECT COUNT(*) as cnt
55                      FROM " . $this->hlp->prefix . "session as A
56                     WHERE $tlimit
57                       AND views = 1";
58            $result = $this->hlp->runSQL($sql);
59            $data['bouncerate'] = $result[0]['cnt'] * 100 / $data['sessions'];
60            $result = $this->hlp->runSQL($sql);
61            $data['newvisitors'] = $result[0]['cnt'] * 100 / $data['sessions'];
62        }
63
64        // calculate avg. number of views per session
65        $sql = "SELECT AVG(views) as cnt
66                  FROM " . $this->hlp->prefix . "session as A
67                     WHERE $tlimit";
68        $result = $this->hlp->runSQL($sql);
69        $data['avgpages'] = $result[0]['cnt'];
70
71        /* not used currently
72                $sql = "SELECT COUNT(id) as robots
73                          FROM ".$this->hlp->prefix."access as A
74                         WHERE $tlimit
75                           AND ua_type = 'robot'";
76                $result = $this->hlp->runSQL($sql);
77                $data['robots'] = $result[0]['robots'];
78        */
79
80        // average time spent on the site
81        $sql = "SELECT AVG(end - dt)/60 as time
82                  FROM " . $this->hlp->prefix . "session as A
83                 WHERE $tlimit
84                   AND dt != end
85                   AND DATE(dt) = DATE(end)";
86        $result = $this->hlp->runSQL($sql);
87        $data['timespent'] = $result[0]['time'];
88
89        // logins
90        $sql = "SELECT COUNT(*) as logins
91                  FROM " . $this->hlp->prefix . "logins as A
92                 WHERE $tlimit
93                   AND (type = 'l' OR type = 'p')";
94        $result = $this->hlp->runSQL($sql);
95        $data['logins'] = $result[0]['logins'];
96
97        // registrations
98        $sql = "SELECT COUNT(*) as registrations
99                  FROM " . $this->hlp->prefix . "logins as A
100                 WHERE $tlimit
101                   AND type = 'C'";
102        $result = $this->hlp->runSQL($sql);
103        $data['registrations'] = $result[0]['registrations'];
104
105        // current users
106        $sql = "SELECT COUNT(*) as current
107                  FROM " . $this->hlp->prefix . "lastseen
108                 WHERE `dt` >= NOW() - INTERVAL 10 MINUTE";
109        $result = $this->hlp->runSQL($sql);
110        $data['current'] = $result[0]['current'];
111
112        return $data;
113    }
114
115    /**
116     * standard statistics follow, only accesses made by browsers are counted
117     * for general stats like browser or OS only visitors not pageviews are counted
118     */
119
120    /**
121     * Return some trend data about visits and edits in the wiki
122     */
123    public function dashboardviews($tlimit, $hours = false)
124    {
125        if ($hours) {
126            $TIME = 'HOUR(dt)';
127        } else {
128            $TIME = 'DATE(dt)';
129        }
130
131        $data = [];
132
133        // access trends
134        $sql = "SELECT $TIME as time,
135                       COUNT(DISTINCT session) as sessions,
136                       COUNT(session) as pageviews,
137                       COUNT(DISTINCT uid) as visitors
138                  FROM " . $this->hlp->prefix . "access as A
139                 WHERE $tlimit
140                   AND ua_type = 'browser'
141              GROUP BY $TIME
142              ORDER BY time";
143        $result = $this->hlp->runSQL($sql);
144        foreach ($result as $row) {
145            $data[$row['time']]['sessions'] = $row['sessions'];
146            $data[$row['time']]['pageviews'] = $row['pageviews'];
147            $data[$row['time']]['visitors'] = $row['visitors'];
148        }
149        return $data;
150    }
151
152    public function dashboardwiki($tlimit, $hours = false)
153    {
154        if ($hours) {
155            $TIME = 'HOUR(dt)';
156        } else {
157            $TIME = 'DATE(dt)';
158        }
159
160        $data = [];
161
162        // edit trends
163        foreach (['E', 'C', 'D'] as $type) {
164            $sql = "SELECT $TIME as time,
165                           COUNT(*) as cnt
166                      FROM " . $this->hlp->prefix . "edits as A
167                     WHERE $tlimit
168                       AND type = '$type'
169                  GROUP BY $TIME
170                  ORDER BY time";
171            $result = $this->hlp->runSQL($sql);
172            foreach ($result as $row) {
173                $data[$row['time']][$type] = $row['cnt'];
174            }
175        }
176        ksort($data);
177        return $data;
178    }
179
180    public function history($tlimit, $info, $interval = false)
181    {
182        if ($interval == 'weeks') {
183            $TIME = 'EXTRACT(YEAR FROM dt), EXTRACT(WEEK FROM dt)';
184        } elseif ($interval == 'months') {
185            $TIME = 'EXTRACT(YEAR_MONTH FROM dt)';
186        } else {
187            $TIME = 'dt';
188        }
189
190        $mod = 1;
191        if ($info == 'media_size' || $info == 'page_size') {
192            $mod = 1024 * 1024;
193        }
194
195        $sql = "SELECT $TIME as time,
196                       AVG(`value`)/$mod as cnt
197                  FROM " . $this->hlp->prefix . "history as A
198                 WHERE $tlimit
199                   AND info = '$info'
200                  GROUP BY $TIME
201                  ORDER BY $TIME";
202        return $this->hlp->runSQL($sql);
203    }
204
205    public function searchengines($tlimit, $start = 0, $limit = 20)
206    {
207        $sql = "SELECT COUNT(*) as cnt, engine as eflag, engine
208                  FROM " . $this->hlp->prefix . "search as A
209                 WHERE $tlimit
210              GROUP BY engine
211              ORDER BY cnt DESC, engine" .
212            $this->mklimit($start, $limit);
213        return $this->hlp->runSQL($sql);
214    }
215
216    public function searchphrases($extern, $tlimit, $start = 0, $limit = 20)
217    {
218        if ($extern) {
219            $WHERE = "engine != 'dokuwiki'";
220            $I = '';
221        } else {
222            $WHERE = "engine = 'dokuwiki'";
223            $I = 'i';
224        }
225        $sql = "SELECT COUNT(*) as cnt, query, query as ${I}lookup
226                  FROM " . $this->hlp->prefix . "search as A
227                 WHERE $tlimit
228                   AND $WHERE
229              GROUP BY query
230              ORDER BY cnt DESC, query" .
231            $this->mklimit($start, $limit);
232        return $this->hlp->runSQL($sql);
233    }
234
235    public function searchwords($extern, $tlimit, $start = 0, $limit = 20)
236    {
237        if ($extern) {
238            $WHERE = "engine != 'dokuwiki'";
239            $I = '';
240        } else {
241            $WHERE = "engine = 'dokuwiki'";
242            $I = 'i';
243        }
244        $sql = "SELECT COUNT(*) as cnt, word, word as ${I}lookup
245                  FROM " . $this->hlp->prefix . "search as A,
246                       " . $this->hlp->prefix . "searchwords as B
247                 WHERE $tlimit
248                   AND A.id = B.sid
249                   AND $WHERE
250              GROUP BY word
251              ORDER BY cnt DESC, word" .
252            $this->mklimit($start, $limit);
253        return $this->hlp->runSQL($sql);
254    }
255
256    public function outlinks($tlimit, $start = 0, $limit = 20)
257    {
258        $sql = "SELECT COUNT(*) as cnt, link as url
259                  FROM " . $this->hlp->prefix . "outlinks as A
260                 WHERE $tlimit
261              GROUP BY link
262              ORDER BY cnt DESC, link" .
263            $this->mklimit($start, $limit);
264        return $this->hlp->runSQL($sql);
265    }
266
267    public function pages($tlimit, $start = 0, $limit = 20)
268    {
269        $sql = "SELECT COUNT(*) as cnt, page
270                  FROM " . $this->hlp->prefix . "access as A
271                 WHERE $tlimit
272                   AND ua_type = 'browser'
273              GROUP BY page
274              ORDER BY cnt DESC, page" .
275            $this->mklimit($start, $limit);
276        return $this->hlp->runSQL($sql);
277    }
278
279    public function edits($tlimit, $start = 0, $limit = 20)
280    {
281        $sql = "SELECT COUNT(*) as cnt, page
282                  FROM " . $this->hlp->prefix . "edits as A
283                 WHERE $tlimit
284              GROUP BY page
285              ORDER BY cnt DESC, page" .
286            $this->mklimit($start, $limit);
287        return $this->hlp->runSQL($sql);
288    }
289
290    public function images($tlimit, $start = 0, $limit = 20)
291    {
292        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
293                  FROM " . $this->hlp->prefix . "media as A
294                 WHERE $tlimit
295                   AND mime1 = 'image'
296              GROUP BY media
297              ORDER BY cnt DESC, media" .
298            $this->mklimit($start, $limit);
299        return $this->hlp->runSQL($sql);
300    }
301
302    public function imagessum($tlimit)
303    {
304        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
305                  FROM " . $this->hlp->prefix . "media as A
306                 WHERE $tlimit
307                   AND mime1 = 'image'";
308        return $this->hlp->runSQL($sql);
309    }
310
311    public function downloads($tlimit, $start = 0, $limit = 20)
312    {
313        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
314                  FROM " . $this->hlp->prefix . "media as A
315                 WHERE $tlimit
316                   AND mime1 != 'image'
317              GROUP BY media
318              ORDER BY cnt DESC, media" .
319            $this->mklimit($start, $limit);
320        return $this->hlp->runSQL($sql);
321    }
322
323    public function downloadssum($tlimit)
324    {
325        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
326                  FROM " . $this->hlp->prefix . "media as A
327                 WHERE $tlimit
328                   AND mime1 != 'image'";
329        return $this->hlp->runSQL($sql);
330    }
331
332    public function referer($tlimit, $start = 0, $limit = 20)
333    {
334        $sql = "SELECT COUNT(*) as cnt, ref as url
335                  FROM " . $this->hlp->prefix . "access as A
336                 WHERE $tlimit
337                   AND ua_type = 'browser'
338                   AND ref_type = 'external'
339              GROUP BY ref_md5
340              ORDER BY cnt DESC, url" .
341            $this->mklimit($start, $limit);
342        return $this->hlp->runSQL($sql);
343    }
344
345    public function newreferer($tlimit, $start = 0, $limit = 20)
346    {
347        $sql = "SELECT COUNT(*) as cnt, ref as url
348                  FROM " . $this->hlp->prefix . "access as B,
349                       " . $this->hlp->prefix . "refseen as A
350                 WHERE $tlimit
351                   AND ua_type = 'browser'
352                   AND ref_type = 'external'
353                   AND A.ref_md5 = B.ref_md5
354              GROUP BY A.ref_md5
355              ORDER BY cnt DESC, url" .
356            $this->mklimit($start, $limit);
357        return $this->hlp->runSQL($sql);
358    }
359
360    public function countries($tlimit, $start = 0, $limit = 20)
361    {
362        $sql = "SELECT COUNT(DISTINCT session) as cnt, B.code AS cflag, B.country
363                  FROM " . $this->hlp->prefix . "access as A,
364                       " . $this->hlp->prefix . "iplocation as B
365                 WHERE $tlimit
366                   AND A.ip = B.ip
367              GROUP BY B.code
368              ORDER BY cnt DESC, B.country" .
369            $this->mklimit($start, $limit);
370        return $this->hlp->runSQL($sql);
371    }
372
373    public function browsers($tlimit, $start = 0, $limit = 20, $ext = true)
374    {
375        if ($ext) {
376            $sel = 'ua_info as bflag, ua_info as browser, ua_ver';
377            $grp = 'ua_info, ua_ver';
378        } else {
379            $grp = 'ua_info';
380            $sel = 'ua_info';
381        }
382
383        $sql = "SELECT COUNT(DISTINCT session) as cnt, $sel
384                  FROM " . $this->hlp->prefix . "access as A
385                 WHERE $tlimit
386                   AND ua_type = 'browser'
387              GROUP BY $grp
388              ORDER BY cnt DESC, ua_info" .
389            $this->mklimit($start, $limit);
390        return $this->hlp->runSQL($sql);
391    }
392
393    public function os($tlimit, $start = 0, $limit = 20)
394    {
395        $sql = "SELECT COUNT(DISTINCT session) as cnt, os as osflag, os
396                  FROM " . $this->hlp->prefix . "access as A
397                 WHERE $tlimit
398                   AND ua_type = 'browser'
399              GROUP BY os
400              ORDER BY cnt DESC, os" .
401            $this->mklimit($start, $limit);
402        return $this->hlp->runSQL($sql);
403    }
404
405    public function topuser($tlimit, $start = 0, $limit = 20)
406    {
407        $sql = "SELECT COUNT(*) as cnt, user
408                  FROM " . $this->hlp->prefix . "access as A
409                 WHERE $tlimit
410                   AND ua_type = 'browser'
411                   AND user != ''
412              GROUP BY user
413              ORDER BY cnt DESC, user" .
414            $this->mklimit($start, $limit);
415        return $this->hlp->runSQL($sql);
416    }
417
418    public function topeditor($tlimit, $start = 0, $limit = 20)
419    {
420        $sql = "SELECT COUNT(*) as cnt, user
421                  FROM " . $this->hlp->prefix . "edits as A
422                 WHERE $tlimit
423                   AND user != ''
424              GROUP BY user
425              ORDER BY cnt DESC, user" .
426            $this->mklimit($start, $limit);
427        return $this->hlp->runSQL($sql);
428    }
429
430    public function topgroup($tlimit, $start = 0, $limit = 20)
431    {
432        $sql = "SELECT COUNT(*) as cnt, `group`
433                  FROM " . $this->hlp->prefix . "groups as A
434                 WHERE $tlimit
435                   AND `type` = 'view'
436              GROUP BY `group`
437              ORDER BY cnt DESC, `group`" .
438            $this->mklimit($start, $limit);
439        return $this->hlp->runSQL($sql);
440    }
441
442    public function topgroupedit($tlimit, $start = 0, $limit = 20)
443    {
444        $sql = "SELECT COUNT(*) as cnt, `group`
445                  FROM " . $this->hlp->prefix . "groups as A
446                 WHERE $tlimit
447                   AND `type` = 'edit'
448              GROUP BY `group`
449              ORDER BY cnt DESC, `group`" .
450            $this->mklimit($start, $limit);
451        return $this->hlp->runSQL($sql);
452    }
453
454
455    public function resolution($tlimit, $start = 0, $limit = 20)
456    {
457        $sql = "SELECT COUNT(DISTINCT uid) as cnt,
458                       ROUND(screen_x/100)*100 as res_x,
459                       ROUND(screen_y/100)*100 as res_y,
460                       CONCAT(ROUND(screen_x/100)*100,'x',ROUND(screen_y/100)*100) as resolution
461                  FROM " . $this->hlp->prefix . "access as A
462                 WHERE $tlimit
463                   AND ua_type  = 'browser'
464                   AND screen_x != 0
465                   AND screen_y != 0
466              GROUP BY resolution
467              ORDER BY cnt DESC" .
468            $this->mklimit($start, $limit);
469        return $this->hlp->runSQL($sql);
470    }
471
472    public function viewport($tlimit, $start = 0, $limit = 20)
473    {
474        $sql = "SELECT COUNT(DISTINCT uid) as cnt,
475                       ROUND(view_x/100)*100 as res_x,
476                       ROUND(view_y/100)*100 as res_y,
477                       CONCAT(ROUND(view_x/100)*100,'x',ROUND(view_y/100)*100) as resolution
478                  FROM " . $this->hlp->prefix . "access as A
479                 WHERE $tlimit
480                   AND ua_type  = 'browser'
481                   AND view_x != 0
482                   AND view_y != 0
483              GROUP BY resolution
484              ORDER BY cnt DESC" .
485            $this->mklimit($start, $limit);
486
487        return $this->hlp->runSQL($sql);
488    }
489
490    public function seenusers($tlimit, $start = 0, $limit = 20)
491    {
492        $sql = "SELECT `user`, `dt`
493                  FROM " . $this->hlp->prefix . "lastseen as A
494              ORDER BY `dt` DESC" .
495            $this->mklimit($start, $limit);
496
497        return $this->hlp->runSQL($sql);
498    }
499
500
501    /**
502     * Builds a limit clause
503     */
504    public function mklimit($start, $limit)
505    {
506        $start = (int)$start;
507        $limit = (int)$limit;
508        if ($limit) {
509            $limit += 1;
510            return " LIMIT $start,$limit";
511        } elseif ($start) {
512            return " OFFSET $start";
513        }
514        return '';
515    }
516
517    /**
518     * Create a time limit for use in SQL
519     */
520    public function mktlimit(&$from, &$to)
521    {
522        // fixme add better sanity checking here:
523        $from = preg_replace('/[^\d\-]+/', '', $from);
524        $to = preg_replace('/[^\d\-]+/', '', $to);
525        if (!$from) $from = date('Y-m-d');
526        if (!$to) $to = date('Y-m-d');
527
528        return "A.dt >= '$from 00:00:00' AND A.dt <= '$to 23:59:59'";
529    }
530}
531