Index: www/sites/all/modules/custom/np_scan/np_scan.analytics.inc =================================================================== --- www/sites/all/modules/custom/np_scan/np_scan.analytics.inc (revision 34376) +++ www/sites/all/modules/custom/np_scan/np_scan.analytics.inc (working copy) @@ -56,14 +56,11 @@ // get the basic order $rs = db_query_range(" SELECT ss.active_vid as vid - FROM {node} n - INNER JOIN {scan_settings} ss ON n.nid = ss.nid AND ss.status = %d - WHERE n.nid IN ( - SELECT o.nid - FROM {og_ancestry} o - WHERE o.group_nid IN (" . $client_id_placeholders . ") - ) - ORDER BY ". ($timeframe == 24 ? 'n.np_views_24' : 'n.np_views_page') ." DESC + FROM {scan_hits} h + INNER JOIN {scan_settings} ss ON h.nid = ss.nid AND ss.status = %d + INNER JOIN {og_ancestry} o ON h.nid = o.nid + WHERE o.group_nid IN (" . $client_id_placeholders . ") + ORDER BY ". ($timeframe == 24 ? 'h.page_today' : 'h.page_last') ." DESC ", $args, 0, $limit); while ($row = db_fetch_object($rs)) { $scans[$row->$key] = FALSE; @@ -101,9 +98,12 @@ $scan_placeholders = implode(',', array_fill(0, count($scans), '%d')); $rs = db_query(" SELECT s.scan_id, s.nid, s.vid, - n.np_views_page, n.np_views_24 AS np_views_page_24, n.np_views_widget, n.np_views_widget_24 + h.page_all AS np_views_page, + h.page_today AS np_views_page_24, + h.widget_all AS np_views_widget, + h.widget_today AS np_views_widget_24 FROM {scan} s - INNER JOIN {node} n ON n.nid = s.nid + INNER JOIN {scan_hits} h ON h.nid = s.nid WHERE s." . $key . " IN ( " . $scan_placeholders . ") ", array_keys($scans)); while ($statistics = db_fetch_object($rs)) { @@ -215,14 +215,16 @@ scan_api_set_active_shard('misc'); // Some general statistics $statistics = db_fetch_object(db_query(" - SELECT SUM(ss.status) AS online, COUNT(*) AS count, SUM(n.np_views_24) AS views_24, SUM(n.np_views_page) AS views, SUM(n.np_views_widget) AS widget_views - FROM {node} n - INNER JOIN {scan_settings} ss ON n.nid = ss.nid - WHERE n.nid IN ( - SELECT o.nid - FROM {og_ancestry} o - WHERE o.group_nid IN (" . $client_id_placeholders . ") - ) + SELECT SUM(ss.status) AS online, + COUNT(*) AS count, + SUM(h.page_today) AS views_24, + SUM(h.page_all) AS views, + SUM(h.widget_today) AS widget_views_24, + SUM(h.widget_all) AS widget_views + FROM {scan_hits} h + INNER JOIN {scan_settings} ss ON h.nid = ss.nid + INNER JOIN {og_ancestry} o ON h.nid = o.nid + WHERE o.group_nid IN (" . $client_id_placeholders . ") ", $client_ids)); scan_api_set_active_shard(); Index: www/sites/all/modules/custom/np_scan/np_scan.module =================================================================== --- www/sites/all/modules/custom/np_scan/np_scan.module (revision 34376) +++ www/sites/all/modules/custom/np_scan/np_scan.module (working copy) @@ -432,6 +432,36 @@ /* Backfilling. Wait until next run. */ } } + + // Mini-worker to update the page views for this minute. + require_once(drupal_get_path('module', 'np_potpourri') .'/BeanStalk.class.php'); + $beanstalk = BeanStalk::open(array( + 'servers' => array( variable_get('np_scan_beanstalk_host', '67.220.204.51:11300') ), + 'select' => 'random peek', + 'auto_unyaml' => false, + )); + + $beanstalk->watch('hit-logger'); + scan_api_set_active_shard('misc'); + while ($job = $beanstalk->reserve_with_timeout(0)) { + $data = explode('|', $job->get()); + switch ($data[1]) { + case 'widget': + $query = 'UPDATE {scan_hits} SET widget_today = widget_today + 1, widget_all = widget_all + 1, widget_last = %d WHERE nid = %d'; + break; + case 'page': + $query = 'UPDATE {scan_hits} SET page_today = page_today + 1, page_all = page_all + 1, page_last = %d WHERE nid = %d'; + break; + } + db_query($query, $data[2], $data[0]); + if (db_affected_rows()) { + $job->delete(); + } + else { + // Hmm, update didn't work. Let's try again in a minute... + $job->release(1024, 60); + } + } scan_api_set_active_shard(); } @@ -764,6 +794,9 @@ $scan_settings->use_globals = NP_SCAN_USE_GLOBAL_EXPLETIVES | NP_SCAN_USE_GLOBAL_USERS | NP_SCAN_USE_GLOBAL_URLS; drupal_write_record('scan_settings', $scan_settings); + // Add the hit counter / denormalized data for sorting on mysql side. + db_query('INSERT INTO {scan_hits} (nid, client_id, status, created) VALUES (%d, %d, %d, %d)', $node->nid, $node->og_groups[0], $scan_settings->status, $node->created); + if (isset($node->locations)) { foreach ($node->locations as $location) { $location->scan_id = $scan->scan_id; @@ -912,6 +945,7 @@ np_scan_queue_delete($row->scan_id); } db_query("DELETE FROM {scan} WHERE nid = %d", $node->nid); + db_query("DELETE FROM {scan_hits} WHERE nid = %d", $node->nid); db_query("DELETE FROM {scan_settings} WHERE nid = %d", $node->nid); db_query("DELETE FROM {scan_destination} WHERE nid = %d", $node->nid); np_scan_reload_workers('scan'); Index: www/sites/all/modules/custom/np_scan_export/np_scan_export.module =================================================================== --- www/sites/all/modules/custom/np_scan_export/np_scan_export.module (revision 34376) +++ www/sites/all/modules/custom/np_scan_export/np_scan_export.module (working copy) @@ -53,13 +53,7 @@ foreach($groups as $gid => $group) { // Select all the views for scans in that group // that were viewed in the last 24 hrs. - $result = db_query(" - SELECT n.nid, n.title, n.created, n.np_views_page, n.np_views_24, n.np_views_widget, n.np_views_widget_24 - FROM {node} n - LEFT JOIN {og_ancestry} oga ON n.nid = oga.nid - INNER JOIN {node} noga ON oga.group_nid = noga.nid - WHERE oga.group_nid = %d AND - (n.np_views_24 > 0 OR n.np_views_widget_24 > 0)", $gid); + $result = db_query('SELECT h.* FROM {scan_hits} h INNER JOIN {og_ancestry} o ON h.nid = o.nid WHERE o.group_nid = %d AND (h.widget_today > 0 OR h.page_today > 0)', $gid); $csv = ''; while ($scan = db_fetch_object($result)) { @@ -67,10 +61,10 @@ array( $scan->title, url("node/$scan->nid/edit", array('absolute' => TRUE)), - $scan->np_views_page, - $scan->np_views_24, - $scan->np_views_widget, - $scan->np_views_widget_24, + $scan->page_all, + $scan->page_today, + $scan->widget_all, + $scan->widget_today, ) ); } Index: www/sites/all/modules/custom/np_potpourri/np_potpourri.beanstalk.inc =================================================================== --- www/sites/all/modules/custom/np_potpourri/np_potpourri.beanstalk.inc (revision 34376) +++ www/sites/all/modules/custom/np_potpourri/np_potpourri.beanstalk.inc (working copy) @@ -18,6 +18,7 @@ $searchapi_stats = array(); $statworker_stats = array(); $delete_stats = array(); + $hit_stats = array(); $default_stats = array(); $stats = array(); @@ -42,6 +43,9 @@ $beanstalk->stats_tube($prefix . "delete", $delete_stats); $output .= t('

