You are here

quiz_stats.admin.inc in Quiz 7.5

Administration file for Quiz stats Module.

File

modules/quiz_stats/quiz_stats.admin.inc
View source
<?php

/**
 * @file
 * Administration file for Quiz stats Module.
 */

/**
 * Page for selecting between several quiz revisions.
 *
 * @param $nid
 *  Quiz node id.
 *
 * @return
 *  Themed html.
 */
function quiz_stats_revision_selector_page($quiz) {
  $res = db_query('SELECT vid FROM {node_revision} WHERE nid = :nid ORDER BY vid', array(
    ':nid' => $quiz->nid,
  ));

  // Uses this variable to slightly increase performance.
  $count = 0;
  $vids = array();
  while ($res_o = $res
    ->fetch()) {
    $vids[] = $res_o->vid;
    $count++;
  }
  if ($count < 1) {
    return t('Something went wrong. Please try again');
  }
  elseif ($count == 1) {

    // If there is only one revision we jump directly to that revision.
    drupal_goto("node/{$quiz->nid}/quiz/statistics/{$vids[0]}");
  }
  else {
    $content = array();
    $content['explanation'] = t('There are !num revisions of this quiz that have been taken.
    Different revisions may have different scoring, difficulity and other differences affecting its statistics.
    Because of this you have to choose the revision you want to see statistics from.', array(
      '!num' => $count,
    ));
    $content['links'] = array();
    foreach ($vids as $key => $value) {
      $content['links'][] = 'node/' . $quiz->nid . '/quiz/statistics/' . $value;
    }
    return theme('quiz_stats_revision_selector', array(
      'content' => $content,
    ));
  }
}

/**
 * Returns statistics for all available quizzes.
 *
 * @param $uid
 *   User id.
 *
 * @return
 *   HTML table.
 */
function quiz_stats_get_basic_stats($uid = 0) {
  $results = _quiz_get_quizzes($uid);
  return empty($results) ? t('No Statistics found.') : theme('quiz_stats_get_basic_stats', array(
    'results' => $results,
  ));
}

/**
 * Get stats for a single quiz. Maybe also for a single user.
 *
 * @param $vid
 *   quiz revision id.
 * @param $uid
 *   User id.
 *
 * @return
 *   HTML page with charts/graphs.
 */
function quiz_stats_get_adv_stats($vid, $uid = 0) {
  $charts = array();
  drupal_add_css(drupal_get_path('module', 'quiz_stats') . '/quiz_stats.css', array(
    'type' => 'file',
    'weight' => CSS_THEME,
  ));
  $charts['takeup'] = _get_date_vs_takeup_count_chart($vid, $uid);

  // Line chart/graph showing quiz takeup date along x-axis and count along
  // y-axis.
  $charts['status'] = _get_quiz_status_chart($vid, $uid);

  // 3D pie chart showing percentage of pass, fail, incomplete quiz status.
  $charts['top_scorers'] = _get_quiz_top_scorers_chart($vid, $uid);

  // Bar chart displaying top scorers.
  $charts['grade_range'] = _get_quiz_grade_range_chart($vid, $uid);
  return theme('quiz_stats_charts', array(
    'charts' => $charts,
  ));
}

/**
 * Generates grade range chart.
 *
 * @param int $vid
 *   Revision id of quiz node.
 * @param int $uid
 *   An optional user ID. If supplied, only quizzes created by that user will be
 *   returned.
 *
 * @return array|false
 *   Array with chart and metadata, FALSE if no quiz was found.
 */
function _get_quiz_grade_range_chart($vid, $uid = 0) {

  // @todo: make the ranges configurable
  $query = db_select('quiz_node_results', 'qnr');
  $query
    ->addExpression('SUM(score >= 0 && score < 20)', 'zero_to_twenty');
  $query
    ->addExpression('SUM(score >= 20 && score < 40)', 'twenty_to_fourty');
  $query
    ->addExpression('SUM(score >= 40 && score < 60)', 'fourty_to_sixty');
  $query
    ->addExpression('SUM(score >= 60 && score < 80)', 'sixty_to_eighty');
  $query
    ->addExpression('SUM(score >= 80 && score <= 100)', 'eighty_to_hundred');
  $query
    ->condition('qnr.vid', $vid);
  $query
    ->condition('qnr.is_invalid', 0);
  if ($uid != 0) {
    $query
      ->condition('qnr.uid', $uid);
  }
  $range = $query
    ->execute()
    ->fetch();
  $count = $range->zero_to_twenty + $range->twenty_to_fourty + $range->fourty_to_sixty + $range->sixty_to_eighty + $range->eighty_to_hundred;
  if ($count < 2) {
    return FALSE;
  }

  // Return the chart with some meta data.
  $chart = '<div id="quiz_top_scorers" class="quiz-stats-chart-space">';
  $chart .= theme('quiz_grade_range', array(
    'range' => $range,
  ));
  $chart .= '</div>';
  return array(
    'chart' => $chart,
    'title' => t('Distribution'),
    'explanation' => t('This chart shows the distribution of the scores on this quiz.'),
  );
}

