From 9e5dccf8b7c6363b4ea75794631e0ff4645ada6f Mon Sep 17 00:00:00 2001 From: Brian Huisman Date: Fri, 3 Nov 2023 14:26:43 -0400 Subject: [PATCH] Add Statistics page Added a Statistics page. Probably still needs some work. --- orcinus/admin.php | 373 +++++++++++++++++++++++++++++------------- orcinus/css/admin.css | 8 + orcinus/js/admin.js | 55 +++++++ orcinus/search.php | 7 +- 4 files changed, 328 insertions(+), 115 deletions(-) diff --git a/orcinus/admin.php b/orcinus/admin.php index 0dae315..1aee1c2 100644 --- a/orcinus/admin.php +++ b/orcinus/admin.php @@ -104,7 +104,8 @@ if (!in_array($_ODATA['admin_query_log_display'], $_RDATA['admin_query_log_displ $_RDATA['admin_pages'] = array( 'crawler' => 'Crawler', 'index' => 'Page Index', - 'search' => 'Search' + 'search' => 'Search', + 'stats' => 'Statistics' ); if ($_ODATA['s_limit_query_log']) $_RDATA['admin_pages']['queries'] = 'Query Log'; @@ -1259,7 +1260,9 @@ ORCINUS; $_RDATA['s_crawldata_info']['MIME-types'][$row['content_mime']] = $row['num']; } } else $_SESSION['error'][] = 'Could not read charset counts from search database.'; + break; + case 'stats': // Average hits per hour: First find the oldest `stamp` in the // database, then base all averages on the difference between that // time and now; also get average number of results @@ -1293,6 +1296,41 @@ ORCINUS; $_RDATA['q_median_results'] = (count($median) & 1) ? $median[$index]['results'] : ($median[$index - 1]['results'] + $median[$index]['results']) / 2; } } else $_SESSION['error'][] = 'Could not read result counts from query log.'; + + $select = $_DDATA['pdo']->query('SELECT INET_NTOA(`ip`) AS `ipaddr` FROM `'.$_DDATA['tbprefix'].'query`;')->fetchAll(); + $locCount = array(); + $locData = array('unk' => 'Unknown'); + foreach ($select as $row) { + try { + $geo = $_GEOIP2->country($row['ipaddr']); + if (!empty($geo->raw['country']['iso_code'])) { + if (empty($locCount[$geo->raw['country']['iso_code']])) { + $locCount[$geo->raw['country']['iso_code']] = 1; + } else $locCount[$geo->raw['country']['iso_code']]++; + $locData[$geo->raw['country']['iso_code']] = $geo->raw['country']['names']['en']; + } else { + if (empty($locCount['unk'])) { + $locCount['unk'] = 1; + } else $locCount['unk']++; + } + } catch(Exception $e) { + if (empty($locCount['unk'])) { + $locCount['unk'] = 1; + } else $locCount['unk']++; + } + } + arsort($locCount); + + $select = $_DDATA['pdo']->query('SELECT COUNT(*) as `searches`, DAYNAME(FROM_UNIXTIME(`stamp`)) as `weekday` FROM `'.$_DDATA['tbprefix'].'query` GROUP BY `weekday`;')->fetchAll(); + $dayWalker = array('Sun' => 0, 'Mon' => 0, 'Tue' => 0, 'Wed' => 0, 'Thu' => 0, 'Fri' => 0, 'Sat' => 0); + foreach ($select as $day) + $dayWalker[substr($day['weekday'], 0, 3)] = $day['searches']; + + $select = $_DDATA['pdo']->query('SELECT COUNT(*) as `searches`, `stamp` FROM `'.$_DDATA['tbprefix'].'query` GROUP BY HOUR(FROM_UNIXTIME(`stamp`));')->fetchAll(); + for ($x = 0, $days = array(), $hourWalker = array(); $x < 24; $x++) + $hourWalker[str_pad((string)$x, 2, '0', STR_PAD_LEFT).':00'] = 0; + foreach ($select as $hour) + $hourWalker[date('H:00', $hour['stamp'])] = $hour['searches']; break; case 'queries': @@ -2115,122 +2153,65 @@ ORCINUS;

Search Management

-
-
-

Search Information

-
-
    = 1) { ?> -
  • - -
  • = 24) { ?> -
  • - -
  • = 168) { ?> -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • -

    - No searches logged yet. To see search statistics here, start - using your search engine. Tell your friends! -

    -
  • +
    +
    +

    Search Data

    +
    +
      +
    • + +
    • +
    • +
    • + +
    • +
    • +
    • -
    • - -
    • -
    • - -
    • -
    • - -
    • -
    -
    -
    + } ?> +
+
+
@@ -2637,6 +2618,176 @@ ORCINUS; break; + /* ************************************************************ + * Search Statistics *************************************** */ + case 'stats': ?> +
+
+

Search Statistics

+
+ +
+
+

General Statistics

+
+
    = 1) { ?> +
  • + +
  • = 24) { ?> +
  • + +
  • = 168) { ?> +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • +

    + No searches logged yet. To see search statistics here, start + using your search engine. Tell your friends! +

    +
  • +
+
+
+
+

Query Geolocation

+
+
    +
  • + + + + + + + + $searches) { ?> + + + + + + +
    LocationSearches
    + <?php echo strtoupper($iso); ?> + ? + + (%)
    +
  • +
+
+
+
+ +
+
+
+ +

Graphical Statistics

+
+
+
    +
  • +

    All Searches by Day of Week

    + + $value) { ?> + + + + + +
    + + + +
    +
  • +
  • +

    All Searches by Time of Day

    + + $value) { ?> + + + + + +
    + + + +
    +
  • +
+
+
+
+
+
diff --git a/orcinus/css/admin.css b/orcinus/css/admin.css index 5985de2..fa8a6f7 100644 --- a/orcinus/css/admin.css +++ b/orcinus/css/admin.css @@ -53,6 +53,14 @@ input[type="number"] { table-layout:fixed; } +table.bar-graph tbody tr th time { + display:none; +} +table.bar-graph tbody tr:nth-of-type(6n+1) th time, +table.bar-graph tbody tr:last-of-type th time { + display:block; +} + .text-ellipsis { text-overflow:ellipsis; } diff --git a/orcinus/js/admin.js b/orcinus/js/admin.js index ccddd7a..6476bf6 100644 --- a/orcinus/js/admin.js +++ b/orcinus/js/admin.js @@ -358,6 +358,61 @@ for (let x = 0; x < os_index_with_selected.length; x++) { } +/* ***** Page >> Search Statistics ********************************* */ +let bars = document.querySelectorAll('table.bar-graph td[data-height]'); +for (let x = 0; x < bars.length; x++) { + bars[x].style.height = '0px'; + bars[x].style.transition = 'height 0.8s ease'; + + let tb = bars[x].parentNode.parentNode; + if (tb.offsetHeight <= parseInt(bars[x].getAttribute('data-height'))) { + tb.style.height = bars[x].getAttribute('data-height') + 'px'; + tb.dataMaxValue = bars[x].getAttribute('data-value'); + } + setTimeout((function(elem) { + elem.style.height = elem.getAttribute('data-height') + 'px'; + })(bars[x]), 5); +} + +let graphs = document.querySelectorAll('table.bar-graph'); +for (let x = 0; x < graphs.length; x++) { + let thead = graphs[x].getElementsByTagName('thead'); + if (!thead.length) { + let tbody = graphs[x].getElementsByTagName('tbody')[0]; + + // Adjust background lines + if (tbody.dataMaxValue) { + graphs[x].unitHeight = tbody.offsetHeight / tbody.dataMaxValue; + } else graphs[x].unitHeight = tbody.offsetHeight; + + // 5 intervals by default? + graphs[x].interval = tbody.offsetHeight / 5; + + graphs[x].interval = Math.max(1, Math.round(graphs[x].interval / graphs[x].unitHeight / 5) * 5 * graphs[x].unitHeight, graphs[x].unitHeight); + + + thead = document.createElement('thead'); + let tr = document.createElement('tr'); + tr.classList.add('d-flex', 'flex-column-reverse', 'justify-content-end'); + for (let y = 0; y < Math.ceil(tbody.offsetHeight / graphs[x].interval); y++) { + let td = document.createElement('td'); + td.classList.add('d-flex', 'flex-column', 'justify-content-end', 'text-end'); + td.style.height = graphs[x].interval + 'px'; + td.style.transform = 'translatey(0.6em)'; + let small = document.createElement('small'); + small.appendChild(document.createTextNode(Math.round(y * graphs[x].interval / graphs[x].unitHeight))); + td.appendChild(small); + tr.appendChild(td); + } + thead.appendChild(tr); + graphs[x].insertBefore(thead, tbody); + + tbody.style.backgroundClip = 'content-box'; + tbody.style.background = 'repeating-linear-gradient(to top, rgba(0,0,0,0.4) 0px, transparent 1px, transparent ' + graphs[x].interval + 'px)'; + } +} + + /* ***** Page >> Query Log ***************************************** */ let os_admin_query_log_display = document.querySelector('select[name="os_admin_query_log_display"]'); if (os_admin_query_log_display) { diff --git a/orcinus/search.php b/orcinus/search.php index 0555ae9..35426d1 100644 --- a/orcinus/search.php +++ b/orcinus/search.php @@ -792,11 +792,10 @@ if ($_RDATA['s_searchable_pages']) { // If the crawler 'time_start' is more than 'timeout_crawl' // seconds ago, the crawler is probably stuck. Unstick it. -if (OS_getValue('sp_crawling') && - time() - $_ODATA['sp_time_start'] > $_ODATA['sp_timeout_crawl']) { +if (OS_getValue('sp_crawling') && time() - $_ODATA['sp_time_start'] > $_ODATA['sp_timeout_crawl']) { OS_setValue('sp_crawling', 0); - $reason = 'The crawler halted unexpectedly'; + $reason = 'Crawl timeout ('.$_ODATA['sp_timeout_crawl'].'s) exceeded'; if (strpos(OS_getValue('sp_log'), "\n") === false && file_exists($_ODATA['sp_log'])) { $log = file_get_contents($_ODATA['sp_log']); @@ -806,7 +805,7 @@ if (OS_getValue('sp_crawling') && // Send failure email to the admin(s) if ($_MAIL && count($_MAIL->getAllRecipientAddresses()) && $_ODATA['sp_email_failure']) { - $_MAIL->Subject = 'Orcinus Site Search Crawler: Crawler halted unexpectedly'; + $_MAIL->Subject = 'Orcinus Site Search Crawler: Crawl timeout ('.$_ODATA['sp_timeout_crawl'].'s) exceeded'; $_MAIL->Body = implode(" \r\n", preg_grep('/^[\[\*\w\d]/', explode("\n", $_ODATA['sp_log']))); if (!$_MAIL->Send()) OS_setValue('sp_log', $_ODATA['sp_log']."\n".'[ERROR] Could not send notification email'); }