Scan Deletion Queue stats

'); $output .= theme('table', array(), _np_potpourri_beanstalk_format($delete_stats)); + $beanstalk->stats_tube($prefix . "hit-logger", $hit_stats); + $output .= t('

Scan Hit Logger Queue stats

'); + $output .= theme('table', array(), _np_potpourri_beanstalk_format($hit_stats)); $beanstalk->stats_tube($prefix . "default", $default_stats); $output .= t('

Default Queue stats

'); $output .= theme('table', array(), _np_potpourri_beanstalk_format($default_stats)); Index: www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.info =================================================================== --- www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.info (revision 34376) +++ www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.info (working copy) @@ -3,3 +3,4 @@ description = Recommend scans and scan items, and possibly other scan related voting. package = NP core = 6.x +dependencies[] = np_log Index: www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.module =================================================================== --- www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.module (revision 34376) +++ www/sites/all/modules/custom/np_scan_recommend/np_scan_recommend.module (working copy) @@ -220,7 +220,8 @@ elseif ($content_type == 'scan') { foreach ($cached as $cache) { if ($cache['function'] == 'sum' && $cache['tag'] == NP_SCAN_RECOMMEND_SCAN && $cache['value_type'] =='points') { - db_query('UPDATE {node} SET recommend = %d WHERE nid = %d', $cache['value'], $content_id); + // Table is maintained by np_log. + db_query('UPDATE {scan_hits} SET recommend = %d WHERE nid = %d', $cache['value'], $content_id); } } } Index: www/sites/all/modules/custom/np_log/np_log.module =================================================================== --- www/sites/all/modules/custom/np_log/np_log.module (revision 34376) +++ www/sites/all/modules/custom/np_log/np_log.module (working copy) @@ -11,3 +11,39 @@ function np_log_views_api() { return array('api' => 2.0); } + +/** + * Implementation of hook_cron(). + * On-the-hour cron hook to roll over daily hit counting at the proper time. + */ +function np_log_cron() { + $timestamp = time(); + $tz = date_default_timezone_get(); + + $zones = db_query('SELECT DISTINCT field_timezone_value AS tz FROM {content_type_group}'); + while ($zone = db_fetch_object($zones)) { + $key = 'cleanup:' . $zone->tz; + date_default_timezone_set($zone->tz); + // If we have changed days in the timezone we are processing... + if (date('d', variable_get($key, 0)) != date('d', $timestamp)) { + variable_set($key, $time); + // h: Hit counter table. + // n: Group nodes. Used to ensure we're only using the newest revision of the group node. + // g: Group settings. Used to match on timezone. + // o: Ancestry. Used to determine what nodes the matched groups have. + db_query("UPDATE {scan_hits} h, + {node} n, + {content_type_group} g, + {og_ancestry} o + SET widget_today = 0, page_today = 0 + WHERE n.nid = o.group_nid + AND n.vid = g.vid + AND h.nid = o.nid + AND g.field_timezone_value = '%s'", $zone->tz); + $count = db_affected_rows(); + watchdog('np_log', 'Reset daily counts on %count nodes in timezone %tz', array('%count' => $count, '%tz' => $zone->tz)); + } + } + // Restore original timezone. + date_default_timezone_set($tz); +} Index: www/sites/all/modules/custom/np_log/np_log.views.inc =================================================================== --- www/sites/all/modules/custom/np_log/np_log.views.inc (revision 34376) +++ www/sites/all/modules/custom/np_log/np_log.views.inc (working copy) @@ -1,9 +1,91 @@ array( + 'left_field' => 'nid', + 'field' => 'nid', + ), + ); + + $data['scan_hits']['widget_today'] = array( + 'title' => t('Widget Views: Today'), + 'help' => t('The number of widget views in the last 24 hours.'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + + $data['scan_hits']['widget_all'] = array( + 'title' => t('Widget Views: All'), + 'help' => t('All widget views for scan.'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + + $data['scan_hits']['widget_last'] = array( + 'title' => t('Widget Views: Last View Timestamp'), + 'help' => t('The timestamp of the last widget view.'), + 'field' => array( + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort_date', + ), + ); + + $data['scan_hits']['page_today'] = array( + 'title' => t('Page Views: Today'), + 'help' => t('The number of page views in the last 24 hours'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + + $data['scan_hits']['page_all'] = array( + 'title' => t('Page Views: All'), + 'help' => t('All page views for scan.'), + 'group' => t('Scan'), + 'field' => array( + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort', + ), + ); + + $data['scan_hits']['page_last'] = array( + 'title' => t('Page Views: Last View Timestamp'), + 'help' => t('The timestamp of the last page view.'), + 'field' => array( + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'handler' => 'views_handler_sort_date', + ), + ); + + // @@@ Delete the rest of this when views are converted. $data['node']['np_views_page'] = array( - 'title' => t('Page Views'), + 'title' => t('LEGACY Page Views'), 'help' => t('All page views for scan.'), 'group' => t('Scan'), 'field' => array( @@ -16,7 +98,7 @@ ); $data['node']['np_views_widget'] = array( - 'title' => t('Widget Views: All'), + 'title' => t('LEGACY Widget Views: All'), 'help' => t('All widget views for scan.'), 'group' => t('Scan'), 'field' => array( @@ -29,7 +111,7 @@ ); $data['node']['np_views_widget_24'] = array( - 'title' => t('Widget Views: 24h'), + 'title' => t('LEGACY Widget Views: 24h'), 'help' => t('The number of widget views in the last 24 hours.'), 'group' => t('Scan'), 'field' => array( @@ -42,7 +124,7 @@ ); $data['node']['np_views_24'] = array( - 'title' => t('24 hrs views'), + 'title' => t('LEGACY 24 hrs views'), 'help' => t('The number of views in the last 24 hours'), 'group' => t('Scan'), 'field' => array( Index: www/sites/all/modules/custom/np_log/np_log.install =================================================================== --- www/sites/all/modules/custom/np_log/np_log.install (revision 0) +++ www/sites/all/modules/custom/np_log/np_log.install (revision 0) @@ -0,0 +1,111 @@ + t('Stores daily and all-time tallies for widget and page views.'), + 'fields' => array( + 'nid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => t('The nid being tracked.'), + ), + 'client_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Denormalized og nid of client.'), + ), + 'created' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Denormalized node created time.'), + ), + 'status' => array( + 'type' => 'int', + 'size' => 'tiny', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Denormalized scan status from {scan_settings}.'), + ), + 'recommendations' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Number of recommendations (maintained by np_scan_recommend.module)'), + ), + 'widget_today' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Daily widget views. Reset once a day.'), + ), + 'widget_all' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('All time widget views.'), + ), + 'widget_last' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Unix timestamp of last widget view.'), + ), + 'page_today' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Daily page hits. Reset once a day.'), + ), + 'page_all' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('All time page hits.'), + ), + 'page_last' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + 'description' => t('Unix timestamp of last page hit.'), + ), + ), + 'primary key' => array('nid'), + 'indexes' => array( + 'widget_today_idx' => array('widget_today'), + 'widget_all_idx' => array('widget_all'), + 'widget_last_idx' => array('widget_last'), + 'page_today_idx' => array('page_today'), + 'page_all_idx' => array('page_all'), + 'page_last_idx' => array('page_last'), + ), + ); + return $schema; +} Index: www/np_log.php =================================================================== --- www/np_log.php (revision 34376) +++ www/np_log.php (working copy) @@ -1,32 +1,30 @@ add($full_key, 1, FALSE, $expire) ? 1 : $mc->increment($full_key); - if ($new_value && ($type == 'page' || $new_value % 5 == 0)) { - drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - db_set_active('scan'); - if ($type == 'page') { - db_query("UPDATE {node} SET np_views_24 = np_views_24 + 1, np_views_page = np_views_page + 1 WHERE nid = %d", $nid); - } - else { - db_query("UPDATE {node} SET np_views_widget_24 = %d, np_views_widget = np_views_widget + 5, np_views_widget_last = UNIX_TIMESTAMP() WHERE nid = %d", $new_value, $nid); - } - db_set_active(); + $type = ($_GET['type'] == 'widget') ? 'widget' : 'page'; + $timestamp = $_SERVER["REQUEST_TIME"]; + //@todo This is the best place to collect the IP address of the client. + // We can find the proxy list in the server variable + // $_SERVER["HTTP_X_FORWARDED_FOR"]. + + if ($nid > 0) { + $beanstalk = BeanStalk::open(array( + 'servers' => array( variable_get('np_scan_beanstalk_host', '67.220.204.51:11300') ), + 'select' => 'random peek', + 'auto_unyaml' => false, + )); + $job = array( + $nid, + $type, + $timestamp, + ); + + $beanstalk->put(1024, 0, 120, implode('|', $job), 'hit-logger'); } } header('Content-type: image/gif');