/**
 * Generates the top scorers chart.
 *
 * @param int $vid
 *   Revision id of quiz node.
 * @param int $uid
 *   An optional user ID. If supplied, only quizzes created by that user will be
 *   returned.
 *
 * @return array|false
 *   Array with chart and metadata, FALSE if no quiz was found.
 */
function _get_quiz_top_scorers_chart($vid, $uid = 0) {
  $top_scorers = array();
  $query = db_select('quiz_node_results', 'qnr');
  $query
    ->leftJoin('users', 'u', 'u.uid = qnr.uid');
  $query
    ->fields('qnr', array(
    'score',
  ));
  $query
    ->fields('u', array(
    'name',
  ));
  $query
    ->condition('qnr.vid', $vid);
  $query
    ->condition('qnr.is_invalid', 0);
  if ($uid != 0) {
    $query
      ->condition('qnr.uid', $uid);
  }
  $query
    ->orderBy('score', 'DESC');
  $query
    ->range(0, 10);
  $results = $query
    ->execute();
  while ($result = $results
    ->fetchAssoc()) {
    $key = $result['name'] . '-' . $result['score'];
    $top_scorers[$key] = $result;
  }
  if (count($top_scorers) == 0) {
    return FALSE;
  }

  // Return the chart with some meta data.
  $chart = '<div id="quiz_top_scorers" class="quiz-stats-chart-space">';
  $chart .= theme('quiz_top_scorers', array(
    'scorer' => $top_scorers,
  ));
  $chart .= '</div>';
  return array(
    'chart' => $chart,
    'title' => t('Top scorers'),
    'explanation' => t('This chart shows which quiz takers have the highest scores.'),
  );
}

/**
 * Generates a chart showing the status for all registered responses to a quiz.
 *
 * @param int $vid
 *   Revision id of quiz node.
 * @param int $uid
 *   An optional user ID. If supplied, only quizzes created by that user will be
 *   returned.
 *
 * @return array|false
 *   Array with chart and metadata, FALSE if no quiz was found.
 */
function _get_quiz_status_chart($vid, $uid = 0) {

  // Get the pass rate of the given quiz by vid.
  $pass_rate = db_select('quiz_node_properties', 'qnp')
    ->fields('qnp', array(
    'pass_rate',
  ))
    ->condition('vid', $vid)
    ->execute()
    ->fetchField();
  if (!$pass_rate) {
    return FALSE;
  }

  // Get the count value of results row above and below pass rate.
  $query = db_select('quiz_node_results', 'qnr');
  $query
    ->addExpression("SUM(score >= {$pass_rate})", 'no_pass');
  $query
    ->addExpression("SUM(score < {$pass_rate})", 'no_fail');
  $query
    ->addExpression('SUM(is_evaluated = 0)', 'no_incomplete');
  $query
    ->condition('vid', $vid);
  $query
    ->condition('is_invalid', 0);
  $quiz = $query
    ->execute()
    ->fetchAssoc();
  if ($quiz['no_pass'] + $quiz['no_fail'] + $quiz['no_incomplete'] < 1) {

    // No sufficient data.
    return FALSE;
  }

  // Generates quiz status chart 3D pie chart.
  $chart = '<div id="get_quiz_status_chart" class="quiz-stats-chart-space">';
  $chart .= theme('get_quiz_status_chart', array(
    'quiz' => $quiz,
  ));
  $chart .= '</div>';
  return array(
    'chart' => $chart,
    'title' => t('Status'),
    'explanation' => t('This chart shows the status for all attempts made to answer this revision of the quiz.'),
  );
}

/**
 * Generates chart showing how often the quiz has been taken the last days.
 *
 * @param $uid
 *   user id.
 * @param $vid
 *   revision id of quiz.
 *
 * @return
 *   HTML to display chart.
 */
