array( 'title' => 'Statistics', 'description' => 'Apache Solr Statistics settings to measure usage and performance.', 'page callback' => 'drupal_get_form', 'page arguments' => array('apachesolr_stats_admin'), 'access arguments' => array('administer search'), 'type' => MENU_LOCAL_TASK, ), 'admin/reports/apachesolr/stats' => array( 'title' => 'Statistics', 'description' => 'Report of Apache Solr usage and performance.', 'page callback' => 'apachesolr_stats_report', 'page arguments' => array(), 'access arguments' => array('administer search'), 'type' => MENU_LOCAL_TASK, ), 'apachesolr_stats/gadget' => array( 'title' => 'Apache Solr Google Gadget', 'page callback' => 'apachesolr_stats_report_gadget', 'description' => 'Provides content for Google Gadget.', 'page arguments' => array(), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ), 'apachesolr_stats/element/%/%/%' => array( 'title' => 'Apache Solr Google Gadget Elements', 'page callback' => 'apachesolr_stats_report_gadget_element', 'description' => 'Provides content for Google Gadget.', 'page arguments' => array(2,3,4), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ), ); } /** * Build the settings form. */ function apachesolr_stats_admin() { $form = array(); $form['tip'] = array( '#type' => 'markup', '#value' => t('You can also visit the !reportlink.', array('!reportlink' => l(t('report page'), 'admin/reports/apachesolr/stats'))), ); $options = array('1' => t('Enabled'), '0' => t('Disabled')); $form['access'] = array( '#type' => 'fieldset', '#title' => t('Apache Solr query log settings') ); $form['access']['apachesolr_stats_enabled'] = array( '#type' => 'radios', '#title' => t('Enable access log'), '#default_value' => variable_get('apachesolr_stats_enabled', 0), '#options' => $options, '#description' => t('Log each query to Apache Solr.') ); $period = drupal_map_assoc(array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); $form['access']['apachesolr_stats_flush_log_timer'] = array( '#type' => 'select', '#title' => t('Discard query logs older than'), '#default_value' => variable_get('apachesolr_stats_flush_log_timer', 259200), '#options' => $period, '#description' => t('Older query log entries will be automatically discarded. (Requires a correctly configured cron maintenance task.)', array('@cron' => url('admin/reports/status'))) ); $form['access']['apachesolr_stats_ignore_ip_list'] = array( '#type' => 'textarea', '#title' => t('IP addresses that will not be logged'), '#default_value' => variable_get('apachesolr_stats_ignore_ip_list', ''), '#description' => t('Enter IP addresses (e.g.: 192.168.1.2), one per line. You can match entire subnets using a partial IP address ending with a period (e.g.: 192.168.)') ); $form['gadget'] = array( '#type' => 'fieldset', '#title' => t('Google Gadget settings'), '#description' => t('You can embed statistics displays via Google Gadget by configuring the setting below.') ); $key = variable_get("apachesolr_stats_gadget_key", ""); $form['gadget']['apachesolr_stats_gadget_key'] = array( '#type' => 'textfield', '#title' => t('Google Gadget secret key'), '#description' => t("Enter a string that will be embedded in the Gadget URL. Leave empty to disable. WARNING: changing this setting will deactivate all installed gadgets; users can re-enable them by entering the new key in their gadget's preferences."), '#default_value' => $key, ); if ($key) { $gadget_url = url("apachesolr_stats/gadget/{$key}", array('absolute' => TRUE)); $gadget_embed_url = 'http://fusion.google.com/add?source=atgs&moduleurl=' . urlencode($gadget_url); $gadget_embed_html = "Add to Google"; $form['gadget']['embed_link'] = array( '#type' => 'markup', '#value' => t('The gadget is currently available at these URLs:') . '', ); } return system_settings_form($form); } /** * Implementation of hook_apachesolr_modify_query(). * * Adds debugQuery parameter to Solr call that returns processing time, etc. */ function apachesolr_stats_apachesolr_modify_query(&$query, &$params, $caller) { if (variable_get('apachesolr_stats_enabled', 0)) { // Add the debug query argument. // See: http://wiki.apache.org/solr/CommonQueryParameters#head-f45a9396425956a4db8d6478ed6029adfb7b0858 if ($caller == 'apachesolr_search') { $params['debugQuery'] = 'true'; } } } /** * Implementation of hook_exit(). * * This is the spot where actual logging takes place. */ function apachesolr_stats_exit() { if (!variable_get('apachesolr_stats_enabled', 0)) { return; } // Apparently there can be cases where some modules aren't loaded. if (! function_exists('apachesolr_has_searched')) { return; } // Ignore certain IPs $ignore_list = variable_get('apachesolr_stats_ignore_ip_list', ''); if ($ignore_list) { $ips_to_ignore = preg_split('/[\s]+/', $ignore_list); $request_ip_address = ip_address(); foreach ($ips_to_ignore as $ip) { if (strpos($request_ip_address, $ip) === 0) { return; } } } global $user; if (! apachesolr_has_searched()) { return; } $response = apachesolr_static_response_cache(); $query = apachesolr_current_query(); $url_queryvalues = $query->get_url_queryvalues(); db_query("INSERT INTO {apachesolr_stats} (timestamp, uid, sid, numfound, showed_suggestions, total_time, prepare_time, process_time, page, keywords, filters, sort, params) VALUES (%d, %d, '%s', %d, %d, %d, %d, %d, '%s', '%s','%s','%s','%s')", time(), $user->uid, session_id(), $response->response->numFound, (int) get_object_vars($response->spellcheck->suggestions), $response->debug->timing->time, $response->debug->timing->prepare->time, $response->debug->timing->process->time, isset($_GET['page']) ? $_GET['page'] : '', $query->get_query_basic(), $url_queryvalues['filters'], $url_queryvalues['solrsort'], serialize($response->responseHeader->params) ); return; /* $times = array(); $times['total']['total'] = $response->debug->timing->time; foreach (array('prepare', 'process') as $phase) { foreach($response->debug->timing->prepare as $key => $value) { if (is_object($value)) { $times[$phase][$key] = (int) $value->time; } else { $times[$phase]['total'] = (int) $value; } } } dsm($times); return; */ } /** * Callback function that outputs an XML description for a Google Gadget * and terminates PHP execution. * * @see apachesolr_stats_menu() */ function apachesolr_stats_report_gadget() { $settings_key = variable_get("apachesolr_stats_gadget_key", ""); if ($settings_key === 0) { echo t("Invalid request"); exit; } $granularities = apachesolr_stats_get_granularities(); $report_elements = apachesolr_stats_generate_report_elements($granularities["hour"]); // Generate options $element_options = ""; foreach ($report_elements as $id => $element) { $element_options .= " \n"; } $granularity_options = ""; foreach ($granularities as $id => $granularity) { $granularity_options .= " \n"; } $title = t('Search statistics for @sitename', array('@sitename' => variable_get('site_name', ''))); $access_key = t('Access key'); $var_to_show = t('Variable to show'); $timespan_to_report = t('Time span to report'); $loading = t('Loading...'); // Send correct header for XML header("Content-Type: text/xml"); // Output the gadget XML description $site_base_url = url('', array('absolute' => true)); echo << $element_options $granularity_options $loading ]]> HEREDOC; exit; } /** * Callback function used by Gadget javascript to fetch a particular element. * * @param string $requested_granularity * Granularity of report to use. * @param string $requested_element * ID of report element to return. * @param string $requested_key * Secret key given by gadget. * @see apachesolr_stats_menu() */ function apachesolr_stats_report_gadget_element($requested_granularity, $requested_element, $requested_key) { $granularities = apachesolr_stats_get_granularities(); $settings_key = variable_get("apachesolr_stats_gadget_key", ""); if ($settings_key === 0) { echo "Invalid request: no local key set"; exit; } if ($settings_key != $requested_key) { echo "Invalid request: invalid key $requested_key"; exit; } if (empty($granularities[$requested_granularity])) { echo "Invalid request: bad granularity $requested_granularity"; exit; } $report_elements = apachesolr_stats_generate_report_elements($granularities[$requested_granularity]); foreach ($report_elements as $id => $report_element) { if ($id == $requested_element) { echo "" . $report_element['name'] . "
\n"; echo "
" . $report_elements['span']['value'] . "
\n"; $value = $report_element['value']; $value = str_replace('&', '&', $value); echo "
{$value}
\n"; exit; } } echo "Invalid request: bad element $requested_element"; exit; } /** * Callback for admin/reports/apachesolr/stats. * @param string $granularity * Granularity to use for report. * @return string * The page output as HTML. * @see apachesolr_stats_menu() */ function apachesolr_stats_report($picked_granularity = "hour") { drupal_set_title(t("Apache Solr statistics report")); if (! variable_get('apachesolr_stats_enabled', 0)) { return t('Logging is disabled in the !link. Enable it to log Apache Solr queries.', array('!link' => l('module configuration page', 'admin/settings/apachesolr/stats'))); } $granularities = apachesolr_stats_get_granularities(); // Decide what granularity to use: minute, hour or day // Check if given argument exists; if not, reset to "hour" if (!$granularities[$picked_granularity]) { $picked_granularity = "hour"; } $granularity = $granularities[$picked_granularity]; // Process latest log entries $report_elements = apachesolr_stats_generate_report_elements($granularity); // Create the output HTML::::::::::::::::::::::::::::::::::::: // Granularity picker: // Leave only those less than apachesolr_stats_flush_log_timer $timer_max = variable_get('apachesolr_stats_flush_log_timer', 259200); $output = "
" . t('Choose the report time span:'); foreach ($granularities as $name => $granularity) { if ($name != "all" && $granularity['time_before'] > $timer_max) { continue; } $output .= "   "; if ($name != $picked_granularity) { $output .= l($granularity["last_msg"], "admin/reports/apachesolr/stats/$name"); } else { $output .= "" . $granularity["last_msg"] . ""; } } $output .= "

"; if ($report_elements) { // Report description $output .= t('This is an overview of Apache Solr usage and performance.'); $output .= ' ' . t('You can also visit the !settingslink.', array('!settingslink' => l(t('settings page'), 'admin/settings/apachesolr/stats')) ); // Add Google Gadgets embedding link $key = variable_get("apachesolr_stats_gadget_key", ""); if ($key) { $gadget_url = url("apachesolr_stats/gadget/{$key}", array('absolute' => TRUE)); $gadget_embed_html = '' . t('Add to Google') . ''; $output .= "
" . l(t('Embed as Google Gadget'), "http://www.google.com/ig/adde?moduleurl=" . urlencode($gadget_url)) . " {$gadget_embed_html}
"; } // Render report elements foreach ($report_elements as $id => $data) { // Table data $rows[] = array( "data" => array( array('data' => $data['name'], 'header' => true, 'style' => 'width:33%'), array('data' => $data['value']), ) ); } $output .= theme('table', array(), $rows); } else { drupal_set_message(t('There is not enough stored data to build a report.')); } return $output; } /** * Generate an IMG tag with the URL to generate a chart using Google Charts API. * * @param string $granularity * The granularity to use. * @param array $data * The array of data to chart. * @param integer $start_timeslot * The index of the first data element to chart. * @param integer $last_timeslot * The index of the first data element to chart. * @param integer $total_queries * Integer with the total number of queries included in this chart. * @param boolean $average * Boolean flag: show an average value in the chart. */ function apachesolr_stats_chart($granularity, $data, $start_timeslot, $last_timeslot, $average = FALSE) { // Sample: http://chart.apis.google.com/chart?cht=lc&chs=350x100&chdlp=b&chma=10,10,10,10&chd=s:[encoded chart data] $chart_prefix = 'http://chart.apis.google.com/chart?cht=lc&chs=350x100'; $chart_prefix .= '&chdlp=b&chma=30,100,20,20&chd=s:'; $chd = array(); $chd_min = 9999999; $chd_max = 0; $total = 0; for ($t = $start_timeslot; $t<=$last_timeslot; $t++) { $num = $data[$t]+0; $chd_min = ($chd_min > $num) ? $num : $chd_min; $chd_max = ($chd_max < $num) ? $num : $chd_max; $chd[] = $num; } $chd = array_reverse($chd); if ($count > 0) { $chd_avg = $total / $count; } else { $chd_avg = $num; } // Generate basic image URL $image_url = $chart_prefix . apachesolr_stats_encodedata($chd, $chd_min, $chd_max); // Add labels $chxl = ""; if ($chd_max > 0) { $chxl .= "0:|". intval($chd_min) ."|". intval($chd_max); // Show average value in a label if ($average !== FALSE) { $image_url .= "&chxp=1," . intval($average / $chd_max * 100); $chxl .= sprintf("|1:|%s=%.2f", t('average'), $average); $chxt = "y,r"; } else { $chxt = "y"; } // Add time/date labels $earliest_timestamp = $start_timeslot * $granularity['timespan']; $last_timestamp = $last_timeslot * $granularity['timespan']; $mid_timestamp = ($last_timestamp + $earliest_timestamp) / 2; $time_msg_1 = drupal_urlencode(strftime($granularity['format'], $earliest_timestamp)); $time_msg_2 = drupal_urlencode(strftime($granularity['format'], $mid_timestamp)); $time_msg_3 = drupal_urlencode(strftime($granularity['format'], $last_timestamp)); if ($chxt) { $chxt = "{$chxt},x"; $chxl .= "|2:|{$time_msg_1}|{$time_msg_2}|{$time_msg_3}"; } else { $chxt = "x"; $chxl .= "|1:|{$time_msg_1}|{$time_msg_2}|{$time_msg_3}"; } $image_url .= "&chxl={$chxl}&chxt={$chxt}"; } // Return the image tag return ""; } /** * Encode data using Chart's simple encoding. * See http://code.google.com/apis/chart/formats.html#simple * * @param array $chd * an array of integer values to encode. * @param integer $chd_min * an integer with the smallest value to encode. * @param integer $chd_max * an integer with the greatest value to encode. * @return string * a string representing the Google Charts API simple encoding of the data. */ function apachesolr_stats_encodedata($chd, $chd_min, $chd_max) { $encoder_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; $encoded_values = ''; if (is_array($chd)) { foreach ($chd as $value) { $encoded_values .= substr($encoder_string, (($value-$chd_min) / $chd_max)*61, 1); } } return $encoded_values; } /** * Implementation of hook_cron(). * * Remove expired log messages. */ function apachesolr_stats_cron() { db_query('DELETE FROM {apachesolr_stats} WHERE timestamp < %d', time() - variable_get('apachesolr_stats_flush_log_timer', 259200)); } /** * Implementation of apachesolr_search_result_alter(). */ /* TODO: change results here to track clickthrus? function apachesolr_stats_apachesolr_search_result_alter($doc) { dsm($doc); } */ /** * Determine the facet field from a word: im_vid_XX, kw or XXX (anything before ":", like tid, uid, etc) * @return string * The facet field id. */ function apachesolr_stats_determine_field_from_query($facets, $word) { if (strpos($word, ":") !== false) { list($fieldname, $value) = explode(":", $word); if ($fieldname == "tid") { // Replace tid with the correct facet field name (im_vid_XXX) where XXX = vid $term = taxonomy_get_term(intval(substr($word, 4))); $fieldname = "im_vid_" . $term->vid; } else { // Check if $fieldname is really a facet, if not count as keyword if (empty($facets[$fieldname])) { $fieldname = false; } } } else { // No ":" found, so it's a keyword $fieldname = false; } return $fieldname; } /** * Returns a themed table for facet usage * @param array $facets * An array of calculated data to report. * @return string * HTML for a themed table containing the report data. */ function apachesolr_stats_facet_usage_table($facets) { // Report usage in table $header = array( array('data' => t('Facet ID'), 'Xfield' => 'id', 'sort' => 'asc'), array('data' => t('Facet info'), 'Xfield' => 'info', 'sort' => ''), array('data' => t('Queries containing this facet'), 'Xfield' => 'queries', 'sort' => ''), array('data' => t('% of queries containing this facet'), 'Xfield' => 'queries', 'sort' => ''), #array('data' => 'Clickthrus from queries containing this facet', 'Xfield' => 'ct', 'sort' => ''), #array('data' => 'Clickthru rate for facet', 'Xfield' => 'ctp', 'sort' => ''), ); foreach($facets as $fieldname => $facet) { $rows[$fieldname][] = $fieldname; $rows[$fieldname][] = $facet['info']; $rows[$fieldname][] = $facet['usage']; $rows[$fieldname][] = sprintf("%2.1f%%", $facet['usage']/$facets['any']['usage']*100); #$rows[$fieldname][] = $facet['clickthru']; #if ($facet['usage']>0) { # $rows[$fieldname][] = sprintf("%2.1f%%", ($facet['clickthru'] / $facet['usage'])*100); #} } $output = theme('table', $header, $rows, array('style' => 'font-size:80%')); return $output; } /** * Returns the tag for a Google chart with the facet usage * * @param array $facets * An array of calculated data to report. * @return string * HTML for an IMG tag to a Google chart. */ function apachesolr_stats_facet_usage_graph($facets) { // Chart for field usage $leyends = array(); $data = array(); $label_cutoff = 40; foreach($facets as $fieldname => $facet) { $leyend = preg_replace("/^.*ilter by /", "", $facet['info']); if (strlen($leyend) > $label_cutoff) { $leyend = substr($leyend, 0, $label_cutoff) . "..."; } $leyends[] = drupal_urlencode($leyend); $data[] = $facet['usage']; } $chd = apachesolr_stats_encodedata($data, 0, $facets['any']['usage']); // array_reverse() in next line due to apachesolr_stats_encodedata() encoding data backwards $chl = implode('|', array_reverse($leyends)); $height = 30 + sizeof($leyends) * 28; // Percentage labels $chm = "N*f0*%,000000,0,-1,11"; $chart = ""; return $chart; } /** * Returns the facet array to report on. */ function apachesolr_stats_get_facets() { $all_facets = module_invoke_all('apachesolr_facets'); // Keep only those enabled according to apachesolr_get_enabled_facets() $facets = array(); foreach (apachesolr_get_enabled_facets() as $module => $enabled) { foreach ($enabled as $facet_id) { $facets[$facet_id] = $all_facets[$facet_id]; } } // Add some "virtual" facets to report on. $facets['kw'] = array( 'facet_field' => 'kw', 'info' => 'Keyword search', ); $facets['any'] = array( 'facet_field' => 'any', 'info' => '[All queries including any filter and/or keywords]', ); /* $facets['none'] = array( 'facet_field' => 'none', 'info' => '[Clickthrus with no previous queries]', ); */ return $facets; } /** * Returns an array of preset granularities. * @return array * an array of preset granularities for reports. */ function apachesolr_stats_get_granularities() { $granularities = array( 'minute' => array( 'name' => t('minute'), 'timespan' => 60, 'time_before' => 60*60*24, // One day before 'last_msg' => t('last day'), 'format' => '%H:%M', ), 'hour' => array( 'name' => t('hour'), 'timespan' => 60*60, 'time_before' => 60*60*24*7, // One week before 'last_msg' => t('last week'), 'format' => '%m/%d %H:%M', ), 'day' => array( 'name' => t('day'), 'timespan' => 60*60*24, 'time_before' => 60*60*24*2*16, // 4 weeks before 'last_msg' => t('last month'), 'format' => '%m/%d', ), 'all' => array( 'name' => t('day'), 'timespan' => 60*60*24, 'time_before' => 60*60*24*7*16, // 16 weeks before 'last_msg' => t('all time (depends on settings)'), 'format' => '%m/%d', ), ); return $granularities; } /** * Generates report elements for the given granularity. * * @param string $granularity * Timespan to aggregate report by. Possible values: 'minute', 'hour' or 'day' * @return array * An indexed array with the report elements; each element is an array with * the indexes: * 'name' => human-readable name of the element, e.g. "Total queries" * 'value' => html with the result. Can be an image, a number, etc. */ function apachesolr_stats_generate_report_elements($granularity) { // Initialize $facets = apachesolr_stats_get_facets(); $suggestions = 0; $queries = 0; $users = array(); $sessions = array(); $start_timeslot = 0; $last_timeslot = 0; $first_timestamp = 0; $time['max'] = -1; $time['min'] = 9999.999; $report_elements = array(); $keywords = array(); // Scan the logfile and build statistics arrays $result = db_query("SELECT * FROM {apachesolr_stats} WHERE timestamp > %d ORDER BY timestamp DESC", time()- $granularity['time_before']); while ($record = db_fetch_object($result)) { $timeslot = intval($record->timestamp / $granularity['timespan']); if ($last_timeslot == 0) { $last_timeslot = $timeslot; } $users[$record->uid]++; $sessions[$record->sid]++; // Tally suggestions if ($record->showed_suggestions) { $suggestions++; } $total_requests++; $time['total'] += $record->total_time; // $time['prepare'] += $record->prepare_time; // $time['process'] += $record->process_time; // Track max and min response times $time['max'] = ($time['max'] < $record->total_time ? $record->total_time : $time['max']); $time['min'] = ($time['min'] > $record->total_time ? $record->total_time : $time['min']); // Field usage; only when on first results page (meaning it's a fresh search) if ($record->page == "") { unset($facet_processed_flag); $ok = preg_match_all('/[^ ]+/', $record->filters, $matches); foreach($matches[0] as $word) { $fieldname = apachesolr_stats_determine_field_from_query($facets, $word); if ($fieldname != false) { // Count this facet field only once per query if ($facet_processed_flag[$fieldname] != true) { // Add 1 to usage of term from vocabulary $facets[$fieldname]['usage']++; // Mark so we don't count it again for this query $facet_processed_flag[$fieldname] = true; } } } if (trim($record->keywords) != "") { if ($facet_processed_flag['kw'] != true) { $facets['kw']['usage']++; $facet_processed_flag['kw'] = true; // Keep track of individual keywords used $keys_filtered = drupal_strtolower(trim($record->keywords)); $keywords[$keys_filtered]++; // Count keywords with zero results; only when no filters issued. if ($record->numfound == 0 && !$record->filters) { $keywords_noresults[$keys_filtered]++; } } } else { $no_keywords++; } // Count each unique query $facets["any"]['usage']++; // Keep track of how many fields were active per query $simultaneous_fields[sizeof($facet_processed_flag)]++; $total_queries++; } // Sort usage; count only the first page of results if ($record->page == "") { $sort_usage[($record->sort) ? $record->sort : "relevance"]++; } // Group some stats into timeslots (minutes, hours) to show trends if (empty($user_slot[$record->uid][$timeslot])) { $data_per_granularity['users_per_slot'][$timeslot]++; $user_slot[$record->uid][$timeslot] = TRUE; } if (empty($session_slot[$record->sid][$timeslot])) { $data_per_granularity['sessions_per_slot'][$timeslot]++; $session_slot[$record->sid][$timeslot] = TRUE; } $data_per_granularity['queries'][$timeslot]++; $count_per_granularity[$timeslot]++; $data_per_granularity['total_time'][$timeslot] += $record->total_time; $first_timestamp = $record->timestamp; } $start_timeslot = $timeslot; $earliest_timestamp = $start_timeslot * $granularity['timespan']; if (sizeof($sessions) == 0 || sizeof($users) == 0 || $total_queries == 0) { return array(); } $report_elements['span'] = array( 'name' => t('Report span'), 'value' => t('Last @interval (@startdate to @enddate)', array( '@interval' => format_interval(3600 + time() - $first_timestamp), '@startdate' => format_date($first_timestamp), '@enddate' => format_date(time()) )) . '
' . t('Data points in charts are one point per @granularity.', array('@granularity' => $granularity['name'])) ); #$report_elements['queries'] = array('name' => t('Total requests to Solr'), 'value' => $total_queries); // Chart for queries per timeslot $chart = apachesolr_stats_chart($granularity, $data_per_granularity['queries'], $start_timeslot, $last_timeslot, $total_queries / ($last_timeslot-$start_timeslot+1)); $report_elements['total_queries_per'] = array( 'name' => t('Requests'), 'value' => t('Total: @total', array('@total' => $total_queries)) . '
' . $chart ); // Chart for users per timeslot /* $chart = apachesolr_stats_chart($granularity, $data_per_granularity['users_per_slot'], $start_timeslot, $last_timeslot, sizeof($users) / ($last_timeslot-$start_timeslot+1)); $report_elements['total_users_per'] = array( 'name' => t('Unique users'), 'value' => t('Total: @total', array('@total' => sizeof($users))) . '
' . $chart ); */ // Chart for sessions per timeslot $chart = apachesolr_stats_chart($granularity, $data_per_granularity['sessions_per_slot'], $start_timeslot, $last_timeslot, sizeof($sessions) / ($last_timeslot-$start_timeslot+1)); $report_elements['total_sessions_per'] = array( 'name' => t('Unique sessions'), 'value' => t('Total: @total', array('@total' => sizeof($sessions))) . '
' . $chart ); #$report_elements['avg_queries_user'] = array('name' => t('Average requests per user'), 'value' => sprintf("%.1f", $total_queries / sizeof($users))); $report_elements['avg_queries_session'] = array('name' => t('Average requests per session'), 'value' => sprintf("%.1f", $total_queries / sizeof($sessions))); // Chart for average time per timeslot $data = array(); foreach ($data_per_granularity['total_time'] as $timeslot => $value) { $data[$timeslot] = $value / $count_per_granularity[$timeslot]; } // Call with average_empty = FALSE $chart = apachesolr_stats_chart($granularity, $data, $start_timeslot, $last_timeslot, $time['total'] / $total_queries); $report_elements['query_avg_time'] = array( 'name' => t('Average time per request (miliseconds)'), 'value' => sprintf("%s: %.2f ms / %s: %.2f ms / %s: %.2f ms", t('Minimum'), $time['min'], t('Average'), $time['total'] / $total_queries, t('Maximum'), $time['max']) . '
' . $chart ); // Most-used keywords $report_elements['keywords'] = array( 'name' => t('Top search phrases'), 'value' => apachesolr_stats_report_frequent_keywords($keywords, $keywords_noresults) ); // Most-used keywords with no results $report_elements['keywords_noresults'] = array( 'name' => t('Top search phrases with no results'), 'value' => apachesolr_stats_report_frequent_keywords($keywords_noresults, $keywords_noresults, "error") ); // Total spellchecker suggestions $report_elements['spellchecker'] = array('name' => t('Total spellchecker suggestions'), 'value' => $suggestions); // Chart for sort usage $leyends = array(); foreach ($sort_usage as $key => $value) { $leyends[] = drupal_urlencode($key); } $chl = implode('|', $leyends); $chd = implode(',', $sort_usage); $chart = ""; $report_elements['sort_usage'] = array('name' => t('Sort usage'), 'value' => $chart); // Chart for field usage $report_elements['field_usage'] = array( 'name' => t('Facet usage'), 'value' => apachesolr_stats_facet_usage_graph($facets) . apachesolr_stats_facet_usage_table($facets) ); return $report_elements; } /** * Recieves an array of keyword => count and reports the top-used terms. * @param $keywords * array of keyword => count pairs */ function apachesolr_stats_report_frequent_keywords($keywords, $keywords_noresults, $class = "", $number = 25) { if (empty($keywords)) { return ''; } arsort($keywords); // Final elements are the most frequent, get $number elements off the array $slice = array_slice($keywords, 0, $number); // Calculate font size for display $min = 1e9; $max = -1e9; $steps = 6; $weighted_slice = array(); foreach ($slice as $word => $count) { $min = min($min, $count); $max = max($max, $count); $weighted_slice[$word] = array( 'count' => $count, 'log_count' => log($count) ); } // Note: we need to ensure the range is slightly too large to make sure even // the largest element is rounded down. $range = max(.01, $max - $min) * 1.0001; // Add "weight" foreach ($weighted_slice as $word => $data) { $weighted_slice[$word]['weight'] = floor($steps * ($data['count'] - $min) / $range); } $items = array(); foreach ($weighted_slice as $word => $data) { if ($keywords_noresults[$word]) { $class = "error"; } $font_size_pct = 80 + ($data['weight'])*12; $items[] = l( $word, "search/apachesolr_search/$word", array( 'attributes' => array( 'class' => $class, 'style' => 'font-size:' . sprintf("%d%%", $font_size_pct) ), 'absolute' => TRUE, ) ) . " (" . $data["count"] . ")"; } return implode(", ", $items); }