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 = "";
$form['gadget']['embed_link'] = array(
'#type' => 'markup',
'#value' =>
t('The gadget is currently available at these URLs:')
. '
- ' . l(t('Source'), $gadget_url)
. '
- ' . l(t('Embed'), $gadget_embed_url) . ' ' . $gadget_embed_html . '
',
);
}
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 = '';
$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);
}