function _get_date_vs_takeup_count_chart($vid, $uid = 0) {
  $start = 0;
  $end = 30;
  $takeup = array();
  $sql = "SELECT COUNT(result_id) AS count,\n            DATE_FORMAT(FROM_UNIXTIME(time_start), '%Y-%m-%e') AS date\n            FROM {quiz_node_results}\n            WHERE vid = :vid AND time_start > (UNIX_TIMESTAMP(NOW()) - (86400*{$end}))";
  $sql_args[':vid'] = $vid;
  if ($uid != 0) {
    $sql .= " AND uid = :uid";
    $sql_args[':uid'] = $uid;
  }
  $sql .= " GROUP BY date ORDER BY time_start ASC";
  $results = db_query($sql, $sql_args);
  $res_o = $results
    ->fetchAll();
  if ($res_o) {
    $chart = '<div id="date_vs_takeup_count" class="quiz-stats-chart-space">';

    // Wrapping the chart output with div for custom theming.
    $chart .= theme('date_vs_takeup_count', array(
      'takeup' => $res_o,
    ));

    // Generate date vs takeup count line chart.
    $chart .= '</div>';
    return array(
      'chart' => $chart,
      'title' => t('Activity'),
      'explanation' => t('This chart shows how many times the quiz has been taken each day over the last !days days.', array(
        '!days' => $end,
      )),
    );
  }
}

/**
 * Computes quiz attempt count.
 *
 * @param $vid
 *   quiz node id.
 * @param $uid
 *   user id for filtering.
 *
 * @return
 *   Integer containing number of times that a quiz has
 *   been attended, can be filtered by uid.
 */
function _quiz_get_attempt_count($nid, $uid = 0) {
  $sql = 'SELECT COUNT(result_id) FROM {quiz_node_results} WHERE nid = :nid';
  $arg[':nid'] = $nid;
  if ($uid != 0) {
    $sql .= ' AND uid = :uid';
    $arg[':uid'] = $uid;
  }
  return db_query($sql, $arg)
    ->fetchField();
}

// Theme functions.

/**
 * Theme function for quiz grade range chart.
 *
 * Generates Chart using CHART API function.
 *
 * @param $range
 *   array containg quiz results data structure.
 *
 * @return
 *   HTML to render/display chart.
 */
function theme_quiz_grade_range($variables) {
  $range = $variables['range'];
  $max = max((array) $range);
  $count = array_sum((array) $range);
  $chart = array(
    '#type' => 'chart',
    '#chart_type' => 'column',
    '#title' => t('Score distribution'),
  );
  $chart['range'] = array(
    '#type' => 'chart_data',
    '#title' => t('% of quiz takers'),
    '#data' => array(
      round($range->zero_to_twenty / $count * 100),
      round($range->twenty_to_fourty / $count * 100),
      round($range->fourty_to_sixty / $count * 100),
      round($range->sixty_to_eighty / $count * 100),
      round($range->eighty_to_hundred / $count * 100),
    ),
  );
  $chart['xaxis'] = array(
    '#title' => t('Score'),
    '#type' => 'chart_xaxis',
    '#labels' => array(
      '0-20%',
      '20-40%',
      '40-60%',
      '60-80%',
      '80-100%',
    ),
  );
  $chart['yaxis'] = array(
    '#title' => t('Grade istribution'),
    '#type' => 'chart_yaxis',
    '#max' => 100,
  );
  return drupal_render($chart);
}

/**
 * Generates a chart of quiz top scorers.
 *
 * @param $attendees
 *   array containing quiz data structure.
 *
 * @return
 *   HTML to render quiz top scorers chart.
 */
function theme_quiz_top_scorers($variables) {
  $attendees = array_reverse($variables['scorer']);
  $chart = array(
    '#type' => 'chart',
    '#title' => t('Quiz Top Scorers'),
    '#chart_type' => 'column',
  );
  $chart['bar_data'] = array(
    '#type' => 'chart_data',
    '#title' => 'Result',
  );
  foreach ($attendees as $attendee) {
    $chart['bar_data']['#data'][] = (double) $attendee['score'];
    $labels[] = $attendee['name'] . ' ' . $attendee['score'] . '%';
  }
  $chart['yaxis'] = array(
    '#title' => t('Score') . ' (%)',
    '#type' => 'chart_yaxis',
    '#max' => 100,
  );
  $chart['xaxis'] = array(
    '#title' => 'Quiz Attendees',
    '#type' => 'chart_xaxis',
    '#labels' => $labels,
  );
  return drupal_render($chart);
}

/**
 * Generates quiz status chart number of pass, fail, incomplete.
 *
 * @param $quiz
 *   array of quiz data structure.
 *
 * @return
 *   HTML to render quiz status chart.
 */
