xref: /plugin/statistics/Query.php (revision 2a30f557f7f24294810e1d10019698ec841fa49b)
1b6632b6eSAndreas Gohr<?php
2b6632b6eSAndreas Gohr
3b6632b6eSAndreas Gohrnamespace dokuwiki\plugin\statistics;
4b6632b6eSAndreas Gohr
5f9e60319SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
6b6632b6eSAndreas Gohruse helper_plugin_statistics;
7b6632b6eSAndreas Gohr
8f9e60319SAndreas Gohr/**
9f9e60319SAndreas Gohr * This class defines a bunch of SQL queries to fetch various statistics from the database
10f9e60319SAndreas Gohr */
11b6632b6eSAndreas Gohrclass Query
12b6632b6eSAndreas Gohr{
13f9e60319SAndreas Gohr    protected helper_plugin_statistics $hlp;
14f9e60319SAndreas Gohr    protected SQLiteDB $db;
15f9e60319SAndreas Gohr    protected string $from;
16f9e60319SAndreas Gohr    protected string $to;
17f9e60319SAndreas Gohr    protected string $limit = '';
18b6632b6eSAndreas Gohr
19f9e60319SAndreas Gohr    /**
20f9e60319SAndreas Gohr     * @param helper_plugin_statistics $hlp
21f9e60319SAndreas Gohr     */
22b6632b6eSAndreas Gohr    public function __construct(helper_plugin_statistics $hlp)
23b6632b6eSAndreas Gohr    {
24b6632b6eSAndreas Gohr        $this->hlp = $hlp;
25f9e60319SAndreas Gohr        $this->db = $hlp->getDB();
267428e816SAndreas Gohr        $today = date('Y-m-d');
277428e816SAndreas Gohr        $this->setTimeFrame($today, $today);
28f9e60319SAndreas Gohr        $this->setPagination(0, 20);
297428e816SAndreas Gohr    }
307428e816SAndreas Gohr
317428e816SAndreas Gohr    /**
327428e816SAndreas Gohr     * Set the time frame for all queries
33f9e60319SAndreas Gohr     *
34f9e60319SAndreas Gohr     * @param string $from The start date as YYYY-MM-DD
35f9e60319SAndreas Gohr     * @param string $to The end date as YYYY-MM-DD
367428e816SAndreas Gohr     */
37f9e60319SAndreas Gohr    public function setTimeFrame(string $from, string $to): void
387428e816SAndreas Gohr    {
39f9e60319SAndreas Gohr        try {
40f9e60319SAndreas Gohr            $from = new \DateTime($from);
41f9e60319SAndreas Gohr            $to = new \DateTime($to);
42f9e60319SAndreas Gohr        } catch (\Exception $e) {
43f9e60319SAndreas Gohr            $from = new \DateTime();
44f9e60319SAndreas Gohr            $to = new \DateTime();
45f9e60319SAndreas Gohr        }
46f9e60319SAndreas Gohr        $from->setTime(0, 0);
47f9e60319SAndreas Gohr        $to->setTime(23, 59, 59);
487428e816SAndreas Gohr
49f9e60319SAndreas Gohr        $this->from = $from->format('Y-m-d H:i:s');
50f9e60319SAndreas Gohr        $this->to = $to->format('Y-m-d H:i:s');
51f9e60319SAndreas Gohr    }
52f9e60319SAndreas Gohr
53f9e60319SAndreas Gohr    /**
54f9e60319SAndreas Gohr     * Set the pagination settings for some queries
55f9e60319SAndreas Gohr     *
56f9e60319SAndreas Gohr     * @param int $start The start offset
57f9e60319SAndreas Gohr     * @param int $limit The number of results. If one more is returned, there is another page
58f9e60319SAndreas Gohr     * @return void
59f9e60319SAndreas Gohr     */
60f9e60319SAndreas Gohr    public function setPagination(int $start, int $limit)
61f9e60319SAndreas Gohr    {
62f9e60319SAndreas Gohr        // when a limit is set, one more is fetched to indicate when a next page exists
63f9e60319SAndreas Gohr        if ($limit) $limit += 1;
64f9e60319SAndreas Gohr
65f9e60319SAndreas Gohr        if ($limit) {
66f9e60319SAndreas Gohr            $this->limit = " LIMIT $start,$limit";
67f9e60319SAndreas Gohr        } elseif ($start) {
68f9e60319SAndreas Gohr            $this->limit = " OFFSET $start";
69f9e60319SAndreas Gohr        }
70b6632b6eSAndreas Gohr    }
71b6632b6eSAndreas Gohr
72b6632b6eSAndreas Gohr    /**
73b6632b6eSAndreas Gohr     * Return some aggregated statistics
74b6632b6eSAndreas Gohr     */
75f9e60319SAndreas Gohr    public function aggregate(): array
76b6632b6eSAndreas Gohr    {
77*2a30f557SAndreas Gohr        // init some values that might not be set
78*2a30f557SAndreas Gohr        $data = [
79*2a30f557SAndreas Gohr            'referers' => 0, // total number of (external) referrers
80*2a30f557SAndreas Gohr            'external' => 0, // external referrers
81*2a30f557SAndreas Gohr            'search' => 0, // search engine referrers
82*2a30f557SAndreas Gohr            'direct' => 0, // direct referrers
83*2a30f557SAndreas Gohr            'internal' => 0, // internal referrers
84*2a30f557SAndreas Gohr            'bouncerate' => 0,
85*2a30f557SAndreas Gohr            'newvisitors' => 0,
86*2a30f557SAndreas Gohr        ];
87b6632b6eSAndreas Gohr
8810dcb86fSAndreas Gohr        // Count referrer types by joining with referers table
8910dcb86fSAndreas Gohr        $sql = "SELECT
9010dcb86fSAndreas Gohr                    CASE
9110dcb86fSAndreas Gohr                        WHEN R.engine IS NOT NULL THEN 'search'
92*2a30f557SAndreas Gohr                        WHEN R.url = '' THEN 'direct'
93*2a30f557SAndreas Gohr                        WHEN R.url IS NOT NULL THEN 'external'
94*2a30f557SAndreas Gohr                        ELSE 'internal'
9510dcb86fSAndreas Gohr                    END as ref_type,
9610dcb86fSAndreas Gohr                    COUNT(*) as cnt
9710dcb86fSAndreas Gohr                  FROM pageviews as P
9810dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
9910dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
10010dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
10110dcb86fSAndreas Gohr                   AND S.ua_type = ?
102b6632b6eSAndreas Gohr              GROUP BY ref_type";
103f9e60319SAndreas Gohr        $result = $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
104b6632b6eSAndreas Gohr
105f9e60319SAndreas Gohr        foreach ($result as $row) {
106*2a30f557SAndreas Gohr            if ($row['ref_type'] == 'search') {
107*2a30f557SAndreas Gohr                $data['search'] = $row['cnt'];
108*2a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
109*2a30f557SAndreas Gohr            }
110*2a30f557SAndreas Gohr            if ($row['ref_type'] == 'direct') {
111*2a30f557SAndreas Gohr                $data['direct'] = $row['cnt'];
112*2a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
113*2a30f557SAndreas Gohr            }
114*2a30f557SAndreas Gohr            if ($row['ref_type'] == 'external') {
115*2a30f557SAndreas Gohr                $data['external'] = $row['cnt'];
116*2a30f557SAndreas Gohr                $data['referers'] += $row['cnt'];
117*2a30f557SAndreas Gohr            }
118*2a30f557SAndreas Gohr            if ($row['ref_type'] == 'internal') {
119*2a30f557SAndreas Gohr                $data['internal'] = $row['cnt'];
120*2a30f557SAndreas Gohr            }
121b6632b6eSAndreas Gohr        }
122b6632b6eSAndreas Gohr
123b6632b6eSAndreas Gohr        // general user and session info
12410dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT P.session) as sessions,
12510dcb86fSAndreas Gohr                       COUNT(P.session) as views,
12610dcb86fSAndreas Gohr                       COUNT(DISTINCT S.user) as users,
12710dcb86fSAndreas Gohr                       COUNT(DISTINCT S.uid) as visitors
12810dcb86fSAndreas Gohr                  FROM pageviews as P
12910dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
13010dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
13110dcb86fSAndreas Gohr                   AND S.ua_type = ?";
132f9e60319SAndreas Gohr        $result = $this->db->queryRecord($sql, [$this->from, $this->to, 'browser']);
133b6632b6eSAndreas Gohr
13410dcb86fSAndreas Gohr        $data['users'] = $result['users'];
135f9e60319SAndreas Gohr        $data['sessions'] = $result['sessions'];
136f9e60319SAndreas Gohr        $data['pageviews'] = $result['views'];
137f9e60319SAndreas Gohr        $data['visitors'] = $result['visitors'];
138b6632b6eSAndreas Gohr
13910dcb86fSAndreas Gohr        // calculate bounce rate (sessions with only 1 page view)
140b6632b6eSAndreas Gohr        if ($data['sessions']) {
141b6632b6eSAndreas Gohr            $sql = "SELECT COUNT(*) as cnt
14210dcb86fSAndreas Gohr                      FROM (
14310dcb86fSAndreas Gohr                          SELECT P.session, COUNT(*) as views
14410dcb86fSAndreas Gohr                            FROM pageviews as P
14510dcb86fSAndreas Gohr                            LEFT JOIN sessions as S ON P.session = S.session
14610dcb86fSAndreas Gohr                           WHERE P.dt >= ? AND P.dt <= ?
14710dcb86fSAndreas Gohr                             AND S.ua_type = ?
14810dcb86fSAndreas Gohr                        GROUP BY P.session
14910dcb86fSAndreas Gohr                          HAVING views = 1
15010dcb86fSAndreas Gohr                      )";
15110dcb86fSAndreas Gohr            $count = $this->db->queryValue($sql, [$this->from, $this->to, 'browser']);
152f9e60319SAndreas Gohr            $data['bouncerate'] = $count * 100 / $data['sessions'];
153f9e60319SAndreas Gohr            $data['newvisitors'] = $count * 100 / $data['sessions'];
154b6632b6eSAndreas Gohr        }
155b6632b6eSAndreas Gohr
156b6632b6eSAndreas Gohr        // calculate avg. number of views per session
157b6632b6eSAndreas Gohr        $sql = "SELECT AVG(views) as cnt
15810dcb86fSAndreas Gohr                  FROM (
15910dcb86fSAndreas Gohr                      SELECT P.session, COUNT(*) as views
16010dcb86fSAndreas Gohr                        FROM pageviews as P
16110dcb86fSAndreas Gohr                        LEFT JOIN sessions as S ON P.session = S.session
16210dcb86fSAndreas Gohr                       WHERE P.dt >= ? AND P.dt <= ?
16310dcb86fSAndreas Gohr                         AND S.ua_type = ?
16410dcb86fSAndreas Gohr                    GROUP BY P.session
16510dcb86fSAndreas Gohr                  )";
16610dcb86fSAndreas Gohr        $data['avgpages'] = $this->db->queryValue($sql, [$this->from, $this->to, 'browser']);
167b6632b6eSAndreas Gohr
168b6632b6eSAndreas Gohr        // average time spent on the site
16910dcb86fSAndreas Gohr        $sql = "SELECT AVG((julianday(end) - julianday(dt)) * 24 * 60) as time
17010dcb86fSAndreas Gohr                  FROM sessions as S
17110dcb86fSAndreas Gohr                 WHERE S.dt >= ? AND S.dt <= ?
17210dcb86fSAndreas Gohr                   AND S.dt != S.end
17310dcb86fSAndreas Gohr                   AND DATE(S.dt) = DATE(S.end)
17410dcb86fSAndreas Gohr                   AND S.ua_type = ?";
17510dcb86fSAndreas Gohr        $data['timespent'] = $this->db->queryValue($sql, [$this->from, $this->to, 'browser']);
176b6632b6eSAndreas Gohr
177b6632b6eSAndreas Gohr        // logins
178b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as logins
1797428e816SAndreas Gohr                  FROM logins as A
1807428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
1817428e816SAndreas Gohr                   AND (type = ? OR type = ?)";
182f9e60319SAndreas Gohr        $data['logins'] = $this->db->queryValue($sql, [$this->from, $this->to, 'l', 'p']);
183b6632b6eSAndreas Gohr
184b6632b6eSAndreas Gohr        // registrations
185b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as registrations
1867428e816SAndreas Gohr                  FROM logins as A
1877428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
1887428e816SAndreas Gohr                   AND type = ?";
189f9e60319SAndreas Gohr        $data['registrations'] = $this->db->queryValue($sql, [$this->from, $this->to, 'C']);
190b6632b6eSAndreas Gohr
19110dcb86fSAndreas Gohr        // current users (based on recent sessions)
19210dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT uid) as current
19310dcb86fSAndreas Gohr                  FROM sessions
19410dcb86fSAndreas Gohr                 WHERE end >= datetime('now', '-10 minutes')";
195f9e60319SAndreas Gohr        $data['current'] = $this->db->queryValue($sql);
196b6632b6eSAndreas Gohr
197b6632b6eSAndreas Gohr        return $data;
198b6632b6eSAndreas Gohr    }
199b6632b6eSAndreas Gohr
200b6632b6eSAndreas Gohr
201b6632b6eSAndreas Gohr    /**
202b6632b6eSAndreas Gohr     * Return some trend data about visits and edits in the wiki
203f9e60319SAndreas Gohr     *
204f9e60319SAndreas Gohr     * @param bool $hours Use hour resolution rather than days
205f9e60319SAndreas Gohr     * @return array
206b6632b6eSAndreas Gohr     */
207f9e60319SAndreas Gohr    public function dashboardviews(bool $hours = false): array
208b6632b6eSAndreas Gohr    {
209b6632b6eSAndreas Gohr        if ($hours) {
21010dcb86fSAndreas Gohr            $TIME = 'strftime(\'%H\', P.dt)';
211b6632b6eSAndreas Gohr        } else {
21210dcb86fSAndreas Gohr            $TIME = 'DATE(P.dt)';
213b6632b6eSAndreas Gohr        }
214b6632b6eSAndreas Gohr
215b6632b6eSAndreas Gohr        $data = [];
216b6632b6eSAndreas Gohr
217b6632b6eSAndreas Gohr        // access trends
218b6632b6eSAndreas Gohr        $sql = "SELECT $TIME as time,
21910dcb86fSAndreas Gohr                       COUNT(DISTINCT P.session) as sessions,
22010dcb86fSAndreas Gohr                       COUNT(P.session) as pageviews,
22110dcb86fSAndreas Gohr                       COUNT(DISTINCT S.uid) as visitors
22210dcb86fSAndreas Gohr                  FROM pageviews as P
22310dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
22410dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
22510dcb86fSAndreas Gohr                   AND S.ua_type = ?
226b6632b6eSAndreas Gohr              GROUP BY $TIME
227b6632b6eSAndreas Gohr              ORDER BY time";
228f9e60319SAndreas Gohr        $result = $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
229b6632b6eSAndreas Gohr        foreach ($result as $row) {
230b6632b6eSAndreas Gohr            $data[$row['time']]['sessions'] = $row['sessions'];
231b6632b6eSAndreas Gohr            $data[$row['time']]['pageviews'] = $row['pageviews'];
232b6632b6eSAndreas Gohr            $data[$row['time']]['visitors'] = $row['visitors'];
233b6632b6eSAndreas Gohr        }
234b6632b6eSAndreas Gohr        return $data;
235b6632b6eSAndreas Gohr    }
236b6632b6eSAndreas Gohr
237f9e60319SAndreas Gohr    /**
238f9e60319SAndreas Gohr     * @param bool $hours Use hour resolution rather than days
239f9e60319SAndreas Gohr     * @return array
240f9e60319SAndreas Gohr     */
241f9e60319SAndreas Gohr    public function dashboardwiki(bool $hours = false): array
242b6632b6eSAndreas Gohr    {
243b6632b6eSAndreas Gohr        if ($hours) {
2447428e816SAndreas Gohr            $TIME = 'strftime(\'%H\', dt)';
245b6632b6eSAndreas Gohr        } else {
246b6632b6eSAndreas Gohr            $TIME = 'DATE(dt)';
247b6632b6eSAndreas Gohr        }
248b6632b6eSAndreas Gohr
249b6632b6eSAndreas Gohr        $data = [];
250b6632b6eSAndreas Gohr
251b6632b6eSAndreas Gohr        // edit trends
252b6632b6eSAndreas Gohr        foreach (['E', 'C', 'D'] as $type) {
253b6632b6eSAndreas Gohr            $sql = "SELECT $TIME as time,
254b6632b6eSAndreas Gohr                           COUNT(*) as cnt
2557428e816SAndreas Gohr                      FROM edits as A
2567428e816SAndreas Gohr                     WHERE A.dt >= ? AND A.dt <= ?
2577428e816SAndreas Gohr                       AND type = ?
258b6632b6eSAndreas Gohr                  GROUP BY $TIME
259b6632b6eSAndreas Gohr                  ORDER BY time";
260f9e60319SAndreas Gohr            $result = $this->db->queryAll($sql, [$this->from, $this->to, $type]);
261b6632b6eSAndreas Gohr            foreach ($result as $row) {
262b6632b6eSAndreas Gohr                $data[$row['time']][$type] = $row['cnt'];
263b6632b6eSAndreas Gohr            }
264b6632b6eSAndreas Gohr        }
265b6632b6eSAndreas Gohr        ksort($data);
266b6632b6eSAndreas Gohr        return $data;
267b6632b6eSAndreas Gohr    }
268b6632b6eSAndreas Gohr
269f9e60319SAndreas Gohr    /**
270f9e60319SAndreas Gohr     * @param string $info Which type of history to select (FIXME which ones are there?)
271f9e60319SAndreas Gohr     * @param string $interval Group data by this interval (days, weeks, months)
272f9e60319SAndreas Gohr     * @return array
273f9e60319SAndreas Gohr     */
274f9e60319SAndreas Gohr    public function history(string $info, string $interval = 'day'): array
275b6632b6eSAndreas Gohr    {
276b6632b6eSAndreas Gohr        if ($interval == 'weeks') {
2777428e816SAndreas Gohr            $TIME = 'strftime(\'%Y\', dt), strftime(\'%W\', dt)';
278b6632b6eSAndreas Gohr        } elseif ($interval == 'months') {
2797428e816SAndreas Gohr            $TIME = 'strftime(\'%Y-%m\', dt)';
280b6632b6eSAndreas Gohr        } else {
281a087824eSAnna Dabrowska            $TIME = 'strftime(\'%d-%m\', dt)';
282b6632b6eSAndreas Gohr        }
283b6632b6eSAndreas Gohr
284b6632b6eSAndreas Gohr        $mod = 1;
285b6632b6eSAndreas Gohr        if ($info == 'media_size' || $info == 'page_size') {
286b6632b6eSAndreas Gohr            $mod = 1024 * 1024;
287b6632b6eSAndreas Gohr        }
288b6632b6eSAndreas Gohr
289b6632b6eSAndreas Gohr        $sql = "SELECT $TIME as time,
2907428e816SAndreas Gohr                       AVG(value)/$mod as cnt
2917428e816SAndreas Gohr                  FROM history as A
2927428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
2937428e816SAndreas Gohr                   AND info = ?
294b6632b6eSAndreas Gohr                  GROUP BY $TIME
295b6632b6eSAndreas Gohr                  ORDER BY $TIME";
296f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, $info]);
297b6632b6eSAndreas Gohr    }
298b6632b6eSAndreas Gohr
299f9e60319SAndreas Gohr    /**
300f9e60319SAndreas Gohr     * @return array
301f9e60319SAndreas Gohr     */
302f9e60319SAndreas Gohr    public function searchengines(): array
303b6632b6eSAndreas Gohr    {
30410dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.engine
30510dcb86fSAndreas Gohr                  FROM pageviews as P
30610dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
30710dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
30810dcb86fSAndreas Gohr                   AND R.engine IS NOT NULL
30910dcb86fSAndreas Gohr                   AND R.engine != ''
31010dcb86fSAndreas Gohr              GROUP BY R.engine
31110dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.engine" .
312f9e60319SAndreas Gohr            $this->limit;
313f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
314b6632b6eSAndreas Gohr    }
315b6632b6eSAndreas Gohr
316f9e60319SAndreas Gohr    /**
317f9e60319SAndreas Gohr     * @return array
318f9e60319SAndreas Gohr     */
319*2a30f557SAndreas Gohr    public function searchphrases(): array
320b6632b6eSAndreas Gohr    {
321*2a30f557SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, query, query as ilookup
322*2a30f557SAndreas Gohr                  FROM search
323*2a30f557SAndreas Gohr                 WHERE dt >= ? AND dt <= ?
324*2a30f557SAndreas Gohr              GROUP BY query
325*2a30f557SAndreas Gohr              ORDER BY cnt DESC, query" .
326f9e60319SAndreas Gohr            $this->limit;
327*2a30f557SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
328b6632b6eSAndreas Gohr    }
329b6632b6eSAndreas Gohr
330f9e60319SAndreas Gohr    /**
331f9e60319SAndreas Gohr     * @return array
332f9e60319SAndreas Gohr     */
333*2a30f557SAndreas Gohr    public function searchwords(): array
334b6632b6eSAndreas Gohr    {
335*2a30f557SAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SW.word, SW.word as ilookup
336*2a30f557SAndreas Gohr                  FROM search as S,
337*2a30f557SAndreas Gohr                       searchwords as SW
33810dcb86fSAndreas Gohr                 WHERE S.dt >= ? AND S.dt <= ?
339*2a30f557SAndreas Gohr                   AND S.id = SW.sid
34010dcb86fSAndreas Gohr              GROUP BY SW.word
34110dcb86fSAndreas Gohr              ORDER BY cnt DESC, SW.word" .
342f9e60319SAndreas Gohr            $this->limit;
343*2a30f557SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
344b6632b6eSAndreas Gohr    }
345b6632b6eSAndreas Gohr
346f9e60319SAndreas Gohr    /**
347f9e60319SAndreas Gohr     * @return array
348f9e60319SAndreas Gohr     */
349f9e60319SAndreas Gohr    public function outlinks(): array
350b6632b6eSAndreas Gohr    {
351b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, link as url
3527428e816SAndreas Gohr                  FROM outlinks as A
3537428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
354b6632b6eSAndreas Gohr              GROUP BY link
355b6632b6eSAndreas Gohr              ORDER BY cnt DESC, link" .
356f9e60319SAndreas Gohr            $this->limit;
357f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
358b6632b6eSAndreas Gohr    }
359b6632b6eSAndreas Gohr
360f9e60319SAndreas Gohr    /**
361f9e60319SAndreas Gohr     * @return array
362f9e60319SAndreas Gohr     */
363f9e60319SAndreas Gohr    public function pages(): array
364b6632b6eSAndreas Gohr    {
36510dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, P.page
36610dcb86fSAndreas Gohr                  FROM pageviews as P
36710dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
36810dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
36910dcb86fSAndreas Gohr                   AND S.ua_type = ?
37010dcb86fSAndreas Gohr              GROUP BY P.page
37110dcb86fSAndreas Gohr              ORDER BY cnt DESC, P.page" .
372f9e60319SAndreas Gohr            $this->limit;
373f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
374b6632b6eSAndreas Gohr    }
375b6632b6eSAndreas Gohr
376f9e60319SAndreas Gohr    /**
377f9e60319SAndreas Gohr     * @return array
378f9e60319SAndreas Gohr     */
379f9e60319SAndreas Gohr    public function edits(): array
380b6632b6eSAndreas Gohr    {
381b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, page
3827428e816SAndreas Gohr                  FROM edits as A
3837428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
384b6632b6eSAndreas Gohr              GROUP BY page
385b6632b6eSAndreas Gohr              ORDER BY cnt DESC, page" .
386f9e60319SAndreas Gohr            $this->limit;
387f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
388b6632b6eSAndreas Gohr    }
389b6632b6eSAndreas Gohr
390f9e60319SAndreas Gohr    /**
391f9e60319SAndreas Gohr     * @return array
392f9e60319SAndreas Gohr     */
393f9e60319SAndreas Gohr    public function images(): array
394b6632b6eSAndreas Gohr    {
395b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
3967428e816SAndreas Gohr                  FROM media as A
3977428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
3987428e816SAndreas Gohr                   AND mime1 = ?
399b6632b6eSAndreas Gohr              GROUP BY media
400b6632b6eSAndreas Gohr              ORDER BY cnt DESC, media" .
401f9e60319SAndreas Gohr            $this->limit;
402f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'image']);
403b6632b6eSAndreas Gohr    }
404b6632b6eSAndreas Gohr
405f9e60319SAndreas Gohr    /**
406f9e60319SAndreas Gohr     * @return array
407f9e60319SAndreas Gohr     */
408f9e60319SAndreas Gohr    public function imagessum(): array
409b6632b6eSAndreas Gohr    {
410b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
4117428e816SAndreas Gohr                  FROM media as A
4127428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
4137428e816SAndreas Gohr                   AND mime1 = ?";
414f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'image']);
415b6632b6eSAndreas Gohr    }
416b6632b6eSAndreas Gohr
417f9e60319SAndreas Gohr    /**
418f9e60319SAndreas Gohr     * @return array
419f9e60319SAndreas Gohr     */
420f9e60319SAndreas Gohr    public function downloads(): array
421b6632b6eSAndreas Gohr    {
422b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, media, SUM(size) as filesize
4237428e816SAndreas Gohr                  FROM media as A
4247428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
4257428e816SAndreas Gohr                   AND mime1 != ?
426b6632b6eSAndreas Gohr              GROUP BY media
427b6632b6eSAndreas Gohr              ORDER BY cnt DESC, media" .
428f9e60319SAndreas Gohr            $this->limit;
429f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'image']);
430b6632b6eSAndreas Gohr    }
431b6632b6eSAndreas Gohr
432f9e60319SAndreas Gohr    /**
433f9e60319SAndreas Gohr     * @return array
434f9e60319SAndreas Gohr     */
435f9e60319SAndreas Gohr    public function downloadssum(): array
436b6632b6eSAndreas Gohr    {
437b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, SUM(size) as filesize
4387428e816SAndreas Gohr                  FROM media as A
4397428e816SAndreas Gohr                 WHERE A.dt >= ? AND A.dt <= ?
4407428e816SAndreas Gohr                   AND mime1 != ?";
441f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'image']);
442b6632b6eSAndreas Gohr    }
443b6632b6eSAndreas Gohr
444f9e60319SAndreas Gohr    /**
445f9e60319SAndreas Gohr     * @return array
446f9e60319SAndreas Gohr     */
447f9e60319SAndreas Gohr    public function referer(): array
448b6632b6eSAndreas Gohr    {
44910dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.url
45010dcb86fSAndreas Gohr                  FROM pageviews as P
45110dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
45210dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
45310dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
45410dcb86fSAndreas Gohr                   AND S.ua_type = ?
45510dcb86fSAndreas Gohr                   AND R.url IS NOT NULL
45610dcb86fSAndreas Gohr                   AND R.url != ''
45710dcb86fSAndreas Gohr                   AND R.engine IS NULL
45810dcb86fSAndreas Gohr              GROUP BY R.url
45910dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.url" .
460f9e60319SAndreas Gohr            $this->limit;
46110dcb86fSAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
462b6632b6eSAndreas Gohr    }
463b6632b6eSAndreas Gohr
464f9e60319SAndreas Gohr    /**
465f9e60319SAndreas Gohr     * @return array
466f9e60319SAndreas Gohr     */
467f9e60319SAndreas Gohr    public function newreferer(): array
468b6632b6eSAndreas Gohr    {
46910dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, R.url
47010dcb86fSAndreas Gohr                  FROM pageviews as P
47110dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
47210dcb86fSAndreas Gohr                  LEFT JOIN referers as R ON P.ref_id = R.id
47310dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
47410dcb86fSAndreas Gohr                   AND S.ua_type = ?
47510dcb86fSAndreas Gohr                   AND R.url IS NOT NULL
47610dcb86fSAndreas Gohr                   AND R.url != ''
47710dcb86fSAndreas Gohr                   AND R.engine IS NULL
47810dcb86fSAndreas Gohr                   AND R.dt >= ?
47910dcb86fSAndreas Gohr              GROUP BY R.url
48010dcb86fSAndreas Gohr              ORDER BY cnt DESC, R.url" .
481f9e60319SAndreas Gohr            $this->limit;
48210dcb86fSAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser', $this->from]);
483b6632b6eSAndreas Gohr    }
484b6632b6eSAndreas Gohr
485f9e60319SAndreas Gohr    /**
486f9e60319SAndreas Gohr     * @return array
487f9e60319SAndreas Gohr     */
488f9e60319SAndreas Gohr    public function countries(): array
489b6632b6eSAndreas Gohr    {
49010dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT P.session) as cnt, I.country
49110dcb86fSAndreas Gohr                  FROM pageviews as P
49210dcb86fSAndreas Gohr                  LEFT JOIN iplocation as I ON P.ip = I.ip
49310dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
49410dcb86fSAndreas Gohr                   AND I.country IS NOT NULL
49510dcb86fSAndreas Gohr                   AND I.country != ''
49610dcb86fSAndreas Gohr              GROUP BY I.code
49710dcb86fSAndreas Gohr              ORDER BY cnt DESC, I.country" .
498f9e60319SAndreas Gohr            $this->limit;
499f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
500b6632b6eSAndreas Gohr    }
501b6632b6eSAndreas Gohr
502f9e60319SAndreas Gohr    /**
503f9e60319SAndreas Gohr     * @param bool $ext return extended information
504f9e60319SAndreas Gohr     * @return array
505f9e60319SAndreas Gohr     */
506*2a30f557SAndreas Gohr    public function browsers(bool $ext = false): array
507b6632b6eSAndreas Gohr    {
508b6632b6eSAndreas Gohr        if ($ext) {
50910dcb86fSAndreas Gohr            $sel = 'S.ua_info as browser, S.ua_ver';
51010dcb86fSAndreas Gohr            $grp = 'S.ua_info, S.ua_ver';
511b6632b6eSAndreas Gohr        } else {
512*2a30f557SAndreas Gohr            $sel = 'S.ua_info as browser';
51310dcb86fSAndreas Gohr            $grp = 'S.ua_info';
514b6632b6eSAndreas Gohr        }
515b6632b6eSAndreas Gohr
51610dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.session) as cnt, $sel
51710dcb86fSAndreas Gohr                  FROM sessions as S
51810dcb86fSAndreas Gohr                 WHERE S.dt >= ? AND S.dt <= ?
51910dcb86fSAndreas Gohr                   AND S.ua_type = ?
520b6632b6eSAndreas Gohr              GROUP BY $grp
52110dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.ua_info" .
522f9e60319SAndreas Gohr            $this->limit;
523f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
524b6632b6eSAndreas Gohr    }
525b6632b6eSAndreas Gohr
526f9e60319SAndreas Gohr    /**
527f9e60319SAndreas Gohr     * @return array
528f9e60319SAndreas Gohr     */
529f9e60319SAndreas Gohr    public function os(): array
530b6632b6eSAndreas Gohr    {
53110dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.session) as cnt, S.os
53210dcb86fSAndreas Gohr                  FROM sessions as S
53310dcb86fSAndreas Gohr                 WHERE S.dt >= ? AND S.dt <= ?
53410dcb86fSAndreas Gohr                   AND S.ua_type = ?
53510dcb86fSAndreas Gohr              GROUP BY S.os
53610dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.os" .
537f9e60319SAndreas Gohr            $this->limit;
538f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
539b6632b6eSAndreas Gohr    }
540b6632b6eSAndreas Gohr
541f9e60319SAndreas Gohr    /**
542f9e60319SAndreas Gohr     * @return array
543f9e60319SAndreas Gohr     */
544f9e60319SAndreas Gohr    public function topuser(): array
545b6632b6eSAndreas Gohr    {
54610dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, S.user
54710dcb86fSAndreas Gohr                  FROM pageviews as P
54810dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
54910dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
55010dcb86fSAndreas Gohr                   AND S.ua_type = ?
55110dcb86fSAndreas Gohr                   AND S.user IS NOT NULL
55210dcb86fSAndreas Gohr                   AND S.user != ?
55310dcb86fSAndreas Gohr              GROUP BY S.user
55410dcb86fSAndreas Gohr              ORDER BY cnt DESC, S.user" .
555f9e60319SAndreas Gohr            $this->limit;
556f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser', '']);
557b6632b6eSAndreas Gohr    }
558b6632b6eSAndreas Gohr
559f9e60319SAndreas Gohr    /**
560f9e60319SAndreas Gohr     * @return array
561f9e60319SAndreas Gohr     */
562f9e60319SAndreas Gohr    public function topeditor(): array
563b6632b6eSAndreas Gohr    {
564b6632b6eSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, user
56510dcb86fSAndreas Gohr                  FROM edits as E
56610dcb86fSAndreas Gohr             LEFT JOIN sessions as S ON E.session = S.session
56710dcb86fSAndreas Gohr                 WHERE E.dt >= ? AND E.dt <= ?
56810dcb86fSAndreas Gohr                   AND S.user != ?
569b6632b6eSAndreas Gohr              GROUP BY user
570b6632b6eSAndreas Gohr              ORDER BY cnt DESC, user" .
571f9e60319SAndreas Gohr            $this->limit;
572f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, '']);
573b6632b6eSAndreas Gohr    }
574b6632b6eSAndreas Gohr
575f9e60319SAndreas Gohr    /**
576f9e60319SAndreas Gohr     * @return array
577f9e60319SAndreas Gohr     */
578f9e60319SAndreas Gohr    public function topgroup(): array
579b6632b6eSAndreas Gohr    {
58010dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, G.`group`
58110dcb86fSAndreas Gohr                  FROM pageviews as P
58210dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
58310dcb86fSAndreas Gohr                  LEFT JOIN groups as G ON S.user = G.user
58410dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
58510dcb86fSAndreas Gohr                   AND S.ua_type = ?
58610dcb86fSAndreas Gohr                   AND G.`group` IS NOT NULL
58710dcb86fSAndreas Gohr              GROUP BY G.`group`
58810dcb86fSAndreas Gohr              ORDER BY cnt DESC, G.`group`" .
589f9e60319SAndreas Gohr            $this->limit;
59010dcb86fSAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser']);
591b6632b6eSAndreas Gohr    }
592b6632b6eSAndreas Gohr
593f9e60319SAndreas Gohr    /**
594f9e60319SAndreas Gohr     * @return array
595f9e60319SAndreas Gohr     */
596f9e60319SAndreas Gohr    public function topgroupedit(): array
597b6632b6eSAndreas Gohr    {
59810dcb86fSAndreas Gohr        $sql = "SELECT COUNT(*) as cnt, G.`group`
59910dcb86fSAndreas Gohr                  FROM edits as E
60010dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON E.session = S.session
60110dcb86fSAndreas Gohr                  LEFT JOIN groups as G ON S.user = G.user
60210dcb86fSAndreas Gohr                 WHERE E.dt >= ? AND E.dt <= ?
60310dcb86fSAndreas Gohr                   AND G.`group` IS NOT NULL
60410dcb86fSAndreas Gohr              GROUP BY G.`group`
60510dcb86fSAndreas Gohr              ORDER BY cnt DESC, G.`group`" .
606f9e60319SAndreas Gohr            $this->limit;
60710dcb86fSAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to]);
608b6632b6eSAndreas Gohr    }
609b6632b6eSAndreas Gohr
610b6632b6eSAndreas Gohr
611f9e60319SAndreas Gohr    /**
612f9e60319SAndreas Gohr     * @return array
613f9e60319SAndreas Gohr     */
614f9e60319SAndreas Gohr    public function resolution(): array
615b6632b6eSAndreas Gohr    {
61610dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.uid) as cnt,
61710dcb86fSAndreas Gohr                       ROUND(P.screen_x/100)*100 as res_x,
61810dcb86fSAndreas Gohr                       ROUND(P.screen_y/100)*100 as res_y,
61910dcb86fSAndreas Gohr                       CAST(ROUND(P.screen_x/100)*100 AS int) || 'x' || CAST(ROUND(P.screen_y/100)*100 AS int) as resolution
62010dcb86fSAndreas Gohr                  FROM pageviews as P
62110dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
62210dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
62310dcb86fSAndreas Gohr                   AND S.ua_type = ?
62410dcb86fSAndreas Gohr                   AND P.screen_x != ?
62510dcb86fSAndreas Gohr                   AND P.screen_y != ?
626b6632b6eSAndreas Gohr              GROUP BY resolution
627b6632b6eSAndreas Gohr              ORDER BY cnt DESC" .
628f9e60319SAndreas Gohr            $this->limit;
629f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser', 0, 0]);
630b6632b6eSAndreas Gohr    }
631b6632b6eSAndreas Gohr
632f9e60319SAndreas Gohr    /**
633f9e60319SAndreas Gohr     * @return array
634f9e60319SAndreas Gohr     */
635f9e60319SAndreas Gohr    public function viewport(): array
636b6632b6eSAndreas Gohr    {
63710dcb86fSAndreas Gohr        $sql = "SELECT COUNT(DISTINCT S.uid) as cnt,
63810dcb86fSAndreas Gohr                       ROUND(P.view_x/100)*100 as res_x,
63910dcb86fSAndreas Gohr                       ROUND(P.view_y/100)*100 as res_y,
64010dcb86fSAndreas Gohr                       CAST(ROUND(P.view_x/100)*100 AS int) || 'x' || CAST(ROUND(P.view_y/100)*100 AS int) as resolution
64110dcb86fSAndreas Gohr                  FROM pageviews as P
64210dcb86fSAndreas Gohr                  LEFT JOIN sessions as S ON P.session = S.session
64310dcb86fSAndreas Gohr                 WHERE P.dt >= ? AND P.dt <= ?
64410dcb86fSAndreas Gohr                   AND S.ua_type = ?
64510dcb86fSAndreas Gohr                   AND P.view_x != ?
64610dcb86fSAndreas Gohr                   AND P.view_y != ?
647b6632b6eSAndreas Gohr              GROUP BY resolution
648b6632b6eSAndreas Gohr              ORDER BY cnt DESC" .
649f9e60319SAndreas Gohr            $this->limit;
650b6632b6eSAndreas Gohr
651f9e60319SAndreas Gohr        return $this->db->queryAll($sql, [$this->from, $this->to, 'browser', 0, 0]);
652b6632b6eSAndreas Gohr    }
653b6632b6eSAndreas Gohr
654f9e60319SAndreas Gohr    /**
655f9e60319SAndreas Gohr     * @return array
656f9e60319SAndreas Gohr     */
657f9e60319SAndreas Gohr    public function seenusers(): array
658b6632b6eSAndreas Gohr    {
65910dcb86fSAndreas Gohr        $sql = "SELECT `user`, MAX(`dt`) as dt
66010dcb86fSAndreas Gohr                  FROM users
66110dcb86fSAndreas Gohr                 WHERE `user` IS NOT NULL
66210dcb86fSAndreas Gohr                   AND `user` != ''
66310dcb86fSAndreas Gohr              GROUP BY `user`
664b6632b6eSAndreas Gohr              ORDER BY `dt` DESC" .
665f9e60319SAndreas Gohr            $this->limit;
666b6632b6eSAndreas Gohr
667f9e60319SAndreas Gohr        return $this->db->queryAll($sql);
668b6632b6eSAndreas Gohr    }
669b6632b6eSAndreas Gohr}
670