function theme_get_quiz_status_chart($variables) {
  $quiz = $variables['quiz'];
  $quiz['total'] = $quiz['no_pass'] + $quiz['no_fail'] + $quiz['no_incomplete'];
  $chart = array(
    '#type' => 'chart',
    '#title' => t('Quiz results'),
    '#chart_type' => 'pie',
  );
  $chart['pie_data'] = array(
    '#type' => 'chart_data',
    '#title' => t('Result'),
    '#labels' => array(
      'Pass',
      'Incomplete',
      'Fail',
    ),
    '#data' => array(
      (int) $quiz['no_pass'],
      (int) $quiz['no_incomplete'],
      (int) $quiz['no_fail'],
    ),
  );
  return drupal_render($chart);
}

/**
 * Generates date vs takeup count chart.
 *
 * @param $takeup
 *   Array quiz data structure.
 *
 * @return
 *   HTML to render/display chart.
 */
function theme_date_vs_takeup_count($variables) {
  $chart = array(
    '#type' => 'chart',
    '#title' => t('Activity by date'),
    '#chart_type' => 'line',
  );
  $chart['line_data'] = array(
    '#type' => 'chart_data',
    '#title' => t('Result'),
    '#labels' => array(
      'Something',
    ),
  );
  $chart['line_data']['#data'] = array();
  $labels = array();
  foreach ($variables['takeup'] as $point) {
    $chart['line_data']['#data'][] = (int) $point->count;
    $labels[] = $point->date;
  }
  $chart['xaxis'] = array(
    '#type' => 'chart_xaxis',
    '#labels' => $labels,
  );
  $chart['yaxis'] = array(
    '#title' => 'Participants',
    '#type' => 'chart_yaxis',
  );
  return drupal_render($chart);
}

/**
 * Generates table of results from quiz data structure.
 *
 * @param @results
 *   Array containing quiz results data structure.
 *
 * @return
 *   HTML table containing quiz title, author, questions count, attempt count.
 */
function theme_quiz_stats_get_basic_stats($variables) {
  $results = $variables['results'];
  if (empty($results)) {
    return '<p>' . t('No questions were found.') . '</p>';
  }
  $header = array(
    'title' => t('Quiz'),
    'creator' => t('Author'),
    'created' => t('Created'),
  );
  if (user_access('access author stats')) {
    $header['questions_count'] = t('Questions');
    $header['attempt_count'] = t('Attempts');
  }
  $p = drupal_get_path('module', 'quiz_stats');
  $chart_icon = theme('image', array(
    'path' => "{$p}/chart.png",
    'width' => t('Charts'),
    'height' => t('See charts'),
  ));
  foreach ($results as $result) {
    $title_link = user_access('access author stats') ? 'node/' . $result['nid'] . '/quiz/statistics' : 'user/' . arg(1) . '/stats/';
    $row = array(
      'title' => l($chart_icon . ' ' . $result['title'], $title_link, array(
        'html' => TRUE,
      )),
      'creator' => l($result['name'], 'user/' . $result['uid']),
      'created' => format_date($result['created'], 'short'),
    );
    if (user_access('access author stats')) {
      $row['questions_count'] = quiz_get_number_of_questions($result['vid'], $result['nid']);
      $row['attempt_count'] = _quiz_get_attempt_count($result['nid']);
    }
    $rows[] = $row;
  }
  module_load_include('inc', 'quiz', 'quiz.pages');
  $cc = '<em>' . t('Chart icon from !url', array(
    '!url' => 'pinvoke.com',
  )) . '</em>';
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'attributes' => array(
      'id' => 'tablesorter',
    ),
  )) . $cc;
}

Functions

Namesort descending Description
quiz_stats_get_adv_stats Get stats for a single quiz. Maybe also for a single user.
quiz_stats_get_basic_stats Returns statistics for all available quizzes.
quiz_stats_revision_selector_page Page for selecting between several quiz revisions.
theme_date_vs_takeup_count Generates date vs takeup count chart.
theme_get_quiz_status_chart Generates quiz status chart number of pass, fail, incomplete.
theme_quiz_grade_range Theme function for quiz grade range chart.
theme_quiz_stats_get_basic_stats Generates table of results from quiz data structure.
theme_quiz_top_scorers Generates a chart of quiz top scorers.
_get_date_vs_takeup_count_chart Generates chart showing how often the quiz has been taken the last days.
_get_quiz_grade_range_chart Generates grade range chart.
_get_quiz_status_chart Generates a chart showing the status for all registered responses to a quiz.
_get_quiz_top_scorers_chart Generates the top scorers chart.
_quiz_get_attempt_count Computes quiz attempt count.